"""
Unit tests for HubClient
"""

import pytest
import httpx
from pathlib import Path
from unittest.mock import Mock, patch, MagicMock
import tempfile
import yaml

from aphub_sdk import HubClient, HubClientError
from aphub_sdk.retry import RetryConfig


@pytest.fixture
def mock_response():
    """Create a mock HTTP response"""
    response = Mock(spec=httpx.Response)
    response.status_code = 200
    response.json.return_value = {"success": True}
    response.headers = {}
    response.content = b"test content"
    return response


@pytest.fixture
def client():
    """Create a test client"""
    return HubClient(base_url="http://test-hub.com", api_key="test-key")


def test_client_initialization():
    """Test client initialization"""
    client = HubClient(base_url="http://test.com", api_key="test-key")
    assert client.base_url == "http://test.com"
    assert client.api_key == "test-key"
    assert "Authorization" in client._headers


def test_client_with_tokens():
    """Test client initialization with tokens"""
    client = HubClient(
        base_url="http://test.com",
        access_token="test-access-token",
        refresh_token="test-refresh-token",
    )
    assert client.token_manager is not None
    assert client.token_manager.access_token == "test-access-token"


def test_handle_response_success(mock_response):
    """Test successful response handling"""
    client = HubClient(base_url="http://test.com")
    result = client._handle_response(mock_response)
    assert result == {"success": True}


def test_handle_response_error(mock_response):
    """Test error response handling"""
    client = HubClient(base_url="http://test.com")
    mock_response.status_code = 404
    mock_response.json.return_value = {"detail": "Not found"}
    
    with pytest.raises(HubClientError) as exc_info:
        client._handle_response(mock_response)
    
    assert exc_info.value.status_code == 404


def test_pull_success(client, mock_response):
    """Test successful pull"""
    with patch.object(client.client, 'get', return_value=mock_response):
        with tempfile.TemporaryDirectory() as tmpdir:
            result = client.pull("test-agent", tag="1.0.0", output_path=Path(tmpdir))
            assert result.success is True


def test_pull_with_progress(client, mock_response):
    """Test pull with progress callback"""
    progress_calls = []
    
    def progress_callback(sent, total):
        progress_calls.append((sent, total))
    
    client.progress_callback = progress_callback
    mock_response.headers = {"content-length": "100"}
    
    with patch.object(client.client, 'get', return_value=mock_response):
        with patch('builtins.open', create=True) as mock_open:
            mock_file = MagicMock()
            mock_open.return_value.__enter__.return_value = mock_file
            mock_response.iter_bytes.return_value = [b"chunk1", b"chunk2"]
            
            with tempfile.TemporaryDirectory() as tmpdir:
                client.pull("test-agent", output_path=Path(tmpdir))
                assert len(progress_calls) > 0


def test_push_success(client):
    """Test successful push"""
    mock_response = Mock(spec=httpx.Response)
    mock_response.status_code = 201
    mock_response.json.return_value = {
        "success": True,
        "name": "test-agent",
        "version": "1.0.0",
        "tag": "1.0.0",
        "message": "Agent pushed successfully",
    }
    
    with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
        manifest = {
            "api_version": "aipartnerup.com/v1",
            "kind": "Agent",
            "metadata": {"name": "test-agent", "version": "1.0.0"},
            "spec": {"framework": "aipartnerupflow"},
        }
        yaml.dump(manifest, f)
        manifest_path = Path(f.name)
    
    try:
        with patch.object(client.client, 'post', return_value=mock_response):
            # Mock _update_headers to avoid issues
            with patch.object(client, '_update_headers'):
                result = client.push(manifest_path, tag="1.0.0")
                assert result.success is True
    finally:
        manifest_path.unlink()


def test_search_success(client):
    """Test successful search"""
    mock_response = Mock(spec=httpx.Response)
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "success": True,
        "query": "test",
        "total": 1,
        "page": 1,
        "page_size": 20,
        "total_pages": 1,
        "results": [
            {
                "agent": {
                    "name": "test-agent",
                    "description": "Test agent",
                    "framework": "aipartnerupflow",
                    "latest_version": "1.0.0",
                    "downloads": 10,
                    "stars": 5,
                },
                "score": 1.0,
            }
        ],
    }
    
    with patch.object(client.client, 'get', return_value=mock_response):
        with patch.object(client, '_update_headers'):
            result = client.search("test")
            assert result.total == 1
            assert len(result.results) == 1


