"""Integration tests for MCP server.

These tests require a running FastAPI server to fully test the integration.
Some tests use mocks to avoid this dependency.
"""

from __future__ import annotations

from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from mcp_standalone.client import FastAPIClient
from mcp_standalone.config import MCPConfig
from mcp_standalone.registry import ServerInfo
from mcp_standalone.server import _log_server_discovery, create_server


def test_create_server() -> None:
    """Test creating an MCP server instance."""
    server = create_server()
    assert server is not None
    assert server.name == "gdsfactoryplus"


def test_create_server_with_custom_url() -> None:
    """Test creating an MCP server with custom API URL."""
    custom_url = "http://localhost:9999"
    server = create_server(api_url=custom_url)
    assert server is not None


@pytest.mark.anyio
async def test_fastapi_client_lifecycle() -> None:
    """Test FastAPI client start and close."""
    client = FastAPIClient()
    assert client._client is None  # noqa: SLF001

    await client.start()
    assert client._client is not None  # noqa: SLF001

    await client.close()
    assert client._client is None  # noqa: SLF001


@pytest.mark.anyio
async def test_fastapi_client_context_manager() -> None:
    """Test FastAPI client as context manager."""
    async with FastAPIClient() as client:
        assert client._client is not None  # noqa: SLF001
    # After exiting context, client should be closed
    assert client._client is None  # noqa: SLF001


@pytest.mark.anyio
@patch("httpx.AsyncClient")
async def test_fastapi_client_get_request(mock_client_class: Any) -> None:
    """Test GET request with mocked httpx client."""
    # Setup mock
    mock_response = MagicMock()
    mock_response.json.return_value = {"result": "success"}
    mock_response.raise_for_status = MagicMock()

    mock_client = AsyncMock()
    mock_client.request = AsyncMock(return_value=mock_response)
    mock_client_class.return_value = mock_client

    # Test
    client = FastAPIClient()
    await client.start()
    result = await client.get("/api/test", params={"param": "value"})

    assert result == {"result": "success"}
    mock_client.request.assert_called_once()
    await client.close()


@pytest.mark.anyio
@patch("httpx.AsyncClient")
async def test_fastapi_client_post_request(mock_client_class: Any) -> None:
    """Test POST request with mocked httpx client."""
    # Setup mock
    mock_response = MagicMock()
    mock_response.json.return_value = {"status": "created"}
    mock_response.raise_for_status = MagicMock()

    mock_client = AsyncMock()
    mock_client.request = AsyncMock(return_value=mock_response)
    mock_client_class.return_value = mock_client

    # Test
    client = FastAPIClient()
    await client.start()
    result = await client.post("/api/test", json_data={"key": "value"})

    assert result == {"status": "created"}
    await client.close()


@pytest.mark.anyio
@patch("httpx.AsyncClient")
async def test_fastapi_client_retry_on_failure(mock_client_class: Any) -> None:
    """Test retry logic on request failure."""
    import httpx

    # Setup mock to fail twice then succeed
    mock_response = MagicMock()
    mock_response.json.return_value = {"result": "success"}
    mock_response.raise_for_status = MagicMock()

    mock_client = AsyncMock()
    call_count = 0

    async def mock_request(*_args: Any, **_kwargs: Any) -> MagicMock:
        nonlocal call_count
        call_count += 1
        if call_count < 3:
            msg = "Connection failed"
            raise httpx.RequestError(msg, request=MagicMock())
        return mock_response

    mock_client.request = AsyncMock(side_effect=mock_request)
    mock_client_class.return_value = mock_client

    # Test
    client = FastAPIClient()
    await client.start()
    result = await client.get("/api/test")

    assert result == {"result": "success"}
    assert call_count == 3  # Should have retried twice
    await client.close()


def test_config_defaults() -> None:
    """Test configuration defaults."""
    assert MCPConfig.API_URL == "http://localhost:8787"
    assert MCPConfig.TIMEOUT == 300
    assert MCPConfig.MAX_RETRIES == 3


