"""LVS (Layout vs. Schematic) check tool handler.

This module provides the handler for running LVS verification
on cells against reference netlists, including XML parsing and
actionable mismatch summaries.
"""

from __future__ import annotations

import json
import logging
import xml.etree.ElementTree as ET
from typing import TYPE_CHECKING, Any

from mcp.types import ImageContent, TextContent, Tool

from ..config import MCPConfig
from ..notify import notify_show_check_results
from .base import EndpointMapping, ToolHandler, add_project_param
from .rdb_parser import (
    extract_xml_string,
    parse_category_path,
    parse_polygon_location,
    parse_rdb_cells,
)

if TYPE_CHECKING:
    from ..client import FastAPIClient

logger = logging.getLogger(__name__)

__all__ = ["CheckLvsHandler"]

LVS_VIOLATION_INFO: dict[str, dict[str, str]] = {
    "NetMismatch": {
        "description": "Net connectivity differs between layout and schematic",
        "guidance": (
            "The layout net connectivity does not match the schematic. Check "
            "that all waveguide routes and electrical connections match the "
            "intended netlist topology."
        ),
    },
    "DeviceMismatch": {
        "description": "Device count or type differs between layout and schematic",
        "guidance": (
            "The number or type of devices in the layout does not match the "
            "schematic. Verify that all components are placed and that no "
            "extra or missing instances exist."
        ),
    },
    "PinMismatch": {
        "description": "Pin assignments differ between layout and schematic",
        "guidance": (
            "Pin labels or positions in the layout do not match the schematic. "
            "Check that port names and pin assignments are consistent between "
            "layout and netlist."
        ),
    },
    "TopologyMismatch": {
        "description": "Overall circuit topology differs",
        "guidance": (
            "The circuit topology extracted from the layout does not match the "
            "schematic. This is a high-level mismatch — review the overall "
            "connectivity and component placement."
        ),
    },
}

_LVS_VIOLATION_INFO_LOWER = {k.lower(): v for k, v in LVS_VIOLATION_INFO.items()}


def _lookup_violation_info(
    type_name: str,
    info_map: dict[str, dict[str, str]],
    lower_map: dict[str, dict[str, str]],
) -> dict[str, str] | None:
    """Case-insensitive lookup in a violation info dictionary."""
    return info_map.get(type_name) or lower_map.get(type_name.lower())


def _generate_lvs_recommendations(
    total_violations: int,
    violations_by_type: list[dict[str, Any]],
) -> list[str]:
    """Generate actionable recommendations based on LVS results.

    Args:
        total_violations: Total number of violations
        violations_by_type: List of violation types with counts

    Returns:
        List of recommendation strings with critical warnings
    """
    if total_violations == 0:
        return [
            "Design passes all LVS checks — layout matches schematic perfectly",
            "Layout connectivity matches design intent — device will function as specified",
        ]

    # Start with critical warning
    recommendations = [
        "⚠️  CRITICAL: Layout does NOT match schematic — design intent is violated",
        "⚠️  LVS failures mean the fabricated device will NOT match your specifications",
    ]

    for vtype in violations_by_type:
        type_name = vtype["type"]
        info = _lookup_violation_info(
            type_name, LVS_VIOLATION_INFO, _LVS_VIOLATION_INFO_LOWER
        )
        if info:
            recommendations.append(f"MUST FIX — {type_name}: {info['guidance']}")
        else:
            recommendations.append(
                f"MUST FIX — {type_name}: Review these violations — "
                "this is an unrecognized LVS violation type"
            )

    if violations_by_type:
        top = violations_by_type[0]
        if top["count"] > 10:
            recommendations.append(
                f"PRIORITY: Address {top['type']} first ({top['count']:,} occurrences) — "
                "fixing systematic issues will resolve many violations at once"
            )

    recommendations.append(
        "After fixing violations, re-run LVS to verify layout matches schematic"
    )

    return recommendations


