"""Tests for gfp_mcp Phase 4 modules.

These tests verify the supporting modules in the new gfp_mcp package:
- render.py: render_gds_to_png() and render_built_cells()
- client.py: FastAPIClient (without get_project_info method)
- registry.py, resources.py, config.py: core modules
"""

from __future__ import annotations

import time
from pathlib import Path
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch

import pytest


class TestRenderModule:
    """Tests for gfp_mcp.render module."""

    def test_has_klayout_type(self) -> None:
        """Test that HAS_KLAYOUT is a boolean."""
        from gfp_mcp.render import HAS_KLAYOUT

        assert isinstance(HAS_KLAYOUT, bool)

    def test_render_gds_to_png_file_not_found(self, tmp_path: Path) -> None:
        """Test rendering a non-existent file."""
        from gfp_mcp.render import render_gds_to_png

        gds_file = tmp_path / "nonexistent.gds"
        result = render_gds_to_png(gds_file)
        assert result is None

    def test_render_gds_to_png_without_klayout(self, tmp_path: Path) -> None:
        """Test that rendering gracefully handles missing klayout."""
        gds_file = tmp_path / "test.gds"
        gds_file.write_bytes(b"dummy content")

        from gfp_mcp import render

        original_has_klayout = render.HAS_KLAYOUT
        render.HAS_KLAYOUT = False
        try:
            result = render.render_gds_to_png(gds_file)
            assert result is None
        finally:
            render.HAS_KLAYOUT = original_has_klayout


class TestRenderBuiltCells:
    """Tests for render_built_cells function."""

    @pytest.mark.anyio
    async def test_render_built_cells_empty_names(self) -> None:
        """Test render_built_cells with empty names list."""
        from gfp_mcp.render import render_built_cells

        result = await render_built_cells([], project=None)
        assert result == []

    @pytest.mark.anyio
    async def test_render_built_cells_no_klayout(self) -> None:
        """Test render_built_cells when klayout is not available."""
        from gfp_mcp import render

        original_has_klayout = render.HAS_KLAYOUT
        render.HAS_KLAYOUT = False
        try:
            result = await render.render_built_cells(["test_cell"], project=None)
            assert result == []
        finally:
            render.HAS_KLAYOUT = original_has_klayout

    @pytest.mark.anyio
    async def test_render_built_cells_no_servers(self) -> None:
        """Test render_built_cells when no servers are available."""
        from gfp_mcp.render import HAS_KLAYOUT, render_built_cells

        if not HAS_KLAYOUT:
            pytest.skip("klayout not installed")

        with patch("gfp_mcp.render.ServerRegistry") as mock_registry_class:
            mock_registry = MagicMock()
            mock_registry.get_server_by_project.return_value = None
            mock_registry.list_servers.return_value = []
            mock_registry_class.return_value = mock_registry

            result = await render_built_cells(["test_cell"], project=None)
            assert result == []

    @pytest.mark.anyio
    async def test_render_built_cells_file_timeout(self, tmp_path: Path) -> None:
        """Test render_built_cells when GDS file doesn't appear in time."""
        from gfp_mcp.registry import ServerInfo
        from gfp_mcp.render import HAS_KLAYOUT, render_built_cells

        if not HAS_KLAYOUT:
            pytest.skip("klayout not installed")

        with patch("gfp_mcp.render.ServerRegistry") as mock_registry_class:
            mock_registry = MagicMock()
            server_info = ServerInfo(
                port=8787,
                pid=12345,
                project_path=str(tmp_path),
                project_name="test_project",
            )
            mock_registry.get_server_by_project.return_value = server_info
            mock_registry_class.return_value = mock_registry

            with patch("gfp_mcp.render.wait_for_gds_file", return_value=False):
                result = await render_built_cells(
                    ["missing_cell"], project="test_project"
                )
                assert result == []

    @pytest.mark.anyio
    async def test_render_built_cells_render_failure(self, tmp_path: Path) -> None:
        """Test render_built_cells when PNG rendering fails."""
        from gfp_mcp.registry import ServerInfo
        from gfp_mcp.render import HAS_KLAYOUT, render_built_cells

        if not HAS_KLAYOUT:
            pytest.skip("klayout not installed")

        gds_dir = tmp_path / "build" / "gds"
        gds_dir.mkdir(parents=True)
        gds_file = gds_dir / "bad_cell.gds"
        gds_file.write_bytes(b"invalid gds content")
        time.sleep(0.2)

        with patch("gfp_mcp.render.ServerRegistry") as mock_registry_class:
            mock_registry = MagicMock()
            server_info = ServerInfo(
                port=8787,
                pid=12345,
                project_path=str(tmp_path),
                project_name="test_project",
            )
            mock_registry.get_server_by_project.return_value = server_info
            mock_registry_class.return_value = mock_registry

            with patch("gfp_mcp.render.render_gds_to_png", return_value=None):
                result = await render_built_cells(["bad_cell"], project="test_project")
                assert result == []