def test_retry_on_failure(client):
    """Test retry logic"""
    # Configure retry with short backoff for testing
    client.retry_config = RetryConfig(max_retries=2, backoff_factor=0.1)
    
    mock_response_success = Mock(spec=httpx.Response)
    mock_response_success.status_code = 200
    mock_response_success.json.return_value = {"success": True}
    mock_response_success.headers = {}
    mock_response_success.content = b"test"
    mock_response_success.iter_bytes.return_value = [b"test"]
    
    mock_response_error = Mock(spec=httpx.Response)
    mock_response_error.status_code = 500
    mock_response_error.json.return_value = {"detail": "Internal server error"}
    
    # First call fails, second succeeds
    with patch.object(
        client.client,
        'get',
        side_effect=[
            httpx.HTTPStatusError("Error", request=Mock(), response=mock_response_error),
            mock_response_success
        ]
    ):
        with patch.object(client, '_update_headers'):
            with tempfile.TemporaryDirectory() as tmpdir:
                result = client.pull("test-agent", output_path=Path(tmpdir))
                assert result.success is True


def test_error_messages(client):
    """Test error message formatting"""
    mock_response = Mock(spec=httpx.Response)
    mock_response.status_code = 400
    mock_response.json.return_value = {"detail": "Invalid manifest"}
    
    with patch.object(client.client, 'post', return_value=mock_response):
        with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
            manifest = {
                "api_version": "aipartnerup.com/v1",
                "kind": "Agent",
                "metadata": {"name": "test", "version": "1.0.0"},
                "spec": {"framework": "aipartnerupflow"},
            }
            yaml.dump(manifest, f)
            manifest_path = Path(f.name)
        
        try:
            with patch.object(client, '_update_headers'):
                with pytest.raises(HubClientError) as exc_info:
                    client.push(manifest_path)
                assert "Invalid manifest" in str(exc_info.value)
        finally:
            manifest_path.unlink()


def test_login_success():
    """Test successful login"""
    from aphub_sdk import HubClient, AuthResponse
    
    client = HubClient(base_url="http://test.com")
    
    mock_response = Mock(spec=httpx.Response)
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "access_token": "test-access-token",
        "refresh_token": "test-refresh-token",
        "token_type": "bearer",
        "expires_in": 3600,
    }
    
    with patch('httpx.Client') as mock_client_class:
        mock_client = Mock()
        mock_client.post.return_value = mock_response
        mock_client.timeout = Mock(read=30.0)
        mock_client_class.return_value.__enter__.return_value = mock_client
        mock_client_class.return_value.__exit__.return_value = None
        
        result = client.login("testuser", "testpass")
        assert isinstance(result, AuthResponse)
        assert result.access_token == "test-access-token"
        assert result.refresh_token == "test-refresh-token"


def test_login_failure():
    """Test login failure"""
    from aphub_sdk import HubClient, HubClientError
    
    client = HubClient(base_url="http://test.com")
    
    mock_response = Mock(spec=httpx.Response)
    mock_response.status_code = 401
    mock_response.text = "Invalid credentials"
    
    with patch('httpx.Client') as mock_client_class:
        mock_client = Mock()
        mock_client.post.side_effect = httpx.HTTPStatusError("Error", request=Mock(), response=mock_response)
        mock_client.timeout = Mock(read=30.0)
        mock_client_class.return_value.__enter__.return_value = mock_client
        mock_client_class.return_value.__exit__.return_value = None
        
        with pytest.raises(HubClientError) as exc_info:
            client.login("testuser", "wrongpass")
        assert exc_info.value.status_code == 401


def test_get_manifest_success(client):
    """Test successful get_manifest"""
    mock_response = Mock(spec=httpx.Response)
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "api_version": "aipartnerup.com/v1",
        "kind": "Agent",
        "metadata": {"name": "test-agent", "version": "1.0.0"},
        "spec": {"framework": "aipartnerupflow"},
    }
    
    with patch.object(client.client, 'get', return_value=mock_response):
        with patch.object(client, '_update_headers'):
            manifest = client.get_manifest("test-agent", tag="1.0.0")
            assert manifest.metadata.name == "test-agent"
            assert manifest.metadata.version == "1.0.0"
            assert manifest.spec.framework == "aipartnerupflow"


def test_rate_agent_success(client):
    """Test successful rate_agent"""
    mock_response = Mock(spec=httpx.Response)
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "success": True,
        "message": "Rating submitted successfully",
    }
    
    with patch.object(client.client, 'post', return_value=mock_response):
        with patch.object(client, '_update_headers'):
            result = client.rate_agent("test-agent", rating=5, comment="Great agent!")
            assert result.success is True
            assert "successfully" in result.message.lower()


def test_rate_agent_invalid_rating(client):
    """Test rate_agent with invalid rating"""
    with pytest.raises(HubClientError) as exc_info:
        client.rate_agent("test-agent", rating=6)
    assert "between 1 and 5" in str(exc_info.value).lower()
    
    with pytest.raises(HubClientError) as exc_info:
        client.rate_agent("test-agent", rating=0)
    assert "between 1 and 5" in str(exc_info.value).lower()

