"""Unit tests for gfp_mcp tool handlers.

These tests verify that the new tool handler architecture produces
identical results to the original mappings.py implementation.
"""

from __future__ import annotations

import pytest

from gfp_mcp.tools import (
    TOOL_HANDLERS,
    get_all_tools,
    get_handler,
    get_tool_by_name,
)
from gfp_mcp.tools.bbox import GenerateBboxHandler
from gfp_mcp.tools.build import BuildCellsHandler
from gfp_mcp.tools.cells import GetCellInfoHandler, ListCellsHandler
from gfp_mcp.tools.connectivity import CheckConnectivityHandler
from gfp_mcp.tools.drc import (
    CheckDrcHandler,
    _generate_drc_recommendations,
    _parse_polygon_location,
)
from gfp_mcp.tools.freeze import FreezeCellHandler
from gfp_mcp.tools.lvs import CheckLvsHandler
from gfp_mcp.tools.pdk import GetPdkInfoHandler
from gfp_mcp.tools.port import GetPortCenterHandler
from gfp_mcp.tools.project import GetProjectInfoHandler, ListProjectsHandler
from gfp_mcp.tools.simulation import SimulateComponentHandler


class TestToolRegistry:
    """Test the tool handler registry."""

    def test_all_handlers_registered(self) -> None:
        """Test that all active handlers are registered."""
        assert len(TOOL_HANDLERS) == 11

    def test_get_all_tools_returns_correct_count(self) -> None:
        """Test that get_all_tools returns all tool definitions."""
        tools = get_all_tools()
        assert len(tools) == 11

    def test_get_handler_returns_correct_types(self) -> None:
        """Test that get_handler returns correct handler types."""
        assert isinstance(get_handler("list_projects"), ListProjectsHandler)
        assert isinstance(get_handler("get_project_info"), GetProjectInfoHandler)
        assert isinstance(get_handler("build_cells"), BuildCellsHandler)
        assert isinstance(get_handler("list_cells"), ListCellsHandler)
        assert isinstance(get_handler("get_cell_info"), GetCellInfoHandler)
        assert isinstance(get_handler("check_drc"), CheckDrcHandler)
        assert isinstance(get_handler("check_connectivity"), CheckConnectivityHandler)
        assert isinstance(get_handler("check_lvs"), CheckLvsHandler)
        assert isinstance(get_handler("simulate_component"), SimulateComponentHandler)

    def test_get_handler_nonexistent(self) -> None:
        """Test that get_handler returns None for nonexistent tool."""
        assert get_handler("nonexistent_tool") is None

    def test_get_tool_by_name(self) -> None:
        """Test getting a tool definition by name."""
        tool = get_tool_by_name("build_cells")
        assert tool is not None
        assert tool.name == "build_cells"
        assert tool.description
        assert tool.inputSchema

    def test_get_tool_by_name_nonexistent(self) -> None:
        """Test getting a nonexistent tool returns None."""
        assert get_tool_by_name("nonexistent_tool") is None

    def test_all_tools_have_unique_names(self) -> None:
        """Test that all tools have unique names."""
        tools = get_all_tools()
        names = [tool.name for tool in tools]
        assert len(names) == len(set(names))


class TestProjectHandlers:
    """Test project discovery tool handlers."""

    def test_list_projects_handler_properties(self) -> None:
        """Test ListProjectsHandler properties."""
        handler = ListProjectsHandler()
        assert handler.name == "list_projects"
        assert handler.mapping is None  # Registry-only tool
        assert handler.definition.name == "list_projects"

    def test_get_project_info_handler_properties(self) -> None:
        """Test GetProjectInfoHandler properties."""
        handler = GetProjectInfoHandler()
        assert handler.name == "get_project_info"
        assert handler.mapping is not None
        assert handler.mapping.method == "GET"
        assert handler.mapping.path == "/info"