def test_config_get_api_url() -> None:
    """Test getting API URL from config."""
    url = MCPConfig.get_api_url()
    assert url == MCPConfig.API_URL

    override = "http://example.com:8080"
    url = MCPConfig.get_api_url(override)
    assert url == override


@pytest.mark.anyio
@patch("mcp_standalone.server.FastAPIClient")
async def test_server_list_tools(mock_client_class: Any) -> None:
    """Test listing tools through MCP server."""
    # Create server
    server = create_server()

    # Verify server has the required attributes
    assert server is not None
    assert hasattr(server, "request_handlers")
    assert mock_client_class is not None  # Use the mock parameter

    # Note: This is a simplified test. Full testing would require
    # setting up the MCP protocol communication and properly mocking
    # the MCP server internals.


@patch("mcp_standalone.server.logger")
def test_server_discovery_no_servers(mock_logger: Any) -> None:
    """Test server discovery when no servers are running."""
    client = FastAPIClient()
    client._registry = MagicMock()  # noqa: SLF001
    client._registry.list_servers.return_value = []

    _log_server_discovery(client)

    # Verify warning logged about no servers
    mock_logger.warning.assert_called_once()
    assert "No GDSFactory+ servers found" in str(mock_logger.warning.call_args)
    # Verify registry location logged
    mock_logger.info.assert_called()


@patch("mcp_standalone.server.logger")
def test_server_discovery_one_server(mock_logger: Any) -> None:
    """Test server discovery with one server running."""
    client = FastAPIClient()
    client._registry = MagicMock()  # noqa: SLF001

    server_info = ServerInfo(
        port=8787,
        pid=12345,
        project_path="/path/to/project",
        project_name="test_project",
        pdk="sky130",
    )
    client._registry.list_servers.return_value = [server_info]
    client.list_projects = MagicMock(return_value=[server_info.to_dict()])

    _log_server_discovery(client)

    # Verify info logged with project details
    mock_logger.info.assert_called()
    call_args_str = str(mock_logger.info.call_args_list)
    # Check for format string and parameters
    assert "Discovered %d GDSFactory+ server(s):" in call_args_str
    assert "1" in call_args_str  # Server count
    assert "test_project" in call_args_str
    assert "8787" in call_args_str
    assert "sky130" in call_args_str


@patch("mcp_standalone.server.logger")
def test_server_discovery_multiple_servers(mock_logger: Any) -> None:
    """Test server discovery with multiple servers running."""
    client = FastAPIClient()
    client._registry = MagicMock()  # noqa: SLF001

    servers = [
        ServerInfo(
            port=8787 + i,
            pid=12345 + i,
            project_path=f"/path/to/project{i}",
            project_name=f"project_{i}",
            pdk="sky130",
        )
        for i in range(5)
    ]
    client._registry.list_servers.return_value = servers
    client.list_projects = MagicMock(return_value=[s.to_dict() for s in servers])

    _log_server_discovery(client)

    # Verify info logged with first 3 servers and count of remaining
    mock_logger.info.assert_called()
    call_args_str = str(mock_logger.info.call_args_list)
    # Check for format string and parameters
    assert "Discovered %d GDSFactory+ server(s):" in call_args_str
    assert "5" in call_args_str  # Server count
    # Should show first 3 projects
    assert "project_0" in call_args_str
    assert "project_1" in call_args_str
    assert "project_2" in call_args_str
    # Should indicate more
    assert "and %d more" in call_args_str
    assert "2" in call_args_str  # Remaining count


@patch("mcp_standalone.server.logger")
def test_server_discovery_registry_error(mock_logger: Any) -> None:
    """Test server discovery when registry read fails."""
    client = FastAPIClient()
    client.list_projects = MagicMock(side_effect=Exception("Registry error"))

    _log_server_discovery(client)

    # Verify warning logged about registry error
    mock_logger.warning.assert_called()
    assert "Could not read server registry" in str(mock_logger.warning.call_args)
    # Verify helpful info logged
    mock_logger.info.assert_called()


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