class CheckLvsHandler(ToolHandler):
    """Handler for running LVS verification.

    Compares physical layout to schematic representation
    to ensure they match. Parses KLayout RDB XML and extracts
    structured mismatch summaries with guidance.
    """

    @property
    def name(self) -> str:
        return "check_lvs"

    @property
    def definition(self) -> Tool:
        return Tool(
            name="check_lvs",
            description=(
                "Run LVS (Layout vs. Schematic) verification on a cell against a "
                "reference netlist. This compares the physical layout to the "
                "schematic representation to ensure they match. IMPORTANT: LVS "
                "violations are CRITICAL errors indicating the layout does NOT match "
                "the design intent. ALL violations must be fixed to ensure the "
                "fabricated device matches specifications. Returns structured results "
                "showing mismatches with actionable recommendations."
            ),
            inputSchema=add_project_param(
                {
                    "type": "object",
                    "properties": {
                        "cell": {
                            "type": "string",
                            "description": "Name of the cell to verify",
                        },
                        "netpath": {
                            "type": "string",
                            "description": (
                                "Path to the reference netlist file to compare against"
                            ),
                        },
                        "cellargs": {
                            "type": "string",
                            "description": (
                                "Optional cell arguments as a JSON string. "
                                "Default is empty string."
                            ),
                            "default": "",
                        },
                    },
                    "required": ["cell", "netpath"],
                }
            ),
        )

    @property
    def mapping(self) -> EndpointMapping:
        return EndpointMapping(method="POST", path="/api/check-lvs")

    def transform_request(self, args: dict[str, Any]) -> dict[str, Any]:
        """Transform check_lvs MCP args to FastAPI params.

        Args:
            args: MCP tool arguments

        Returns:
            Dict with 'json_data' key for request body
        """
        return {
            "json_data": {
                "cell": args["cell"],
                "netpath": args["netpath"],
                "cellargs": args.get("cellargs", ""),
            }
        }

    def transform_response(self, response: Any) -> dict[str, Any]:
        """Transform LVS response to LLM-friendly format.

        Parses KLayout RDB XML and extracts structured mismatch summaries
        with type-specific guidance.

        Args:
            response: FastAPI response (XML string or dict with 'content' key)

        Returns:
            Structured summary with violations and recommendations,
            or error dict if parsing fails
        """
        xml_str, error = extract_xml_string(response, check_name="LVS")
        if error is not None:
            return error

        try:
            root = ET.fromstring(xml_str)
        except ET.ParseError as e:
            return {
                "error": f"Failed to parse LVS XML: {e}",
                "raw_preview": xml_str[:500],
                "suggestion": "Check if the LVS check returned valid XML",
            }

        cells = parse_rdb_cells(root)

        violations = []
        type_counts: dict[str, dict[str, Any]] = {}

        for item in root.findall(".//items/item"):
            category_elem = item.find("category")
            cell_elem = item.find("cell")
            values_elem = item.find("values")

            if category_elem is None or category_elem.text is None:
                continue

            parent, child = parse_category_path(category_elem.text)

            cell = (
                cell_elem.text
                if cell_elem is not None and cell_elem.text
                else "unknown"
            )

            violation_type = child if child else parent
            context = parent if child else None

            location = parse_polygon_location(values_elem)

            violation: dict[str, Any] = {
                "category": violation_type,
                "cell": cell,
            }
            if context:
                violation["context"] = context

            if location is not None:
                violation["location"] = location
            elif values_elem is not None:
                violation["location_warning"] = "Could not parse coordinates"

            violations.append(violation)

            if violation_type not in type_counts:
                type_counts[violation_type] = {"count": 0}
            type_counts[violation_type]["count"] += 1

        total_violations = len(violations)
        status = "PASSED" if total_violations == 0 else "FAILED"
        matches_schematic = total_violations == 0

        summary: dict[str, Any] = {
            "total_violations": total_violations,
            "total_categories": len(type_counts),
            "cells_checked": cells,
            "status": status,
            "matches_schematic": matches_schematic,
            "severity": "NONE" if total_violations == 0 else "CRITICAL",
        }

        if total_violations == 0:
            summary["message"] = (
                "No LVS violations found — layout matches schematic perfectly"
            )
        else:
            summary["message"] = (
                f"CRITICAL: {total_violations:,} LVS violation(s) found — "
                "layout does NOT match schematic and must be corrected"
            )

        violations_by_type = [
            {
                "type": vtype,
                "description": (
                    _lookup_violation_info(
                        vtype, LVS_VIOLATION_INFO, _LVS_VIOLATION_INFO_LOWER
                    )
                    or {}
                ).get("description", vtype),
                "count": data["count"],
            }
            for vtype, data in sorted(
                type_counts.items(),
                key=lambda x: x[1]["count"],
                reverse=True,
            )
        ]

        recommendations = _generate_lvs_recommendations(
            total_violations,
            violations_by_type,
        )

        return {
            "summary": summary,
            "violations_by_type": violations_by_type,
            "violations": violations,
            "recommendations": recommendations,
        }

    async def handle(
        self,
        arguments: dict[str, Any],
        client: FastAPIClient,
    ) -> list[TextContent | ImageContent]:
        """Run LVS check and notify VS Code extension."""
        result = await super().handle(arguments, client)

        if MCPConfig.UI_NOTIFICATIONS:
            try:
                result_data = json.loads(result[0].text) if result else {}
                if "error" not in result_data:
                    await notify_show_check_results(
                        arguments.get("project"), "LVS", arguments["cell"]
                    )
            except Exception:
                logger.debug("Failed to send LVS notification", exc_info=True)

        return result