class TestBuildCellsHandler:
    """Test BuildCellsHandler."""

    def test_handler_properties(self) -> None:
        """Test handler properties."""
        handler = BuildCellsHandler()
        assert handler.name == "build_cells"
        assert handler.mapping.method == "POST"
        assert handler.mapping.path == "/api/build-cells"

    def test_transform_request(self) -> None:
        """Test request transformation."""
        handler = BuildCellsHandler()

        args = {"names": ["cell1", "cell2"], "with_metadata": False}
        transformed = handler.transform_request(args)

        assert "params" in transformed
        assert "json_data" in transformed
        assert transformed["json_data"] == ["cell1", "cell2"]
        assert transformed["params"]["with_metadata"] is False

    def test_transform_request_defaults(self) -> None:
        """Test request transformation with default values."""
        handler = BuildCellsHandler()

        args = {"names": ["test_cell"]}
        transformed = handler.transform_request(args)

        assert transformed["params"]["with_metadata"] is True
        assert transformed["params"]["register"] is True

    def test_schema_has_visualize_parameter(self) -> None:
        """Test that build_cells schema includes visualize parameter."""
        handler = BuildCellsHandler()
        schema = handler.definition.inputSchema
        assert "visualize" in schema["properties"]
        assert schema["properties"]["visualize"]["type"] == "array"


class TestCellsHandlers:
    """Test cell listing and info handlers."""

    def test_list_cells_handler(self) -> None:
        """Test ListCellsHandler."""
        handler = ListCellsHandler()
        assert handler.name == "list_cells"
        assert handler.mapping.method == "GET"
        assert handler.mapping.path == "/api/cells"

    def test_list_cells_transform_response(self) -> None:
        """Test ListCellsHandler response transformation."""
        handler = ListCellsHandler()

        response = ["cell1", "cell2", "cell3"]
        transformed = handler.transform_response(response)

        assert transformed["cells"] == ["cell1", "cell2", "cell3"]
        assert transformed["count"] == 3

    def test_get_cell_info_handler(self) -> None:
        """Test GetCellInfoHandler."""
        handler = GetCellInfoHandler()
        assert handler.name == "get_cell_info"
        assert handler.mapping.method == "GET"
        assert handler.mapping.path == "/api/cell-info"

    def test_get_cell_info_transform_request(self) -> None:
        """Test GetCellInfoHandler request transformation."""
        handler = GetCellInfoHandler()

        args = {"name": "test_cell"}
        transformed = handler.transform_request(args)

        assert transformed["params"]["name"] == "test_cell"


