"""Tests for GDS rendering utilities.

These tests verify the file polling and PNG rendering functionality.
"""

from __future__ import annotations

import asyncio
import time
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

from gfp_mcp.render import (
    HAS_KLAYOUT,
    render_built_cells,
    render_gds_to_png,
)
from gfp_mcp.utils import wait_for_gds_file


class TestWaitForGdsFile:
    """Tests for wait_for_gds_file function."""

    @pytest.mark.anyio
    async def test_file_exists_immediately(self, tmp_path: Path) -> None:
        """Test waiting for a file that already exists."""
        gds_file = tmp_path / "test.gds"
        gds_file.write_bytes(b"test content")
        time.sleep(0.2)

        result = await wait_for_gds_file(gds_file, timeout=5.0, min_age=0.1)

        assert result is True

    @pytest.mark.anyio
    async def test_file_does_not_exist_timeout(self, tmp_path: Path) -> None:
        """Test timeout when file never appears."""
        gds_file = tmp_path / "nonexistent.gds"

        result = await wait_for_gds_file(gds_file, timeout=0.5, poll_interval=0.1)

        assert result is False

    @pytest.mark.anyio
    async def test_file_appears_during_polling(self, tmp_path: Path) -> None:
        """Test file appearing while polling."""
        gds_file = tmp_path / "delayed.gds"

        async def create_file_later() -> None:
            await asyncio.sleep(0.3)
            gds_file.write_bytes(b"delayed content")

        asyncio.create_task(create_file_later())

        result = await wait_for_gds_file(
            gds_file, timeout=2.0, poll_interval=0.1, min_age=0.0
        )

        assert result is True
        assert gds_file.exists()

    @pytest.mark.anyio
    async def test_file_too_new(self, tmp_path: Path) -> None:
        """Test file exists but is too new (still being written)."""
        gds_file = tmp_path / "new.gds"
        gds_file.write_bytes(b"content")

        result = await wait_for_gds_file(
            gds_file, timeout=0.3, poll_interval=0.1, min_age=10.0
        )

        assert result is False

    @pytest.mark.anyio
    async def test_default_parameters(self, tmp_path: Path) -> None:
        """Test with default parameters."""
        gds_file = tmp_path / "default.gds"
        gds_file.write_bytes(b"content")
        time.sleep(0.2)

        result = await wait_for_gds_file(gds_file, timeout=1.0)

        assert result is True


class TestRenderGdsToPng:
    """Tests for render_gds_to_png function."""

    def test_file_not_found(self, tmp_path: Path) -> None:
        """Test rendering a non-existent file."""
        gds_file = tmp_path / "nonexistent.gds"

        result = render_gds_to_png(gds_file)

        assert result is None

    @pytest.mark.skipif(not HAS_KLAYOUT, reason="klayout not installed")
    def test_render_valid_gds(self, tmp_path: Path) -> None:
        """Test rendering a valid GDS file (requires klayout)."""
        import klayout.db as db

        layout = db.Layout()
        top_cell = layout.create_cell("TOP")
        layer = layout.layer(1, 0)
        top_cell.shapes(layer).insert(db.Box(0, 0, 10000, 10000))

        gds_file = tmp_path / "test.gds"
        layout.write(str(gds_file))

        result = render_gds_to_png(gds_file)

        assert result is not None
        assert isinstance(result, bytes)
        assert result[:8] == b"\x89PNG\r\n\x1a\n"

    @pytest.mark.skipif(not HAS_KLAYOUT, reason="klayout not installed")
    def test_render_custom_dimensions(self, tmp_path: Path) -> None:
        """Test rendering with custom dimensions (requires klayout)."""
        import klayout.db as db

        layout = db.Layout()
        top_cell = layout.create_cell("TOP")
        layer = layout.layer(1, 0)
        top_cell.shapes(layer).insert(db.Box(0, 0, 10000, 10000))

        gds_file = tmp_path / "test.gds"
        layout.write(str(gds_file))

        result = render_gds_to_png(gds_file, width=400, height=300)

        assert result is not None
        assert isinstance(result, bytes)

    def test_render_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")

        with patch("gfp_mcp.render.HAS_KLAYOUT", False):
            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 TestHasKlayout:
    """Tests for klayout availability detection."""

    def test_has_klayout_type(self) -> None:
        """Test that HAS_KLAYOUT is a boolean."""
        assert isinstance(HAS_KLAYOUT, bool)

    def test_klayout_modules_consistent(self) -> None:
        """Test that klayout module availability is consistent."""
        from gfp_mcp import render

        if render.HAS_KLAYOUT:
            assert render.db is not None
            assert render.lay is not None
        else:
            assert render.db is None or render.lay is None


class TestServerIntegration:
    """Tests for server integration with rendering."""

    @pytest.mark.anyio
    async def test_render_built_cells_no_servers(self) -> None:
        """Test render_built_cells when no servers are available."""
        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_empty_names(self) -> None:
        """Test render_built_cells with empty names list."""
        result = await render_built_cells(
            [],
            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

        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
    @pytest.mark.skipif(not HAS_KLAYOUT, reason="klayout not installed")
    async def test_render_built_cells_success(self, tmp_path: Path) -> None:
        """Test successful rendering of built cells."""
        import klayout.db as db

        from gfp_mcp.registry import ServerInfo

        gds_dir = tmp_path / "build" / "gds"
        gds_dir.mkdir(parents=True)

        layout = db.Layout()
        top_cell = layout.create_cell("test_cell")
        layer = layout.layer(1, 0)
        top_cell.shapes(layer).insert(db.Box(0, 0, 10000, 10000))
        gds_file = gds_dir / "test_cell.gds"
        layout.write(str(gds_file))
        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

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

            assert len(result) == 1
            assert result[0].type == "image"
            assert result[0].mimeType == "image/png"
            assert len(result[0].data) > 0

    @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

        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 == []


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