"""Handlers for KiCad CLI export commands (Gerber, PDF, SVG, STEP, BOM CSV)."""

import os
import subprocess
import tempfile
from pathlib import Path


def handle_export_gerber(kicad_info: dict, args: dict) -> dict:
    """Export Gerber files from a PCB using kicad-cli."""
    return _run_pcb_export(kicad_info, args, "gerbers", "Gerber")


def handle_export_pdf(kicad_info: dict, args: dict) -> dict:
    """Export PDF from a PCB or schematic using kicad-cli."""
    file_path = args.get("filePath", "")
    if not file_path:
        return {"success": False, "error": "No filePath specified"}

    if file_path.endswith(".kicad_sch"):
        return _run_sch_export(kicad_info, args, "pdf", "PDF")
    else:
        return _run_pcb_export(kicad_info, args, "pdf", "PDF")


def handle_export_svg(kicad_info: dict, args: dict) -> dict:
    """Export SVG from a PCB using kicad-cli."""
    return _run_pcb_export(kicad_info, args, "svg", "SVG")


def handle_export_step(kicad_info: dict, args: dict) -> dict:
    """Export 3D STEP file from a PCB using kicad-cli."""
    return _run_pcb_export(kicad_info, args, "step", "STEP")


def handle_export_bom_csv(kicad_info: dict, args: dict) -> dict:
    """Export BOM CSV from a schematic using kicad-cli."""
    return _run_sch_export(kicad_info, args, "bom", "BOM CSV")


def handle_run_erc(kicad_info: dict, args: dict) -> dict:
    """Run ERC (Electrical Rules Check) on a schematic using kicad-cli."""
    if not kicad_info.get("installed"):
        return {
            "success": False,
            "error": "KiCad not installed",
            "_hint": "Report to the user and ask them to install KiCad from https://www.kicad.org/download/, then retry.",
        }

    file_path = args.get("filePath", "")
    if not file_path:
        return {"success": False, "error": "No filePath specified"}

    sch_path = Path(file_path)
    if not sch_path.exists():
        return {
            "success": False,
            "error": f"Schematic file not found: {file_path}",
            "_hint": "The path may exist on Docker but not on the Windows host. Use push_file to copy from Docker first, or verify the host-side path.",
        }

    cli_exe = kicad_info.get("kicad_cli_exe")
    if not cli_exe or not Path(cli_exe).exists():
        return {
            "success": False,
            "error": "kicad-cli not found",
            "_hint": "Report to the user and ask them to reinstall KiCad — kicad-cli is missing from the installation.",
        }

    tmp_fd, output_path = tempfile.mkstemp(suffix=".json")
    os.close(tmp_fd)

    try:
        cmd = [
            cli_exe,
            "sch", "erc",
            "--format", "json",
            "--severity-all",
            "--output", output_path,
            str(sch_path),
        ]

        result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)

        output_file = Path(output_path)
        if not output_file.exists() or output_file.stat().st_size == 0:
            return {"success": False, "error": f"ERC produced no output. stderr: {result.stderr}"}

        import json
        erc_data = json.loads(output_file.read_text(encoding="utf-8"))

        violations = _parse_erc_violations(erc_data)

        total = len(violations)
        errors = sum(1 for v in violations if v["severity"] == "error")
        warnings = sum(1 for v in violations if v["severity"] == "warning")

        return {
            "success": True,
            "output": f"ERC complete: {errors} error(s), {warnings} warning(s), {total} total",
            "data": {
                "violations": violations,
                "summary": {"total": total, "errors": errors, "warnings": warnings},
                "source_file": str(sch_path),
            },
        }

    except subprocess.TimeoutExpired:
        return {"success": False, "error": "ERC timed out after 120 seconds"}
    except Exception as e:
        return {"success": False, "error": f"ERC failed: {e}"}
    finally:
        try:
            Path(output_path).unlink(missing_ok=True)
        except OSError:
            pass


def _parse_erc_violations(erc_data: dict) -> list:
    """Extract structured violation list from ERC JSON output."""
    violations = []
    for v in erc_data.get("violations", []):
        violation = {
            "type": v.get("type", "unknown"),
            "severity": v.get("severity", "warning"),
            "description": v.get("description", ""),
            "items": [],
        }
        for item in v.get("items", []):
            violation["items"].append({
                "description": item.get("description", ""),
                "pos": item.get("pos", {}),
            })
        violations.append(violation)
    return violations