class TestCheckDrcHandler:
    """Test DRC check handler."""

    def test_handler_properties(self) -> None:
        """Test handler properties."""
        handler = CheckDrcHandler()
        assert handler.name == "check_drc"
        assert handler.mapping.method == "POST"
        assert handler.mapping.path == "/api/check-drc"

    def test_transform_request_minimal(self) -> None:
        """Test request transformation with minimal args."""
        handler = CheckDrcHandler()

        args = {"path": "build/gds/test_cell.gds"}
        transformed = handler.transform_request(args)

        assert transformed["json_data"]["path"] == "build/gds/test_cell.gds"
        assert "pdk" not in transformed["json_data"]

    def test_transform_request_full(self) -> None:
        """Test request transformation with all optional args."""
        handler = CheckDrcHandler()

        args = {
            "path": "build/gds/test_cell.gds",
            "pdk": "sky130",
            "process": "A",
            "timeout": 600,
            "host": "https://drc.example.com",
        }
        transformed = handler.transform_request(args)

        assert transformed["json_data"]["path"] == "build/gds/test_cell.gds"
        assert transformed["json_data"]["pdk"] == "sky130"
        assert transformed["json_data"]["process"] == "A"
        assert transformed["json_data"]["timeout"] == 600
        assert transformed["json_data"]["host"] == "https://drc.example.com"

    def test_transform_response_with_violations(self) -> None:
        """Test DRC response transformation with violations."""
        handler = CheckDrcHandler()

        xml_response = """<?xml version="1.0" encoding="utf-8"?>
<report-database>
 <categories>
  <category>
   <name>Width_Si_Etch1</name>
   <description>Min width violation</description>
  </category>
 </categories>
 <cells>
  <cell><name>test_cell</name></cell>
 </cells>
 <items>
  <item>
   <category>Width_Si_Etch1</category>
   <cell>test_cell</cell>
   <comment>Width violation</comment>
   <values>
    <value>polygon: (1.0,2.0;3.0,4.0;5.0,6.0)</value>
   </values>
  </item>
 </items>
</report-database>"""

        transformed = handler.transform_response(xml_response)

        assert transformed["summary"]["total_violations"] == 1
        assert transformed["summary"]["status"] == "FAILED"
        assert "location" in transformed["violations"][0]

    def test_transform_response_no_violations(self) -> None:
        """Test DRC response with no violations."""
        handler = CheckDrcHandler()

        xml_response = """<?xml version="1.0" encoding="utf-8"?>
<report-database>
 <categories></categories>
 <cells><cell><name>clean_cell</name></cell></cells>
 <items></items>
</report-database>"""

        transformed = handler.transform_response(xml_response)

        assert transformed["summary"]["total_violations"] == 0
        assert transformed["summary"]["status"] == "PASSED"

    def test_transform_response_invalid_xml(self) -> None:
        """Test error handling for invalid XML."""
        handler = CheckDrcHandler()

        transformed = handler.transform_response("not valid xml")

        assert "error" in transformed

    def test_transform_response_json_wrapped(self) -> None:
        """Test handling of JSON-wrapped XML."""
        handler = CheckDrcHandler()

        xml_content = """<?xml version="1.0"?>
<report-database>
 <categories></categories>
 <cells><cell><name>test</name></cell></cells>
 <items></items>
</report-database>"""

        transformed = handler.transform_response({"content": xml_content})

        assert "summary" in transformed
        assert transformed["summary"]["status"] == "PASSED"


class TestParsePolygonLocation:
    """Test polygon location parsing helper."""

    def test_parse_valid_polygon(self) -> None:
        """Test parsing valid polygon coordinates."""
        import xml.etree.ElementTree as ET

        values_elem = ET.fromstring(
            "<values><value>polygon: (1.0,2.0;3.0,4.0;5.0,6.0)</value></values>"
        )
        result = _parse_polygon_location(values_elem)

        assert result is not None
        assert "bbox" in result
        assert "centroid" in result
        assert "size" in result

    def test_parse_none_element(self) -> None:
        """Test parsing None element."""
        assert _parse_polygon_location(None) is None

    def test_parse_non_polygon(self) -> None:
        """Test parsing non-polygon value."""
        import xml.etree.ElementTree as ET

        values_elem = ET.fromstring("<values><value>not a polygon</value></values>")
        result = _parse_polygon_location(values_elem)

        assert result is None


class TestGenerateDrcRecommendations:
    """Test DRC recommendations generator."""

    def test_no_violations(self) -> None:
        """Test recommendations for no violations."""
        recommendations = _generate_drc_recommendations(0, [], [])
        assert "Design passes all DRC checks" in recommendations

    def test_many_violations(self) -> None:
        """Test recommendations for many violations."""
        violations_by_category = [{"category": "Width", "count": 150}]
        recommendations = _generate_drc_recommendations(
            150, violations_by_category, ["cell"]
        )

        assert any("Focus on" in r for r in recommendations)
        assert any("systematic" in r.lower() for r in recommendations)


