"""Cross-platform export tools backed by kicad-cli."""

from __future__ import annotations

import csv
import json
import re
import subprocess as _subprocess
import time as _time
from pathlib import Path
from typing import Any

from mcp.server.fastmcp import Context, FastMCP

from ..config import get_config
from ..discovery import get_cli_capabilities
from ..models.export import (
    ExportBOMInput,
    ExportGerberInput,
    ExportNetlistInput,
    ExportPdfInput,
)
from .aliases import notify_deprecated, register_alias
from .export_support import (
    _ensure_output_dir,
    _get_pcb_file,
    _get_sch_file,
    _run_cli,
    _run_cli_variants,
)
from .metadata import headless_compatible
from .variants import variant_apply_to_kicad_cli_args

# Public compatibility for tests and downstream monkeypatches.  These aliases
# point at Python's process/time modules, so monkeypatching
# kicad_mcp.tools.export.subprocess.run or .time.sleep still affects _run_cli's
# shared module objects.
subprocess = _subprocess
time = _time

DEFAULT_PCB_PDF_LAYERS = ["F.Cu", "Edge.Cuts"]
_WINDOWS_ANCHORED_PATH = re.compile(r"^(?:[a-zA-Z]:|//|\\\\)")
__all__ = [
    "_ensure_output_dir",
    "_get_pcb_file",
    "_get_sch_file",
    "_run_cli",
    "_run_cli_variants",
    "subprocess",
    "time",
]


def _safe_output_filename(raw_name: str, *, default_name: str) -> str:
    name = raw_name.strip() if raw_name else default_name
    if not name:
        raise ValueError("Output file names cannot be empty or whitespace only.")
    if "/" in name or "\\" in name:
        raise ValueError("Output file names cannot contain directory separators or traversal.")
    if _WINDOWS_ANCHORED_PATH.match(name):
        raise ValueError("Output file names must be relative to the export output directory.")
    candidate = Path(name).expanduser()
    if candidate.is_absolute() or candidate.anchor:
        raise ValueError("Output file names must be relative to the export output directory.")
    if len(candidate.parts) != 1 or candidate.name in {"", ".", ".."}:
        raise ValueError("Output file names cannot contain directory separators or traversal.")
    return candidate.name


def _resolve_output_file(subdir: str, raw_name: str, *, default_name: str) -> Path:
    return _ensure_output_dir(subdir) / _safe_output_filename(raw_name, default_name=default_name)


def _human_size(size_bytes: int) -> str:
    """Format a byte count as a human-readable string."""
    size = float(size_bytes)
    for unit in ("B", "KB", "MB", "GB"):
        if size < 1024.0:
            return f"{size:.1f} {unit}"
        size /= 1024.0
    return f"{size:.1f} TB"


def _format_file_list(files: list[Path], heading: str) -> str:
    if not files:
        return f"{heading}\nNo files were produced."
    lines = [heading]
    lines.extend(f"- {file.name}" for file in files[:25])
    if len(files) > 25:
        lines.append(f"... and {len(files) - 25} more files")
    return "\n".join(lines)


def _read_preview(path: Path) -> str:
    cfg = get_config()
    content = path.read_text(encoding="utf-8", errors="ignore")
    if len(content) > cfg.max_text_response_chars:
        return f"{content[: cfg.max_text_response_chars]}\n... [truncated]"
    return content


LOW_LEVEL_EXPORT_NOTICE = (
    "Debug export only: this low-level export does not enforce project_quality_gate(). "
    "Use export_manufacturing_package() for a gated release handoff."
)


def _with_low_level_export_notice(message: str) -> str:
    return f"{LOW_LEVEL_EXPORT_NOTICE}\n\n{message}"


def _active_variant_args(variant_name: str | None = None) -> list[str]:
    try:
        args = variant_apply_to_kicad_cli_args(variant_name)
    except ValueError:
        if variant_name:
            raise
        return []
    if not args:
        return args
    # ``--variant`` was added to ``kicad-cli`` in KiCad 10.  Earlier CLIs (9.x
    # and below) reject it as ``Unknown argument`` and abort the export.  The
    # ``default`` variant is a synthetic no-op baseline that adds no overrides,
    # so suppress it unconditionally; for explicit non-default variants, gate
    # on the local CLI's advertised capability.
    if args == ["--variant", "default"]:
        return []
    try:
        caps = get_cli_capabilities(get_config().kicad_cli)
    except Exception:
        return args
    if not caps.supports_cli_variant:
        raise ValueError(
            f"The detected kicad-cli does not support --variant. "
            f"Cannot apply variant '{args[1]}'. Upgrade to KiCad 10+ "
            f"or run variant_set_active('default') to clear the override."
        )
    return args


async def _report_progress(
    ctx: Context[Any, Any, Any] | None,
    progress: float,
    total: float,
    message: str,
) -> None:
    if ctx is None:
        return
    try:
        await ctx.report_progress(progress, total, message)
    except ValueError:
        return