def _run_pcb_export(kicad_info: dict, args: dict, export_type: str, label: str) -> dict:
    """Generic PCB export via kicad-cli pcb export <type>."""
    if not kicad_info.get("installed"):
        return {
            "success": False,
            "error": "KiCad not installed",
            "_hint": "Report to the user and ask them to install KiCad from https://www.kicad.org/download/, then retry.",
        }

    file_path = args.get("filePath", "")
    if not file_path:
        return {"success": False, "error": "No filePath specified"}

    pcb_path = Path(file_path)
    if not pcb_path.exists():
        return {
            "success": False,
            "error": f"PCB file not found: {file_path}",
            "_hint": "The path may exist on Docker but not on the Windows host. Use push_file to copy from Docker first, or verify the host-side path.",
        }

    cli_exe = kicad_info.get("kicad_cli_exe")
    if not cli_exe or not Path(cli_exe).exists():
        return {
            "success": False,
            "error": "kicad-cli not found",
            "_hint": "Report to the user and ask them to reinstall KiCad — kicad-cli is missing from the installation.",
        }

    # Determine output path
    output_dir = args.get("outputDir", "")
    if not output_dir:
        output_dir = str(pcb_path.parent / f"{export_type}_output")

    # For gerbers, output is a directory; for others, it's a file
    if export_type == "gerbers":
        os.makedirs(output_dir, exist_ok=True)
        output_arg = output_dir
    elif export_type == "step":
        os.makedirs(output_dir, exist_ok=True)
        output_arg = os.path.join(output_dir, pcb_path.stem + ".step")
    else:
        os.makedirs(output_dir, exist_ok=True)
        ext = ".pdf" if export_type == "pdf" else ".svg"
        output_arg = os.path.join(output_dir, pcb_path.stem + ext)

    try:
        cmd = [cli_exe, "pcb", "export", export_type, "--output", output_arg, str(pcb_path)]

        # Add layers for SVG/PDF. kicad-cli `pcb export svg` AND (on KiCad 10)
        # `pcb export pdf` both REQUIRE --layers (they error "At least one layer must
        # be specified" with none), so when the caller didn't specify any we default
        # to a useful full-board set for BOTH. Accept `layers` as either a list
        # (["F.Cu","B.Cu"]) or a comma string ("F.Cu,B.Cu") — a list used to blow up
        # with "expected str … not list" when handed straight to cmd.extend. (winvm 2026-07.)
        layers = args.get("layers", "")
        if isinstance(layers, (list, tuple)):
            layers = ",".join(str(x) for x in layers)
        if not layers and export_type in ("svg", "pdf"):
            layers = "F.Cu,B.Cu,F.Silkscreen,B.Silkscreen,F.Mask,B.Mask,Edge.Cuts"
        if layers and export_type in ("svg", "pdf"):
            cmd.extend(["--layers", layers])

        result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)

        if result.returncode != 0:
            return {"success": False, "error": f"{label} export failed: {result.stderr}"}

        # List output files
        if export_type == "gerbers":
            files = [f.name for f in Path(output_dir).iterdir() if f.is_file()]
        else:
            files = [Path(output_arg).name] if Path(output_arg).exists() else []

        return {
            "success": True,
            "output": f"{label} exported to {output_arg} ({len(files)} file(s))",
            "data": {"output_path": output_arg, "files": files},
        }

    except subprocess.TimeoutExpired:
        return {"success": False, "error": f"{label} export timed out after 120 seconds"}
    except Exception as e:
        return {"success": False, "error": f"{label} export failed: {e}"}


def _run_sch_export(kicad_info: dict, args: dict, export_type: str, label: str) -> dict:
    """Generic schematic export via kicad-cli sch export <type>."""
    if not kicad_info.get("installed"):
        return {
            "success": False,
            "error": "KiCad not installed",
            "_hint": "Report to the user and ask them to install KiCad from https://www.kicad.org/download/, then retry.",
        }

    file_path = args.get("filePath", "")
    if not file_path:
        return {"success": False, "error": "No filePath specified"}

    sch_path = Path(file_path)
    if not sch_path.exists():
        return {
            "success": False,
            "error": f"Schematic file not found: {file_path}",
            "_hint": "The path may exist on Docker but not on the Windows host. Use push_file to copy from Docker first, or verify the host-side path.",
        }

    cli_exe = kicad_info.get("kicad_cli_exe")
    if not cli_exe or not Path(cli_exe).exists():
        return {
            "success": False,
            "error": "kicad-cli not found",
            "_hint": "Report to the user and ask them to reinstall KiCad — kicad-cli is missing from the installation.",
        }

    output_dir = args.get("outputDir", "")
    if not output_dir:
        output_dir = str(sch_path.parent / f"{export_type}_output")

    os.makedirs(output_dir, exist_ok=True)

    ext_map = {"pdf": ".pdf", "bom": ".csv"}
    ext = ext_map.get(export_type, f".{export_type}")
    output_arg = os.path.join(output_dir, sch_path.stem + ext)

    try:
        cmd = [cli_exe, "sch", "export", export_type, "--output", output_arg, str(sch_path)]

        result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)

        if result.returncode != 0:
            return {"success": False, "error": f"{label} export failed: {result.stderr}"}

        exists = Path(output_arg).exists()
        return {
            "success": True,
            "output": f"{label} exported to {output_arg}",
            "data": {
                "output_path": output_arg,
                "files": [Path(output_arg).name] if exists else [],
            },
        }

    except subprocess.TimeoutExpired:
        return {"success": False, "error": f"{label} export timed out after 120 seconds"}
    except Exception as e:
        return {"success": False, "error": f"{label} export failed: {e}"}