class TestCheckConnectivityHandler:
    """Test connectivity check handler."""

    def test_handler_properties(self) -> None:
        """Test handler properties."""
        handler = CheckConnectivityHandler()
        assert handler.name == "check_connectivity"
        assert handler.mapping.method == "POST"
        assert handler.mapping.path == "/api/check-connectivity"

    def test_transform_request(self) -> None:
        """Test request transformation."""
        handler = CheckConnectivityHandler()

        args = {"path": "build/gds/test.gds"}
        transformed = handler.transform_request(args)

        assert transformed["json_data"]["path"] == "build/gds/test.gds"


class TestCheckLvsHandler:
    """Test LVS check handler."""

    def test_handler_properties(self) -> None:
        """Test handler properties."""
        handler = CheckLvsHandler()
        assert handler.name == "check_lvs"
        assert handler.mapping.method == "POST"
        assert handler.mapping.path == "/api/check-lvs"

    def test_transform_request(self) -> None:
        """Test request transformation."""
        handler = CheckLvsHandler()

        args = {"cell": "mzi", "netpath": "netlists/mzi.spice"}
        transformed = handler.transform_request(args)

        assert transformed["json_data"]["cell"] == "mzi"
        assert transformed["json_data"]["netpath"] == "netlists/mzi.spice"
        assert transformed["json_data"]["cellargs"] == ""

    def test_transform_request_with_cellargs(self) -> None:
        """Test request transformation with cellargs."""
        handler = CheckLvsHandler()

        args = {
            "cell": "mzi",
            "netpath": "netlists/mzi.spice",
            "cellargs": '{"length": 10}',
        }
        transformed = handler.transform_request(args)

        assert transformed["json_data"]["cellargs"] == '{"length": 10}'


class TestSimulateComponentHandler:
    """Test simulation handler."""

    def test_handler_properties(self) -> None:
        """Test handler properties."""
        handler = SimulateComponentHandler()
        assert handler.name == "simulate_component"
        assert handler.mapping.method == "POST"

    def test_transform_request_minimal(self) -> None:
        """Test request transformation with minimal args."""
        handler = SimulateComponentHandler()

        args = {"name": "mzi"}
        transformed = handler.transform_request(args)

        assert transformed["path"] == "/api/sax/mzi/simulate"
        assert transformed["json_data"]["layout"] == {}
        assert transformed["json_data"]["model"] == {}
        assert transformed["json_data"]["from_netlist"] is False
        assert transformed["fallback_path"] == "/api/simulate"

    def test_transform_request_full(self) -> None:
        """Test request transformation with all args."""
        handler = SimulateComponentHandler()

        args = {
            "name": "mzi",
            "layout": {"length": 100},
            "model": {"wl": [1.5, 1.55]},
            "how": "from_netlist",
        }
        transformed = handler.transform_request(args)

        assert transformed["json_data"]["layout"] == {"length": 100}
        assert transformed["json_data"]["model"] == {"wl": [1.5, 1.55]}
        assert transformed["json_data"]["from_netlist"] is True

    def test_transform_response(self) -> None:
        """Test response transformation."""
        handler = SimulateComponentHandler()

        response = {
            "wavelengths": [1.5, 1.55],
            "sdict": {},
            "file_path": "/path/to/results.json",
            "simulation_id": "abc123",
            "cached": False,
        }
        transformed = handler.transform_response(response)

        assert transformed["file_path"] == "/path/to/results.json"
        assert "estimated_tokens" in transformed
        assert "wavelengths" not in transformed
        assert "sdict" not in transformed

    def test_transform_response_unexpected_type(self) -> None:
        """Test response transformation with unexpected type."""
        handler = SimulateComponentHandler()

        transformed = handler.transform_response("not a dict")
        assert "error" in transformed