class TestClientModule:
    """Tests for gfp_mcp.client module."""

    def test_get_project_info_removed(self) -> None:
        """Verify get_project_info method is NOT in gfp_mcp.client.FastAPIClient."""
        from gfp_mcp.client import FastAPIClient

        assert not hasattr(FastAPIClient, "get_project_info")
        # Also verify it's not in an instance
        client = FastAPIClient()
        assert not hasattr(client, "get_project_info")

    @pytest.mark.anyio
    async def test_client_lifecycle(self) -> None:
        """Test FastAPIClient start and close."""
        from gfp_mcp.client import FastAPIClient

        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_client_context_manager(self) -> None:
        """Test FastAPIClient as context manager."""
        from gfp_mcp.client import FastAPIClient

        async with FastAPIClient() as client:
            assert client._client is not None  # noqa: SLF001
        assert client._client is None  # noqa: SLF001

    @pytest.mark.anyio
    @patch("httpx.AsyncClient")
    async def test_client_get_request(self, mock_client_class: Any) -> None:
        """Test GET request with mocked httpx client."""
        from gfp_mcp.client import FastAPIClient

        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

        client = FastAPIClient(base_url="http://localhost:9999")
        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()

    def test_list_projects(self) -> None:
        """Test list_projects method exists and works."""
        from gfp_mcp.client import FastAPIClient

        client = FastAPIClient()
        client._registry = MagicMock()  # noqa: SLF001
        client._registry.list_servers.return_value = []

        result = client.list_projects()
        assert result == []


class TestRegistryModule:
    """Tests for gfp_mcp.registry module."""

    def test_server_registry_import(self) -> None:
        """Test that ServerRegistry can be imported from gfp_mcp.registry."""
        from gfp_mcp.registry import ServerRegistry

        registry = ServerRegistry()
        assert hasattr(registry, "list_servers")
        assert hasattr(registry, "get_server")
        assert hasattr(registry, "get_server_by_project")

    def test_server_info_import(self) -> None:
        """Test that ServerInfo can be imported from gfp_mcp.registry."""
        from gfp_mcp.registry import ServerInfo

        info = ServerInfo(
            port=8787,
            pid=12345,
            project_path="/path/to/project",
            project_name="test_project",
        )
        assert info.port == 8787
        assert info.pid == 12345

    def test_get_registry_path_import(self) -> None:
        """Test that get_registry_path can be imported from gfp_mcp.registry."""
        from gfp_mcp.registry import get_registry_path

        path = get_registry_path()
        assert path.name == "server-registry.json"


class TestResourcesModule:
    """Tests for gfp_mcp.resources module."""

    def test_get_all_resources_import(self) -> None:
        """Test that get_all_resources can be imported from gfp_mcp.resources."""
        from gfp_mcp.resources import get_all_resources

        resources = get_all_resources()
        assert isinstance(resources, list)
        assert len(resources) > 0

    def test_get_resource_content_import(self) -> None:
        """Test that get_resource_content can be imported from gfp_mcp.resources."""
        from gfp_mcp.resources import get_resource_content

        content = get_resource_content("instructions://build_custom_cell")
        assert content is not None
        assert "Workflow" in content

    def test_resources_list_import(self) -> None:
        """Test that RESOURCES can be imported from gfp_mcp.resources."""
        from gfp_mcp.resources import RESOURCES

        assert isinstance(RESOURCES, list)


class TestConfigModule:
    """Tests for gfp_mcp.config module."""

    def test_mcp_config_import(self) -> None:
        """Test that MCPConfig can be imported from gfp_mcp.config."""
        from gfp_mcp.config import MCPConfig

        assert MCPConfig.TIMEOUT == 300
        assert MCPConfig.MAX_RETRIES == 3
        assert MCPConfig.RETRY_BACKOFF == 0.5

    def test_get_gfp_api_key_import(self) -> None:
        """Test that get_gfp_api_key can be imported from gfp_mcp.config."""
        from gfp_mcp.config import get_gfp_api_key

        # Just test that the function is callable
        result = get_gfp_api_key()
        # Result can be None or a string
        assert result is None or isinstance(result, str)

    def test_set_gfp_api_key_import(self) -> None:
        """Test that set_gfp_api_key can be imported from gfp_mcp.config."""
        from gfp_mcp.config import set_gfp_api_key

        # Just verify the function exists and requires a non-empty key
        with pytest.raises(ValueError, match="API key is required"):
            set_gfp_api_key("")


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