"""DRC (Design Rule Check) tool handler.

This module provides the handler for running DRC checks on GDS files,
including XML parsing and actionable violation summaries.
"""

from __future__ import annotations

import json
import logging
import xml.etree.ElementTree as ET
from pathlib import PurePosixPath
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_polygon_location,
    parse_rdb_categories_flat,
    parse_rdb_cells,
)

if TYPE_CHECKING:
    from ..client import FastAPIClient

logger = logging.getLogger(__name__)

__all__ = ["CheckDrcHandler"]


def _generate_drc_recommendations(
    total_violations: int,
    violations_by_category: list[dict[str, Any]],
    cells: list[str],
) -> list[str]:
    """Generate actionable recommendations based on DRC results.

    Args:
        total_violations: Total number of violations
        violations_by_category: List of violation categories with counts
        cells: List of cells checked

    Returns:
        List of recommendation strings with critical warnings
    """
    if total_violations == 0:
        return [
            "Design passes all DRC checks and is ready for fabrication",
            "All design rules are satisfied — no manufacturing constraints violated",
        ]

    # Start with critical warning
    recommendations = [
        "⚠️  CRITICAL: This design has DRC violations and CANNOT be fabricated",
        "⚠️  Foundries will REJECT layouts with DRC errors — all violations must be fixed",
    ]

    if violations_by_category:
        top_category = violations_by_category[0]
        if top_category["count"] > 10:
            recommendations.append(
                f"PRIORITY: Fix {top_category['category']} violations first "
                f"({top_category['count']:,} occurrences) — likely a systematic issue"
            )
        elif top_category["count"] > 1:
            recommendations.append(
                f"PRIORITY: Address {top_category['category']} rules in {', '.join(cells)}"
            )

    if len(violations_by_category) > 1:
        recommendations.append(
            f"Total of {len(violations_by_category)} different DRC rule categories violated — "
            "review each category and fix all violations"
        )

    if total_violations > 100:
        recommendations.append(
            "⚠️  High violation count suggests fundamental design issues — "
            "consider redesigning problem areas rather than fixing individual errors"
        )

    recommendations.append(
        "After fixing violations, re-run DRC to verify all issues are resolved"
    )

    return recommendations


class CheckDrcHandler(ToolHandler):
    """Handler for running DRC checks on GDS files.

    Parses KLayout RDB XML and extracts actionable violation summary
    without polygon coordinates or verbose metadata. Computes simplified
    location data (bounding box, centroid, size) from polygon coordinates
    to reduce token usage while preserving all violation details.
    """

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

    @property
    def definition(self) -> Tool:
        return Tool(
            name="check_drc",
            description=(
                "Run a full DRC (Design Rule Check) on a GDS file. This uploads "
                "the file to a remote DRC server and runs comprehensive design rule "
                "verification for the specified PDK and process. Use this for "
                "complete design rule validation. IMPORTANT: DRC violations are "
                "CRITICAL errors that will cause foundries to REJECT the design. "
                "ALL violations must be fixed before fabrication. Returns structured "
                "results showing all violations with actionable recommendations."
            ),
            inputSchema=add_project_param(
                {
                    "type": "object",
                    "properties": {
                        "path": {
                            "type": "string",
                            "description": (
                                "Path to the GDS file to check. Can be absolute or "
                                "relative to the project directory."
                            ),
                        },
                        "pdk": {
                            "type": "string",
                            "description": (
                                "PDK to use for the check. If not specified, uses the "
                                "default PDK from settings."
                            ),
                        },
                        "process": {
                            "type": "string",
                            "description": (
                                "Process variant for DRC rules. If not specified, uses "
                                "the default process from settings."
                            ),
                        },
                        "timeout": {
                            "type": "integer",
                            "description": (
                                "Timeout in seconds for the DRC check. If not specified, "
                                "uses the default timeout from settings."
                            ),
                        },
                        "host": {
                            "type": "string",
                            "description": (
                                "API host for the DRC server. If not specified, uses "
                                "the default host from settings."
                            ),
                        },
                    },
                    "required": ["path"],
                }
            ),
        )

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

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

        Args:
            args: MCP tool arguments

        Returns:
            Dict with 'json_data' key for request body
        """
        json_data: dict[str, Any] = {"path": args["path"]}

        if "pdk" in args and args["pdk"]:
            json_data["pdk"] = args["pdk"]
        if "process" in args and args["process"]:
            json_data["process"] = args["process"]
        if "timeout" in args and args["timeout"]:
            json_data["timeout"] = args["timeout"]
        if "host" in args and args["host"]:
            json_data["host"] = args["host"]

        return {"json_data": json_data}

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

        Parses KLayout RDB XML and extracts actionable violation summary
        without polygon coordinates or verbose metadata. Computes simplified
        location data (bounding box, centroid, size) from polygon coordinates
        to reduce token usage by ~82% while preserving all violation details.

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

        Returns:
            Structured summary with all violations including location data,
            or error dict if parsing fails
        """
        xml_str, error = extract_xml_string(response, check_name="DRC")
        if error is not None:
            return error

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

        categories_map = parse_rdb_categories_flat(root)
        cells = parse_rdb_cells(root)

        violations = []
        violation_counts: dict[str, int] = {}

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

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

            category = category_elem.text
            cell = (
                cell_elem.text
                if cell_elem is not None and cell_elem.text
                else "unknown"
            )
            description = (
                comment_elem.text
                if comment_elem is not None and comment_elem.text
                else categories_map.get(category, category)
            )

            location = parse_polygon_location(values_elem)

            violation = {
                "category": category,
                "cell": cell,
                "description": description,
            }

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

            violations.append(violation)

            violation_counts[category] = violation_counts.get(category, 0) + 1

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

        summary = {
            "total_violations": total_violations,
            "total_categories": len(violation_counts),
            "cells_checked": cells,
            "status": status,
            "fabrication_ready": fabrication_ready,
            "severity": "NONE" if total_violations == 0 else "CRITICAL",
        }

        if total_violations == 0:
            summary["message"] = (
                "No DRC violations found — design is ready for fabrication"
            )
        else:
            summary["message"] = (
                f"CRITICAL: {total_violations:,} DRC violation(s) found — "
                "design CANNOT be fabricated until ALL violations are fixed"
            )

        violations_by_category = [
            {
                "category": cat,
                "description": categories_map.get(cat, cat),
                "count": count,
            }
            for cat, count in sorted(
                violation_counts.items(),
                key=lambda x: x[1],
                reverse=True,
            )
        ]

        recommendations = _generate_drc_recommendations(
            total_violations,
            violations_by_category,
            cells,
        )

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

    async def handle(
        self,
        arguments: dict[str, Any],
        client: FastAPIClient,
    ) -> list[TextContent | ImageContent]:
        """Run DRC 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:
                    cell_name = PurePosixPath(arguments["path"]).stem
                    await notify_show_check_results(
                        arguments.get("project"), "DRC", cell_name
                    )
            except Exception:
                logger.debug("Failed to send DRC notification", exc_info=True)

        return result