class TestGetPortCenterHandler:
    """Test port center handler."""

    def test_handler_properties(self) -> None:
        """Test handler properties."""
        handler = GetPortCenterHandler()
        assert handler.name == "get_port_center"
        assert handler.mapping.method == "GET"
        assert handler.mapping.path == "/api/port-center"

    def test_transform_request(self) -> None:
        """Test request transformation."""
        handler = GetPortCenterHandler()

        args = {"netlist": "circuit", "instance": "coupler1", "port": "o1"}
        transformed = handler.transform_request(args)

        assert transformed["params"]["netlist"] == "circuit"
        assert transformed["params"]["instance"] == "coupler1"
        assert transformed["params"]["port"] == "o1"


class TestGenerateBboxHandler:
    """Test bounding box handler."""

    def test_handler_properties(self) -> None:
        """Test handler properties."""
        handler = GenerateBboxHandler()
        assert handler.name == "generate_bbox"
        assert handler.mapping.method == "POST"
        assert handler.mapping.path == "/api/bbox"

    def test_transform_request_minimal(self) -> None:
        """Test request transformation with minimal args."""
        handler = GenerateBboxHandler()

        args = {"path": "build/gds/design.gds"}
        transformed = handler.transform_request(args)

        assert transformed["json_data"]["path"] == "build/gds/design.gds"
        assert "outpath" not in transformed["json_data"]

    def test_transform_request_full(self) -> None:
        """Test request transformation with all args."""
        handler = GenerateBboxHandler()

        args = {
            "path": "build/gds/design.gds",
            "outpath": "build/gds/design-bbox.gds",
            "layers_to_keep": ["WG", "M1"],
            "bbox_layer": [99, 0],
            "ignore_ports": True,
        }
        transformed = handler.transform_request(args)

        assert transformed["json_data"]["outpath"] == "build/gds/design-bbox.gds"
        assert transformed["json_data"]["layers_to_keep"] == ["WG", "M1"]
        assert transformed["json_data"]["ignore_ports"] is True


class TestFreezeCellHandler:
    """Test freeze cell handler."""

    def test_handler_properties(self) -> None:
        """Test handler properties."""
        handler = FreezeCellHandler()
        assert handler.name == "freeze_cell"
        assert handler.mapping.method == "POST"

    def test_transform_request_minimal(self) -> None:
        """Test request transformation with minimal args."""
        handler = FreezeCellHandler()

        args = {"cell_name": "mzi"}
        transformed = handler.transform_request(args)

        assert transformed["path"] == "/freeze/mzi"
        assert transformed["json_data"] == {}

    def test_transform_request_with_kwargs(self) -> None:
        """Test request transformation with kwargs."""
        handler = FreezeCellHandler()

        args = {"cell_name": "mzi", "kwargs": {"length": 100}}
        transformed = handler.transform_request(args)

        assert transformed["path"] == "/freeze/mzi"
        assert transformed["json_data"] == {"length": 100}


class TestGetPdkInfoHandler:
    """Test PDK info handler."""

    def test_handler_properties(self) -> None:
        """Test handler properties."""
        handler = GetPdkInfoHandler()
        assert handler.name == "get_pdk_info"
        assert handler.mapping.method == "GET"
        assert handler.mapping.path == "/info"


class TestSchemaConsistency:
    """Test that handler schemas match expected structure."""

    def test_all_tools_have_valid_schemas(self) -> None:
        """Test all tools have valid input schemas."""
        for tool in get_all_tools():
            assert tool.name
            assert tool.description
            assert tool.inputSchema
            assert "type" in tool.inputSchema
            assert tool.inputSchema["type"] == "object"
            assert "properties" in tool.inputSchema

    def test_project_param_in_http_tools(self) -> None:
        """Test that HTTP-backed tools have project parameter."""
        http_tools = [
            "build_cells",
            "list_cells",
            "get_cell_info",
            "check_drc",
            "check_connectivity",
            "check_lvs",
            "simulate_component",
        ]

        for tool_name in http_tools:
            tool = get_tool_by_name(tool_name)
            assert tool is not None, f"Tool {tool_name} not found"
            assert "project" in tool.inputSchema["properties"], (
                f"Tool {tool_name} missing project param"
            )


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