def register(mcp: FastMCP, *, include_low_level_exports: bool = True) -> None:
    """Register export tools."""

    def _export_gerber(
        output_subdir: str = "gerber",
        layers: list[str] | None = None,
        variant_name: str | None = None,
    ) -> str:
        payload = ExportGerberInput(output_subdir=output_subdir, layers=layers or [])
        pcb_file = _get_pcb_file()
        try:
            out_dir = _ensure_output_dir(payload.output_subdir)
        except ValueError as exc:
            return f"Invalid output path: {exc}"
        caps = get_cli_capabilities(get_config().kicad_cli)

        layer_args = []
        if payload.layers:
            layer_args = ["--layers", ",".join(payload.layers)]
        variant_args = _active_variant_args(variant_name)

        gerber_commands = ["gerbers", "gerber"]
        if caps.gerber_command not in gerber_commands:
            gerber_commands.append(caps.gerber_command)
        variants: list[list[str]] = []
        for gerber_command in gerber_commands:
            variants.extend(
                [
                    [
                        "pcb",
                        "export",
                        gerber_command,
                        *variant_args,
                        "--output",
                        str(out_dir),
                        *layer_args,
                        str(pcb_file),
                    ],
                    [
                        "pcb",
                        "export",
                        gerber_command,
                        *variant_args,
                        "--input",
                        str(pcb_file),
                        "--output",
                        str(out_dir),
                        *layer_args,
                    ],
                ]
            )
        code, _, stderr = _run_cli_variants(variants)
        if code != 0:
            return f"Gerber export failed: {stderr or 'unknown error'}"

        files = sorted(out_dir.glob("*.gbr")) + sorted(out_dir.glob("*.g*"))
        return _format_file_list(files, f"Gerber export completed in {out_dir}:")

    @headless_compatible
    async def export_gerber(
        output_subdir: str = "gerber",
        layers: list[str] | None = None,
        ctx: Context[Any, Any, Any] | None = None,
    ) -> str:
        """Export Gerber manufacturing files."""
        import anyio

        await _report_progress(ctx, 5, 100, "Starting Gerber export...")
        result = await anyio.to_thread.run_sync(
            lambda: _with_low_level_export_notice(_export_gerber(output_subdir, layers))
        )
        await _report_progress(ctx, 100, 100, "Gerber export complete.")
        return result

    def _export_drill(output_subdir: str = "gerber", variant_name: str | None = None) -> str:
        pcb_file = _get_pcb_file()
        try:
            out_dir = _ensure_output_dir(output_subdir)
        except ValueError as exc:
            return f"Invalid output path: {exc}"
        caps = get_cli_capabilities(get_config().kicad_cli)
        variant_args = _active_variant_args(variant_name)
        code, _, stderr = _run_cli_variants(
            [
                [
                    "pcb",
                    "export",
                    caps.drill_command,
                    *variant_args,
                    "--output",
                    str(out_dir),
                    str(pcb_file),
                ],
                [
                    "pcb",
                    "export",
                    caps.drill_command,
                    *variant_args,
                    "--input",
                    str(pcb_file),
                    "--output",
                    str(out_dir),
                ],
            ]
        )
        if code != 0:
            return f"Drill export failed: {stderr or 'unknown error'}"
        files = sorted(out_dir.glob("*.drl")) + sorted(out_dir.glob("*.xnc"))
        return _format_file_list(files, f"Drill export completed in {out_dir}:")

    @headless_compatible
    def export_drill(output_subdir: str = "gerber") -> str:
        """Export drill files."""
        return _with_low_level_export_notice(_export_drill(output_subdir))

    def _export_bom(format: str = "csv", variant_name: str | None = None) -> str:
        payload = ExportBOMInput(format=format)
        sch_file = _get_sch_file()
        out_dir = _ensure_output_dir()
        suffix = "csv" if payload.format == "csv" else "xml"
        out_file = out_dir / f"bom.{suffix}"
        if payload.format == "csv":
            try:
                from .library import _schematic_component_rows
                from .schematic import project_schematic_files

                schematic_files = project_schematic_files()
                if len(schematic_files) > 1:
                    rows = _schematic_component_rows()
                    with out_file.open("w", newline="", encoding="utf-8") as handle:
                        writer = csv.DictWriter(
                            handle,
                            fieldnames=[
                                "reference",
                                "value",
                                "footprint",
                                "lib_id",
                                "lcsc",
                                "mpn",
                                "manufacturer",
                                "populate",
                            ],
                        )
                        writer.writeheader()
                        writer.writerows(rows)
                    return (
                        f"BOM exported to {out_file}\n"
                        f"Consolidated {len(rows)} reference(s) from "
                        f"{len(schematic_files)} schematic files.\n\n"
                        f"{_read_preview(out_file)}"
                    )
            except (OSError, ValueError, RuntimeError) as exc:
                return f"BOM export failed: {exc}"
        variant_args = _active_variant_args(variant_name)
        code, _, stderr = _run_cli_variants(
            [
                [
                    "sch",
                    "export",
                    "bom",
                    *variant_args,
                    "--output",
                    str(out_file),
                    "--format-preset",
                    "CSV",
                    str(sch_file),
                ],
                [
                    "sch",
                    "export",
                    "bom",
                    *variant_args,
                    "--input",
                    str(sch_file),
                    "--output",
                    str(out_file),
                    "--format-preset",
                    "CSV",
                ],
                ["sch", "export", "python-bom", "--output", str(out_file), str(sch_file)],
            ]
        )
        if code != 0 and not out_file.exists():
            return f"BOM export failed: {stderr or 'unknown error'}"
        return f"BOM exported to {out_file}\n\n{_read_preview(out_file)}"

    @headless_compatible
    def export_bom(format: str = "csv") -> str:
        """Export a bill of materials."""
        return _with_low_level_export_notice(_export_bom(format))

    def _export_netlist(format: str = "kicad") -> str:
        payload = ExportNetlistInput(format=format)
        sch_file = _get_sch_file()
        out_dir = _ensure_output_dir()
        extension_map = {"kicad": "net", "spice": "cir", "cadstar": "frp", "orcadpcb2": "net"}
        cli_format_map = {
            "kicad": "kicadsexpr",
            "spice": "spice",
            "cadstar": "cadstar",
            "orcadpcb2": "orcadpcb2",
        }
        out_file = out_dir / f"netlist.{extension_map[payload.format]}"
        variant_args = _active_variant_args()
        code, _, stderr = _run_cli_variants(
            [
                [
                    "sch",
                    "export",
                    "netlist",
                    *variant_args,
                    "--format",
                    cli_format_map[payload.format],
                    "--output",
                    str(out_file),
                    str(sch_file),
                ],
            ]
        )
        if code != 0:
            return f"Netlist export failed: {stderr or 'unknown error'}"
        return f"Netlist exported to {out_file}"

    @headless_compatible
    def export_netlist(format: str = "kicad") -> str:
        """Export a KiCad schematic netlist."""
        return _with_low_level_export_notice(_export_netlist(format))

    @headless_compatible
    def export_spice_netlist() -> str:
        """Export a SPICE netlist."""
        return _with_low_level_export_notice(_export_netlist("spice"))

    def _export_pcb_pdf(layers: list[str] | None = None) -> str:
        payload = ExportPdfInput(layers=layers or [])
        pcb_file = _get_pcb_file()
        out_dir = _ensure_output_dir()
        out_file = out_dir / "board.pdf"
        layers_arg = ",".join(payload.layers or DEFAULT_PCB_PDF_LAYERS)
        variant_args = _active_variant_args()
        code, _, stderr = _run_cli_variants(
            [
                [
                    "pcb",
                    "export",
                    "pdf",
                    *variant_args,
                    "--output",
                    str(out_file),
                    "--layers",
                    layers_arg,
                    str(pcb_file),
                ],
                [
                    "pcb",
                    "export",
                    "pdf",
                    *variant_args,
                    "--input",
                    str(pcb_file),
                    "--output",
                    str(out_file),
                    "--layers",
                    layers_arg,
                ],
            ]
        )
        if code != 0:
            return f"PCB PDF export failed: {stderr or 'unknown error'}"
        return f"PCB PDF exported to {out_file}"

    @headless_compatible
    def export_pcb_pdf(layers: list[str] | None = None) -> str:
        """Export the PCB to PDF."""
        return _with_low_level_export_notice(_export_pcb_pdf(layers))

    def _export_3d_pdf(output_path: str = "") -> str:
        pcb_file = _get_pcb_file()
        caps = get_cli_capabilities(get_config().kicad_cli)
        if not caps.supports_3d_pdf:
            return "3D PDF export is not supported by the detected KiCad CLI."

        try:
            out_file = _resolve_output_file("pdf", output_path, default_name="board-3d.pdf")
        except ValueError as exc:
            return f"Invalid output path: {exc}"
        variant_args = _active_variant_args()
        code, _, stderr = _run_cli_variants(
            [
                [
                    "pcb",
                    "export",
                    "3d-pdf",
                    *variant_args,
                    "--output",
                    str(out_file),
                    str(pcb_file),
                ],
                [
                    "pcb",
                    "export",
                    "3d-pdf",
                    *variant_args,
                    "--input",
                    str(pcb_file),
                    "--output",
                    str(out_file),
                ],
            ]
        )
        if code != 0:
            return f"3D PDF export failed: {stderr or 'unknown error'}"
        return f"3D PDF exported to {out_file}"

    @headless_compatible
    def pcb_export_3d_pdf(output_path: str = "") -> str:
        """Export the PCB to a 3D PDF.

        Parameters
        ----------
        output_path : str
            Output file name (relative to the export output directory).
        """
        return _with_low_level_export_notice(_export_3d_pdf(output_path))

    def _export_sch_pdf() -> str:
        sch_file = _get_sch_file()
        out_dir = _ensure_output_dir()
        out_file = out_dir / "schematic.pdf"
        variant_args = _active_variant_args()
        code, stdout, stderr = _run_cli_variants(
            [
                ["sch", "export", "pdf", *variant_args, "--output", str(out_file), str(sch_file)],
                [
                    "sch",
                    "export",
                    "pdf",
                    *variant_args,
                    "--input",
                    str(sch_file),
                    "--output",
                    str(out_file),
                ],
            ]
        )
        if code != 0:
            return f"Schematic PDF export failed: {stderr or stdout or 'unknown error'}"
        return f"Schematic PDF exported to {out_file}"

    @headless_compatible
    def export_sch_pdf() -> str:
        """Export the schematic to PDF."""
        return _with_low_level_export_notice(_export_sch_pdf())

    @headless_compatible
    def sch_export_svg(
        output_dir: str = "",
        pages: str = "",
        variant_name: str = "",
        theme: str = "",
        black_and_white: bool = False,
        exclude_drawing_sheet: bool = False,
        draw_hop_over: bool = False,
        no_background_color: bool = False,
    ) -> str:
        """Export schematic to SVG format."""
        sch_file = _get_sch_file()
        try:
            out_dir = (
                _ensure_output_dir("svg")
                if not output_dir
                else _resolve_output_file("svg", output_dir, default_name="")
            )
        except ValueError as exc:
            return f"Invalid output path: {exc}"

        cmd = ["sch", "export", "svg"]
        if pages:
            cmd.extend(["--pages", pages])
        if variant_name:
            cmd.extend(["--variant", variant_name])
        if theme:
            cmd.extend(["--theme", theme])
        if black_and_white:
            cmd.append("--black-and-white")
        if exclude_drawing_sheet:
            cmd.append("--exclude-drawing-sheet")
        if draw_hop_over:
            cmd.append("--draw-hop-over")
        if no_background_color:
            cmd.append("--no-background-color")
        cmd.extend(["--output", str(out_dir)])
        cmd.append(str(sch_file))

        code, stdout, stderr = _run_cli(*cmd)
        if code != 0:
            return f"Schematic SVG export failed: {stderr or stdout or 'unknown error'}"
        files = sorted(out_dir.glob("*.svg")) if out_dir.is_dir() else []
        return _format_file_list(files, f"Schematic SVG export completed in {out_dir}:")

    @headless_compatible
    def export_sch_svg() -> str:
        """Export the schematic to SVG when supported."""
        return _with_low_level_export_notice(sch_export_svg())

    @headless_compatible
    def sch_export_dxf(
        output_dir: str = "",
        pages: str = "",
        variant_name: str = "",
        theme: str = "",
        black_and_white: bool = False,
        exclude_drawing_sheet: bool = False,
        draw_hop_over: bool = False,
    ) -> str:
        """Export schematic to DXF format."""
        sch_file = _get_sch_file()
        try:
            out_dir = (
                _ensure_output_dir("dxf")
                if not output_dir
                else _resolve_output_file("dxf", output_dir, default_name="")
            )
        except ValueError as exc:
            return f"Invalid output path: {exc}"

        cmd = ["sch", "export", "dxf"]
        if pages:
            cmd.extend(["--pages", pages])
        if variant_name:
            cmd.extend(["--variant", variant_name])
        if theme:
            cmd.extend(["--theme", theme])
        if black_and_white:
            cmd.append("--black-and-white")
        if exclude_drawing_sheet:
            cmd.append("--exclude-drawing-sheet")
        if draw_hop_over:
            cmd.append("--draw-hop-over")
        cmd.extend(["--output", str(out_dir)])
        cmd.append(str(sch_file))

        code, stdout, stderr = _run_cli(*cmd)
        if code != 0:
            return f"Schematic DXF export failed: {stderr or stdout or 'unknown error'}"
        files = sorted(out_dir.glob("*.dxf")) if out_dir.is_dir() else []
        return _format_file_list(files, f"Schematic DXF export completed in {out_dir}:")

    @headless_compatible
    def export_sch_dxf() -> str:
        """Export the schematic to DXF when supported."""
        return _with_low_level_export_notice(sch_export_dxf())

    @headless_compatible
    def sch_export_ps(
        output_dir: str = "",
        pages: str = "",
        variant_name: str = "",
        theme: str = "",
        black_and_white: bool = False,
        exclude_drawing_sheet: bool = False,
        draw_hop_over: bool = False,
        no_background_color: bool = False,
    ) -> str:
        """Export schematic to PostScript format."""
        sch_file = _get_sch_file()
        try:
            out_dir = (
                _ensure_output_dir("ps")
                if not output_dir
                else _resolve_output_file("ps", output_dir, default_name="")
            )
        except ValueError as exc:
            return f"Invalid output path: {exc}"

        cmd = ["sch", "export", "ps"]
        if pages:
            cmd.extend(["--pages", pages])
        if variant_name:
            cmd.extend(["--variant", variant_name])
        if theme:
            cmd.extend(["--theme", theme])
        if black_and_white:
            cmd.append("--black-and-white")
        if exclude_drawing_sheet:
            cmd.append("--exclude-drawing-sheet")
        if draw_hop_over:
            cmd.append("--draw-hop-over")
        if no_background_color:
            cmd.append("--no-background-color")
        cmd.extend(["--output", str(out_dir)])
        cmd.append(str(sch_file))

        code, stdout, stderr = _run_cli(*cmd)
        if code != 0:
            return f"Schematic PostScript export failed: {stderr or stdout or 'unknown error'}"
        files = sorted(out_dir.glob("*.ps")) if out_dir.is_dir() else []
        return _format_file_list(files, f"Schematic PostScript export completed in {out_dir}:")

    @headless_compatible
    def sch_export_python_bom(output_file: str = "") -> str:
        """Export the schematic legacy XML BOM using python-bom."""
        sch_file = _get_sch_file()
        try:
            out_file = _resolve_output_file("bom", output_file, default_name="bom.xml")
        except ValueError as exc:
            return f"Invalid output path: {exc}"

        cmd = ["sch", "export", "python-bom", "--output", str(out_file), str(sch_file)]
        code, stdout, stderr = _run_cli(*cmd)
        if code != 0:
            return f"Legacy Python BOM export failed: {stderr or stdout or 'unknown error'}"
        return f"Legacy Python BOM exported to {out_file}"

    @headless_compatible
    def export_sch_python_bom() -> str:
        """Export the schematic BOM using KiCad's Python BOM engine."""
        return _with_low_level_export_notice(sch_export_python_bom())

    def _export_3d_model(
        cli_command: str,
        output_path: str,
        *,
        supported: bool,
        default_name: str,
        label: str,
        # 3D options:
        force: bool = False,
        no_unspecified: bool = False,
        no_dnp: bool = False,
        variant_name: str | None = None,
        grid_origin: bool = False,
        drill_origin: bool = False,
        subst_models: bool = False,
        board_only: bool = False,
        cut_vias_in_body: bool = False,
        no_board_body: bool = False,
        no_components: bool = False,
        component_filter: str = "",
        include_tracks: bool = False,
        include_pads: bool = False,
        include_zones: bool = False,
        include_inner_copper: bool = False,
        include_silkscreen: bool = False,
        include_soldermask: bool = False,
        fuse_shapes: bool = False,
        fill_all_vias: bool = False,
        no_extra_pad_thickness: bool = False,
        min_distance: str = "",
        net_filter: str = "",
        user_origin: str = "",
        # VRML specific:
        units: str = "",
        models_dir: str = "",
        models_relative: bool = False,
    ) -> str:
        pcb_file = _get_pcb_file()
        if not supported:
            return f"{label} export is not supported by the detected KiCad CLI."

        try:
            out_file = _resolve_output_file("3d", output_path, default_name=default_name)
        except ValueError as exc:
            return f"Invalid output path: {exc}"

        cmd = ["pcb", "export", cli_command]
        if force:
            cmd.append("--force")
        if no_unspecified:
            cmd.append("--no-unspecified")
        if no_dnp:
            cmd.append("--no-dnp")

        variant_args = _active_variant_args(variant_name)
        cmd.extend(variant_args)

        if grid_origin:
            cmd.append("--grid-origin")
        if drill_origin:
            cmd.append("--drill-origin")
        if subst_models:
            cmd.append("--subst-models")
        if board_only:
            cmd.append("--board-only")
        if cut_vias_in_body:
            cmd.append("--cut-vias-in-body")
        if no_board_body:
            cmd.append("--no-board-body")
        if no_components:
            cmd.append("--no-components")
        if component_filter:
            cmd.extend(["--component-filter", component_filter])
        if include_tracks:
            cmd.append("--include-tracks")
        if include_pads:
            cmd.append("--include-pads")
        if include_zones:
            cmd.append("--include-zones")
        if include_inner_copper:
            cmd.append("--include-inner-copper")
        if include_silkscreen:
            cmd.append("--include-silkscreen")
        if include_soldermask:
            cmd.append("--include-soldermask")
        if fuse_shapes:
            cmd.append("--fuse-shapes")
        if fill_all_vias:
            cmd.append("--fill-all-vias")
        if no_extra_pad_thickness:
            cmd.append("--no-extra-pad-thickness")
        if min_distance:
            cmd.extend(["--min-distance", min_distance])
        if net_filter:
            cmd.extend(["--net-filter", net_filter])
        if user_origin:
            cmd.extend(["--user-origin", user_origin])

        # VRML specific:
        if units:
            cmd.extend(["--units", units])
        if models_dir:
            cmd.extend(["--models-dir", models_dir])
        if models_relative:
            cmd.append("--models-relative")

        cmd.extend(["--output", str(out_file)])
        cmd.append(str(pcb_file))

        code, stdout, stderr = _run_cli(*cmd)
        if code != 0:
            return f"{label} export failed: {stderr or stdout or 'unknown error'}"
        return f"{label} model exported to {out_file}"

    @headless_compatible
    def pcb_export_brep(
        output_file: str = "",
        variant_name: str | None = None,
        **kwargs: Any,  # noqa: ANN401
    ) -> str:
        """Export solid model to BREP format."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _export_3d_model(
            "brep",
            output_file,
            supported=caps.supports_brep,
            default_name="board.brep",
            label="BREP",
            variant_name=variant_name,
            **kwargs,
        )

    @headless_compatible
    def export_brep(output_path: str = "") -> str:
        """Export BREP format for the active board."""
        return _with_low_level_export_notice(pcb_export_brep(output_file=output_path))

    @headless_compatible
    def pcb_export_glb(
        output_file: str = "",
        variant_name: str | None = None,
        **kwargs: Any,  # noqa: ANN401
    ) -> str:
        """Export solid model to GLB format."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _export_3d_model(
            "glb",
            output_file,
            supported=caps.supports_glb,
            default_name="board.glb",
            label="GLB",
            variant_name=variant_name,
            **kwargs,
        )

    @headless_compatible
    def export_glb(output_path: str = "") -> str:
        """Export GLB format for the active board."""
        return _with_low_level_export_notice(pcb_export_glb(output_file=output_path))

    @headless_compatible
    def pcb_export_gencad(output_file: str = "", **kwargs: Any) -> str:  # noqa: ANN401
        """Export board to GenCAD format."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _export_3d_model(
            "gencad",
            output_file,
            supported=caps.supports_gencad,
            default_name="board.gencad",
            label="GenCAD",
            **kwargs,
        )

    @headless_compatible
    def export_gencad(output_path: str = "") -> str:
        """Export GenCAD format for the active board."""
        return _with_low_level_export_notice(pcb_export_gencad(output_file=output_path))

    @headless_compatible
    def pcb_export_ipcd356(output_file: str = "") -> str:
        """Export netlist to IPC-D-356 format."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _export_3d_model(
            "ipcd356",
            output_file,
            supported=caps.supports_ipc_d356,
            default_name="board.d356",
            label="IPC-D-356",
        )

    @headless_compatible
    def export_ipc_d356(output_path: str = "") -> str:
        """Export IPC-D-356 format for the active board."""
        return _with_low_level_export_notice(pcb_export_ipcd356(output_file=output_path))

    @headless_compatible
    def pcb_export_ply(
        output_file: str = "",
        variant_name: str | None = None,
        **kwargs: Any,  # noqa: ANN401
    ) -> str:
        """Export solid model to PLY format."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _export_3d_model(
            "ply",
            output_file,
            supported=caps.supports_ply,
            default_name="board.ply",
            label="PLY",
            variant_name=variant_name,
            **kwargs,
        )

    @headless_compatible
    def export_ply(output_path: str = "") -> str:
        """Export PLY format for the active board."""
        return _with_low_level_export_notice(pcb_export_ply(output_file=output_path))

    @headless_compatible
    def pcb_export_stl(
        output_file: str = "",
        variant_name: str | None = None,
        **kwargs: Any,  # noqa: ANN401
    ) -> str:
        """Export solid model to STL format."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _export_3d_model(
            "stl",
            output_file,
            supported=caps.supports_stl,
            default_name="board.stl",
            label="STL",
            variant_name=variant_name,
            **kwargs,
        )

    @headless_compatible
    def export_stl(output_path: str = "") -> str:
        """Export STL format for the active board."""
        return _with_low_level_export_notice(pcb_export_stl(output_file=output_path))

    @headless_compatible
    def pcb_export_u3d(
        output_file: str = "",
        variant_name: str | None = None,
        **kwargs: Any,  # noqa: ANN401
    ) -> str:
        """Export solid model to U3D format."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _export_3d_model(
            "u3d",
            output_file,
            supported=caps.supports_u3d,
            default_name="board.u3d",
            label="U3D",
            variant_name=variant_name,
            **kwargs,
        )

    @headless_compatible
    def export_u3d(output_path: str = "") -> str:
        """Export U3D format for the active board."""
        return _with_low_level_export_notice(pcb_export_u3d(output_file=output_path))

    @headless_compatible
    def pcb_export_vrml(
        output_file: str = "",
        units: str = "in",
        models_dir: str = "",
        models_relative: bool = False,
        variant_name: str | None = None,
        **kwargs: Any,  # noqa: ANN401,
    ) -> str:
        """Export solid model to VRML format."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _export_3d_model(
            "vrml",
            output_file,
            supported=caps.supports_vrml,
            default_name="board.wrl",
            label="VRML",
            units=units,
            models_dir=models_dir,
            models_relative=models_relative,
            variant_name=variant_name,
            **kwargs,
        )

    @headless_compatible
    def export_vrml(output_path: str = "") -> str:
        """Export VRML format for the active board."""
        return _with_low_level_export_notice(pcb_export_vrml(output_file=output_path))

    @headless_compatible
    def pcb_export_ps(output_file: str = "", variant_name: str | None = None, **kwargs: Any) -> str:  # noqa: ANN401
        """Export PCB rendering to PostScript format."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _export_3d_model(
            "ps",
            output_file,
            supported=caps.supports_ps,
            default_name="board.ps",
            label="PostScript",
            variant_name=variant_name,
            **kwargs,
        )

    @headless_compatible
    def export_ps(output_path: str = "") -> str:
        """Export PostScript format for the active board."""
        return _with_low_level_export_notice(pcb_export_ps(output_file=output_path))

    @headless_compatible
    def export_3d_step() -> str:
        """Deprecated alias of ``export_step``; exports a STEP model for the active board.

        Retained for backward compatibility. Prefer ``export_step``, which accepts an
        optional output path. This alias logs a one-time deprecation warning.
        """
        notify_deprecated("export_3d_step")
        return export_step()

    @headless_compatible
    def export_step(output_path: str = "") -> str:
        """Export a STEP model for the active board."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _with_low_level_export_notice(
            _export_3d_model(
                "step",
                output_path,
                supported=caps.supports_step,
                default_name="board.step",
                label="STEP",
            )
        )

    @headless_compatible
    def export_stepz(output_path: str = "") -> str:
        """Export a STEPZ model for the active board."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _with_low_level_export_notice(
            _export_3d_model(
                "stpz",
                output_path,
                supported=caps.supports_stepz,
                default_name="board.stepz",
                label="STEPZ",
            )
        )

    @headless_compatible
    def export_xao(output_path: str = "") -> str:
        """Export an XAO model for the active board."""
        caps = get_cli_capabilities(get_config().kicad_cli)
        return _with_low_level_export_notice(
            _export_3d_model(
                "xao",
                output_path,
                supported=caps.supports_xao,
                default_name="board.xao",
                label="XAO",
            )
        )

    def _export_svg(layer: str = "F.Cu") -> str:
        pcb_file = _get_pcb_file()
        caps = get_cli_capabilities(get_config().kicad_cli)
        if not caps.supports_svg:
            return "SVG export is not supported by the detected KiCad CLI."

        out_dir = _ensure_output_dir("svg")
        variant_args = _active_variant_args()
        code, _, stderr = _run_cli_variants(
            [
                [
                    "pcb",
                    "export",
                    "svg",
                    *variant_args,
                    "--mode-multi",
                    "--layers",
                    layer,
                    "--output",
                    str(out_dir),
                    str(pcb_file),
                ],
            ]
        )
        if code != 0:
            return f"SVG export failed: {stderr or 'unknown error'}"
        files = sorted(out_dir.glob("*.svg"))
        return _format_file_list(files, f"SVG export completed in {out_dir}:")

    @headless_compatible
    def export_svg(layer: str = "F.Cu") -> str:
        """Export a board layer to SVG when supported."""
        return _with_low_level_export_notice(_export_svg(layer))

    def _export_dxf(layer: str = "Edge.Cuts") -> str:
        pcb_file = _get_pcb_file()
        caps = get_cli_capabilities(get_config().kicad_cli)
        if not caps.supports_dxf:
            return "DXF export is not supported by the detected KiCad CLI."

        out_dir = _ensure_output_dir("dxf")
        variant_args = _active_variant_args()
        code, _, stderr = _run_cli_variants(
            [
                [
                    "pcb",
                    "export",
                    "dxf",
                    *variant_args,
                    "--layers",
                    layer,
                    "--output",
                    str(out_dir),
                    str(pcb_file),
                ],
                [
                    "pcb",
                    "export",
                    "dxf",
                    *variant_args,
                    "--input",
                    str(pcb_file),
                    "--layers",
                    layer,
                    "--output",
                    str(out_dir),
                ],
            ]
        )
        if code != 0:
            return f"DXF export failed: {stderr or 'unknown error'}"
        files = sorted(out_dir.glob("*.dxf"))
        return _format_file_list(files, f"DXF export completed in {out_dir}:")

    @headless_compatible
    def export_dxf(layer: str = "Edge.Cuts") -> str:
        """Export a board layer to DXF when supported."""
        return _with_low_level_export_notice(_export_dxf(layer))

    def _export_3d_render(
        output_file: str = "render.png",
        side: str = "top",
        zoom: float = 1.0,
        width: int | None = None,
        height: int | None = None,
        quality: float | None = None,
        preset: str | None = None,
        use_board_stackup_colors: bool = False,
        floor: bool = True,
        perspective: bool = True,
        pan_x: float | None = None,
        pan_y: float | None = None,
        rotate_x: float | None = None,
        rotate_y: float | None = None,
        rotate_z: float | None = None,
        light_top: float | None = None,
        light_bottom: float | None = None,
        light_side: float | None = None,
        light_camera: float | None = None,
        light_side_elevation: float | None = None,
    ) -> str:
        pcb_file = _get_pcb_file()
        caps = get_cli_capabilities(get_config().kicad_cli)
        if not caps.supports_render:
            return "3D render export is not supported by the detected KiCad CLI."

        try:
            out_file = _resolve_output_file("3d", output_file, default_name="render.png")
        except ValueError as exc:
            return f"Invalid output path: {exc}"

        args: list[str] = ["pcb", "render", "--output", str(out_file)]
        args.extend(["--side", side])
        args.extend(["--zoom", str(zoom)])
        if width is not None:
            args.extend(["--width", str(width)])
        if height is not None:
            args.extend(["--height", str(height)])
        if quality is not None:
            args.extend(["--quality", str(quality)])
        if preset:
            args.extend(["--preset", preset])
        if use_board_stackup_colors:
            args.append("--use-board-stackup-colors")
        if not floor:
            args.append("--no-floor")
        if not perspective:
            args.append("--orthographic")
        if pan_x is not None or pan_y is not None:
            px = pan_x if pan_x is not None else 0.0
            py = pan_y if pan_y is not None else 0.0
            args.extend(["--pan", f"{px},{py}"])
        if any(v is not None for v in (rotate_x, rotate_y, rotate_z)):
            rx = rotate_x or 0
            ry = rotate_y or 0
            rz = rotate_z or 0
            args.extend(["--rotate", f"{rx},{ry},{rz}"])
        if light_top is not None:
            args.extend(["--light-top", str(light_top)])
        if light_bottom is not None:
            args.extend(["--light-bottom", str(light_bottom)])
        if light_side is not None:
            args.extend(["--light-side", str(light_side)])
        if light_camera is not None:
            args.extend(["--light-camera", str(light_camera)])
        if light_side_elevation is not None:
            args.extend(["--light-side-elevation", str(light_side_elevation)])

        variant_args = _active_variant_args()
        args.extend(variant_args)
        args.append(str(pcb_file))

        code, _, stderr = _run_cli_variants([args])
        if code != 0:
            return f"3D render failed: {stderr or 'unknown error'}"
        if out_file.exists():
            file_size = _human_size(out_file.stat().st_size)
            return f"Rendered board image exported to {out_file} ({file_size})"
        return f"Rendered board image exported to {out_file}"

    @headless_compatible
    def export_3d_render(
        output_file: str = "render.png",
        side: str = "top",
        zoom: float = 1.0,
        width: int | None = None,
        height: int | None = None,
        quality: float | None = None,
        preset: str | None = None,
        use_board_stackup_colors: bool = False,
        floor: bool = True,
        perspective: bool = True,
        pan_x: float | None = None,
        pan_y: float | None = None,
        rotate_x: float | None = None,
        rotate_y: float | None = None,
        rotate_z: float | None = None,
        light_top: float | None = None,
        light_bottom: float | None = None,
        light_side: float | None = None,
        light_camera: float | None = None,
        light_side_elevation: float | None = None,
    ) -> str:
        """Render a 3D view of the active PCB board to a PNG image.

        Parameters
        ----------
        output_file : str
            Output file name (PNG or JPG). Defaults to ``render.png``.
        side : str
            View direction: ``top``, ``bottom``, ``front``, ``back``, ``left``, ``right``.
        zoom : float
            Camera zoom factor (0.05–20.0).
        width, height : int | None
            Output image dimensions in pixels.
        quality : float | None
            Rendering quality (0.0–1.0).
        preset : str | None
            Render preset name (e.g. ``photo``, ``standard``).
        use_board_stackup_colors : bool
            Use the board stackup-defined colors.
        floor : bool
            Show the reflective floor. Default True.
        perspective : bool
            Perspective projection. Set False for orthographic.
        pan_x, pan_y : float | None
            Camera pan offset in mm.
        rotate_x, rotate_y, rotate_z : float | None
            Camera rotation in degrees.
        light_top, light_bottom, light_side, light_camera : float | None
            Light intensity for each direction (0.0–1.0).
        light_side_elevation : float | None
            Side light elevation angle in degrees.
        """
        return _with_low_level_export_notice(
            _export_3d_render(
                output_file=output_file,
                side=side,
                zoom=zoom,
                width=width,
                height=height,
                quality=quality,
                preset=preset,
                use_board_stackup_colors=use_board_stackup_colors,
                floor=floor,
                perspective=perspective,
                pan_x=pan_x,
                pan_y=pan_y,
                rotate_x=rotate_x,
                rotate_y=rotate_y,
                rotate_z=rotate_z,
                light_top=light_top,
                light_bottom=light_bottom,
                light_side=light_side,
                light_camera=light_camera,
                light_side_elevation=light_side_elevation,
            )
        )

    def _export_pick_and_place(
        format: str = "csv",
        variant_name: str | None = None,
    ) -> str:
        pcb_file = _get_pcb_file()
        caps = get_cli_capabilities(get_config().kicad_cli)
        pos_cmd = caps.position_command

        out_dir = _ensure_output_dir("pos")
        variant_args = _active_variant_args(variant_name)
        code, _, stderr = _run_cli_variants(
            [
                [
                    "pcb",
                    "export",
                    pos_cmd,
                    *variant_args,
                    "--format",
                    format,
                    "--output",
                    str(out_dir),
                    str(pcb_file),
                ],
                [
                    "pcb",
                    "export",
                    pos_cmd,
                    *variant_args,
                    "--format",
                    format,
                    "--input",
                    str(pcb_file),
                    "--output",
                    str(out_dir),
                ],
            ]
        )
        if code != 0:
            return f"Pick and place export failed: {stderr or 'unknown error'}"
        files = sorted(out_dir.iterdir()) if out_dir.exists() else []
        return _format_file_list(files, f"Pick and place data exported to {out_dir}:")

    @headless_compatible
    def export_pick_and_place(format: str = "csv", variant: str | None = None) -> str:
        """Export pick and place (CPL) data for the active PCB.

        Parameters
        ----------
        format : str
            Output format (e.g. ``csv``, ``ascii``).
        variant : str | None
            Optional design variant name. When set, exports variant-specific
            pick-and-place data (component population, value, footprint
            overrides). Uses the active variant when omitted.
        """
        return _with_low_level_export_notice(
            _export_pick_and_place(format=format, variant_name=variant)
        )

    def _export_ipc2581(variant_name: str | None = None) -> str:
        pcb_file = _get_pcb_file()
        caps = get_cli_capabilities(get_config().kicad_cli)
        if not caps.supports_ipc2581:
            return "IPC-2581 export is not supported by the detected KiCad CLI."

        try:
            out_file = _resolve_output_file("ipc2581", "", default_name="board.ipc2581")
        except ValueError as exc:
            return f"Invalid output path: {exc}"
        variant_args = _active_variant_args(variant_name)
        code, _, stderr = _run_cli_variants(
            [
                [
                    "pcb",
                    "export",
                    "ipc2581",
                    *variant_args,
                    "--output",
                    str(out_file),
                    str(pcb_file),
                ],
                [
                    "pcb",
                    "export",
                    "ipc2581",
                    *variant_args,
                    "--input",
                    str(pcb_file),
                    "--output",
                    str(out_file),
                ],
            ]
        )
        if code != 0:
            return f"IPC-2581 export failed: {stderr or 'unknown error'}"
        return f"IPC-2581 exported to {out_file}"

    @headless_compatible
    def export_ipc2581() -> str:
        """Export the active PCB to IPC-2581 format."""
        return _with_low_level_export_notice(_export_ipc2581())

    def _export_odb(variant_name: str | None = None) -> str:
        pcb_file = _get_pcb_file()
        caps = get_cli_capabilities(get_config().kicad_cli)
        if not caps.supports_odb_export:
            return "ODB++ export is not supported by the detected KiCad CLI."

        try:
            out_file = _resolve_output_file("odb", "", default_name="board.odb")
        except ValueError as exc:
            return f"Invalid output path: {exc}"
        variant_args = _active_variant_args(variant_name)
        code, _, stderr = _run_cli_variants(
            [
                [
                    "pcb",
                    "export",
                    "odb",
                    *variant_args,
                    "--compression",
                    "--output",
                    str(out_file),
                    str(pcb_file),
                ],
                [
                    "pcb",
                    "export",
                    "odb",
                    *variant_args,
                    "--compression",
                    "--input",
                    str(pcb_file),
                    "--output",
                    str(out_file),
                ],
            ]
        )
        if code != 0:
            return f"ODB++ export failed: {stderr or 'unknown error'}"
        return f"ODB++ exported to {out_file}"

    @headless_compatible
    def export_odb() -> str:
        """Export the active PCB to ODB++ format."""
        return _with_low_level_export_notice(_export_odb())

    @headless_compatible
    def get_board_stats() -> str:
        """Export board statistics and return a readable preview."""
        pcb_file = _get_pcb_file()
        out_file = _ensure_output_dir() / "board_stats.txt"
        code, stdout, stderr = _run_cli_variants(
            [
                ["pcb", "export", "stats", "--output", str(out_file), str(pcb_file)],
                ["pcb", "export", "stats", "--input", str(pcb_file), "--output", str(out_file)],
            ]
        )
        if out_file.exists():
            return _read_preview(out_file)
        if code != 0:
            return f"Board stats export failed: {stderr or 'unknown error'}"
        return stdout or "Board statistics were generated without a text report."

    @headless_compatible
    def pcb_export_stats(output_name: str | None = None) -> str:
        """Export board statistics (net count, component count, layer count, etc.)
        via the KiCad CLI ``pcb export stats`` command.

        Parameters
        ----------
        output_name : str | None
            Optional output file name (saved under the project output directory).
            Defaults to ``board_stats.json``.
        """
        pcb_file = _get_pcb_file()
        out_dir = _ensure_output_dir("stats")
        out_file = out_dir / (output_name.strip() if output_name else "board_stats.json")
        if "/" in str(out_file.relative_to(out_dir)) or "\\" in str(out_file.relative_to(out_dir)):
            raise ValueError("Output name must be a single file name, not a path.")

        code, stdout, stderr = _run_cli_variants(
            [
                [
                    "pcb",
                    "export",
                    "stats",
                    "--output",
                    str(out_file),
                    str(pcb_file),
                ],
                [
                    "pcb",
                    "export",
                    "stats",
                    "--input",
                    str(pcb_file),
                    "--output",
                    str(out_file),
                ],
            ]
        )
        if code != 0:
            return f"Board stats export failed: {stderr or 'unknown error'}"
        if not out_file.exists():
            return "Board stats export completed but no output file was produced."

        try:
            stats = json.loads(out_file.read_text(encoding="utf-8"))
            return json.dumps(stats, indent=2)
        except (json.JSONDecodeError, OSError):
            return f"Board stats exported to {out_file}."

    @headless_compatible
    async def export_manufacturing_package(
        variant: str = "",
        ctx: Context[Any, Any, Any] | None = None,
    ) -> str:
        """Generate the standard set of manufacturing exports."""
        import anyio

        from .validation import _evaluate_project_gate, _render_project_gate_report

        variant_name = variant.strip() or None
        await _report_progress(ctx, 5, 100, "Running full project quality gate...")
        outcomes = await anyio.to_thread.run_sync(_evaluate_project_gate)
        blocking = [outcome for outcome in outcomes if outcome.status != "PASS"]
        if blocking:
            return _render_project_gate_report(
                blocking,
                summary=(
                    "- Manufacturing package export is hard-blocked until the full "
                    "project quality gate passes."
                ),
            )

        await _report_progress(ctx, 25, 100, "Exporting Gerbers...")
        results = [
            await anyio.to_thread.run_sync(lambda: _export_gerber(variant_name=variant_name)),
        ]
        await _report_progress(ctx, 45, 100, "Exporting drill files...")
        results.extend(
            [await anyio.to_thread.run_sync(lambda: _export_drill(variant_name=variant_name))]
        )
        await _report_progress(ctx, 65, 100, "Exporting BOM...")
        results.extend(
            [
                await anyio.to_thread.run_sync(lambda: _export_bom(variant_name=variant_name)),
            ]
        )
        await _report_progress(ctx, 85, 100, "Exporting pick-and-place data...")
        results.extend(
            [
                await anyio.to_thread.run_sync(
                    lambda: _export_pick_and_place(variant_name=variant_name)
                ),
            ]
        )
        ipc_result = await anyio.to_thread.run_sync(
            lambda: _export_ipc2581(variant_name=variant_name)
        )
        if not ipc_result.startswith("IPC-2581 export is not supported"):
            results.append(ipc_result)
        odb_result = await anyio.to_thread.run_sync(lambda: _export_odb(variant_name=variant_name))
        if not odb_result.startswith("ODB++ export is not supported"):
            results.append(odb_result)
        await _report_progress(ctx, 100, 100, "Manufacturing package complete.")
        return "\n\n".join(results)

    if include_low_level_exports:
        mcp.tool()(export_gerber)
        mcp.tool()(export_drill)
        mcp.tool()(export_bom)
        mcp.tool()(export_netlist)
        mcp.tool()(export_spice_netlist)
        mcp.tool()(export_pcb_pdf)
        mcp.tool()(export_sch_pdf)
        mcp.tool()(export_sch_svg)
        mcp.tool()(export_sch_dxf)
        mcp.tool()(export_sch_python_bom)
        register_alias(mcp, export_3d_step, "export_step")
        mcp.tool()(export_step)
        mcp.tool()(export_stepz)
        mcp.tool()(export_xao)
        mcp.tool()(export_brep)
        mcp.tool()(export_glb)
        mcp.tool()(export_gencad)
        mcp.tool()(export_ipc_d356)
        mcp.tool()(export_ply)
        mcp.tool()(export_stl)
        mcp.tool()(export_u3d)
        mcp.tool()(export_vrml)
        mcp.tool()(export_ps)
        mcp.tool()(pcb_export_3d_pdf)
        mcp.tool()(export_3d_render)
        mcp.tool()(export_pick_and_place)
        mcp.tool()(export_ipc2581)
        mcp.tool()(export_odb)
        mcp.tool()(export_svg)
        mcp.tool()(export_dxf)

    mcp.tool()(get_board_stats)
    mcp.tool()(export_manufacturing_package)
    mcp.tool()(pcb_export_stats)
