"""
CLI command for initializing KiCad projects with manufacturer design rules.

Provides the `kct init` command to create or configure a KiCad project with
manufacturer-specific design rules from the start, preventing DRC issues later.

Usage:
    kct init myproject --mfr jlcpcb --layers 4
    kct init myproject.kicad_pro --mfr jlcpcb --layers 2
    kct init . --mfr seeed --layers 4 --copper 2
"""

import argparse
import json
import sys
from pathlib import Path

from rich.console import Console
from rich.table import Table

from kicad_tools.core.netclass_templates import (
    apply_design_template,
    get_available_design_types,
    get_netclass_summary,
)
from kicad_tools.manufacturers import DesignRules, get_manufacturer_ids, get_profile
from kicad_tools.units import get_current_formatter

console = Console()
err_console = Console(stderr=True)


def create_project_file(project_path: Path, project_name: str) -> dict:
    """Create a new KiCad project file with basic structure.

    Args:
        project_path: Path to the project file
        project_name: Name of the project

    Returns:
        Project data dictionary
    """
    return {
        "meta": {"filename": project_name, "version": 1},
        "board": {"design_settings": {"rules": {}, "defaults": {}}},
        "schematic": {"meta": {"version": 1}},
        "sheets": [],
        "text_variables": {},
    }


def create_dru_content(rules: DesignRules, manufacturer_id: str, layers: int) -> str:
    """Generate KiCad design rules file content.

    Args:
        rules: Design rules from manufacturer profile
        manufacturer_id: Manufacturer identifier
        layers: Number of copper layers

    Returns:
        String content for .kicad_dru file
    """
    return f"""\
(version 1)

; Design rules for {manufacturer_id.upper()} ({layers}-layer)
; Generated by kicad-tools init command

(rule "Minimum Trace Width"
  (constraint track_width (min {rules.min_trace_width_mm}mm)))

(rule "Minimum Clearance"
  (constraint clearance (min {rules.min_clearance_mm}mm)))

(rule "Minimum Via Drill"
  (constraint hole_size (min {rules.min_via_drill_mm}mm) (max {rules.max_hole_diameter_mm}mm)))

(rule "Minimum Via Diameter"
  (constraint via_diameter (min {rules.min_via_diameter_mm}mm)))

(rule "Minimum Annular Ring"
  (constraint annular_width (min {rules.min_annular_ring_mm}mm)))

(rule "Copper to Edge Clearance"
  (constraint edge_clearance (min {rules.min_copper_to_edge_mm}mm)))

(rule "Hole to Edge Clearance"
  (constraint hole_to_hole (min {rules.min_hole_to_edge_mm}mm)))

(rule "Minimum Silkscreen Width"
  (constraint silk_clearance (min {rules.min_silkscreen_width_mm}mm)))

(rule "Minimum Solder Mask Dam"
  (constraint physical_clearance (min {rules.min_solder_mask_dam_mm}mm)))
"""


def print_init_summary(
    project_path: Path,
    dru_path: Path,
    profile,
    rules: DesignRules,
    layers: int,
    copper: float,
    created_project: bool,
    created_dru: bool,
    design_type: str | None = None,
    netclass_summary: list[dict] | None = None,
) -> None:
    """Print a summary of what was initialized.

    Args:
        project_path: Path to the project file
        dru_path: Path to the DRU file
        profile: Manufacturer profile
        rules: Design rules applied
        layers: Number of copper layers
        copper: Copper weight in oz
        created_project: Whether a new project was created
        created_dru: Whether DRU file was created/updated
        design_type: Design type template applied (if any)
        netclass_summary: Summary of netclasses created (if any)
    """
    fmt = get_current_formatter()
    console.print(f"\n[bold green]✓[/bold green] Initialized project for {profile.name}")
    console.print(f"  Configuration: {layers}-layer, {copper}oz copper")
    if design_type:
        console.print(f"  Design type: {design_type}")
    console.print(f"  Units: {fmt.unit_name}\n")

    # Design rules table
    table = Table(title="Design Rules Applied", show_header=True, header_style="bold cyan")
    table.add_column("Parameter", style="dim")
    table.add_column("Value", justify="right")
    table.add_column("Previous", justify="right", style="dim")

    table.add_row(
        "Via drill",
        fmt.format(rules.min_via_drill_mm),
        fmt.format(0.20) if created_project else "-",
    )
    table.add_row(
        "Via diameter",
        fmt.format(rules.min_via_diameter_mm),
        fmt.format(0.45) if created_project else "-",
    )
    table.add_row(
        "Min clearance",
        fmt.format(rules.min_clearance_mm),
        fmt.format(0.20) if created_project else "-",
    )
    table.add_row(
        "Min trace width",
        fmt.format(rules.min_trace_width_mm),
        fmt.format(0.25) if created_project else "-",
    )
    table.add_row(
        "Annular ring",
        fmt.format(rules.min_annular_ring_mm),
        fmt.format(0.13) if created_project else "-",
    )
    table.add_row(
        "Copper to edge",
        fmt.format(rules.min_copper_to_edge_mm),
        fmt.format(0.30) if created_project else "-",
    )

    console.print(table)

    # Netclass table (if netclasses were configured)
    if netclass_summary and len(netclass_summary) > 1:
        console.print()
        nc_table = Table(title="Net Classes Configured", show_header=True, header_style="bold cyan")
        nc_table.add_column("Class", style="bold")
        nc_table.add_column("Track Width", justify="right")
        nc_table.add_column("Clearance", justify="right")
        nc_table.add_column("Patterns", justify="right", style="dim")

        for nc in netclass_summary:
            nc_table.add_row(
                nc["name"],
                fmt.format(nc["track_width"]),
                fmt.format(nc["clearance"]),
                str(nc["pattern_count"]) if nc["pattern_count"] > 0 else "-",
            )

        console.print(nc_table)

    # Files section
    console.print("\n[bold]Files:[/bold]")
    if created_project:
        console.print(f"  [green]Created:[/green] {project_path}")
    else:
        console.print(f"  [yellow]Updated:[/yellow] {project_path}")

    if created_dru:
        console.print(f"  [green]Created:[/green] {dru_path}")
    else:
        console.print(f"  [yellow]Updated:[/yellow] {dru_path}")

    # Next steps
    console.print("\n[bold]Next steps:[/bold]")
    console.print("  1. Open project in KiCad")
    console.print("  2. Design schematic")
    console.print("  3. Layout PCB (rules will be enforced)")
    console.print(f"  4. Run: kct audit {project_path.name} --mfr {profile.id}")


def init_project(
    target: str,
    manufacturer: str,
    layers: int = 2,
    copper: float = 1.0,
    design_type: str | None = None,
    dry_run: bool = False,
    output_format: str = "text",
) -> int:
    """Initialize a KiCad project with manufacturer design rules.

    Args:
        target: Project name or path to existing .kicad_pro file
        manufacturer: Manufacturer ID (jlcpcb, seeed, etc.)
        layers: Number of copper layers
        copper: Copper weight in oz
        design_type: Design type template for netclass configuration
        dry_run: If True, show what would be done without making changes
        output_format: Output format ("text" or "json")

    Returns:
        Exit code (0 for success)
    """
    # Get manufacturer profile
    try:
        profile = get_profile(manufacturer)
    except ValueError as e:
        err_console.print(f"[red]Error:[/red] {e}")
        return 1

    # Get design rules for configuration
    rules = profile.get_design_rules(layers, copper)

    # Determine project path
    target_path = Path(target)

    if target_path.suffix == ".kicad_pro":
        # Existing project file specified
        project_path = target_path
        project_name = target_path.stem
    elif target_path.suffix:
        # Some other file extension
        err_console.print(
            f"[red]Error:[/red] Expected .kicad_pro file or project name, got: {target_path.suffix}"
        )
        return 1
    else:
        # Project name/directory specified
        if target == ".":
            # Use current directory name
            project_name = Path.cwd().name
            project_path = Path.cwd() / f"{project_name}.kicad_pro"
        else:
            project_name = target_path.name
            # Add .kicad_pro suffix to the path
            project_path = target_path.with_suffix(".kicad_pro")

    # Determine DRU path (same directory as project file)
    dru_path = project_path.with_suffix(".kicad_dru")

    # Track what we'll create
    will_create_project = not project_path.exists()
    will_create_dru = not dru_path.exists()

    # JSON output mode
    if output_format == "json":
        result = {
            "project_path": str(project_path),
            "dru_path": str(dru_path),
            "manufacturer": profile.id,
            "layers": layers,
            "copper_oz": copper,
            "design_type": design_type,
            "rules": rules.to_dict(),
            "will_create_project": will_create_project,
            "will_create_dru": will_create_dru,
            "dry_run": dry_run,
        }
        print(json.dumps(result, indent=2))
        if dry_run:
            return 0

    # Dry run - show what would happen
    if dry_run:
        console.print("\n[bold yellow]Dry run:[/bold yellow] No changes will be made\n")
        print_init_summary(
            project_path,
            dru_path,
            profile,
            rules,
            layers,
            copper,
            will_create_project,
            will_create_dru,
            design_type=design_type,
        )
        return 0

    # Create or load project file
    if project_path.exists():
        try:
            project_data = json.loads(project_path.read_text(encoding="utf-8"))
        except (json.JSONDecodeError, OSError) as e:
            err_console.print(f"[red]Error:[/red] Failed to load project file: {e}")
            return 1
    else:
        # Create parent directory if needed
        project_path.parent.mkdir(parents=True, exist_ok=True)
        project_data = create_project_file(project_path, project_name)

    # Ensure nested structure exists
    if "board" not in project_data:
        project_data["board"] = {}
    if "design_settings" not in project_data["board"]:
        project_data["board"]["design_settings"] = {}
    if "rules" not in project_data["board"]["design_settings"]:
        project_data["board"]["design_settings"]["rules"] = {}
    if "defaults" not in project_data["board"]["design_settings"]:
        project_data["board"]["design_settings"]["defaults"] = {}

    settings = project_data["board"]["design_settings"]

    # Apply design rules
    settings["rules"]["min_clearance"] = rules.min_clearance_mm
    settings["rules"]["min_track_width"] = rules.min_trace_width_mm
    settings["rules"]["min_via_diameter"] = rules.min_via_diameter_mm
    settings["rules"]["min_via_annular_width"] = rules.min_annular_ring_mm
    settings["rules"]["min_through_hole_diameter"] = rules.min_hole_diameter_mm
    settings["rules"]["min_via_hole"] = rules.min_via_drill_mm
    settings["rules"]["min_copper_edge_clearance"] = rules.min_copper_to_edge_mm

    # Set defaults for new objects
    settings["defaults"]["track_min_width"] = rules.min_trace_width_mm
    settings["defaults"]["clearance_min"] = rules.min_clearance_mm
    settings["defaults"]["via_min_diameter"] = rules.min_via_diameter_mm
    settings["defaults"]["via_min_drill"] = rules.min_via_drill_mm

    # Store manufacturer metadata
    if "meta" not in project_data:
        project_data["meta"] = {}
    project_data["meta"]["manufacturer"] = profile.id
    project_data["meta"]["layers"] = layers
    project_data["meta"]["copper_oz"] = copper

    # Apply netclass template if specified
    netclass_summary = None
    if design_type:
        try:
            apply_design_template(project_data, design_type)
            project_data["meta"]["design_type"] = design_type
            netclass_summary = get_netclass_summary(project_data)
        except ValueError as e:
            err_console.print(f"[red]Error:[/red] {e}")
            return 1

    # Write project file
    try:
        project_path.write_text(json.dumps(project_data, indent=2), encoding="utf-8")
    except OSError as e:
        err_console.print(f"[red]Error:[/red] Failed to write project file: {e}")
        return 1

    # Create/update DRU file
    dru_content = create_dru_content(rules, profile.id, layers)
    try:
        dru_path.write_text(dru_content, encoding="utf-8")
    except OSError as e:
        err_console.print(f"[red]Error:[/red] Failed to write DRU file: {e}")
        return 1

    # Print summary
    if output_format == "text":
        print_init_summary(
            project_path,
            dru_path,
            profile,
            rules,
            layers,
            copper,
            will_create_project,
            will_create_dru,
            design_type=design_type,
            netclass_summary=netclass_summary,
        )

    return 0


def main(argv: list[str] | None = None) -> int:
    """Main entry point for init command."""
    design_types = get_available_design_types()
    parser = argparse.ArgumentParser(
        description="Initialize a KiCad project with manufacturer design rules",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=f"""\
Examples:
  kct init myproject --mfr jlcpcb               Create project with JLCPCB 2-layer rules
  kct init myproject --mfr jlcpcb --layers 4    Create project with 4-layer rules
  kct init myproject --mfr jlcpcb -t audio      Create audio project with netclasses
  kct init existing.kicad_pro --mfr seeed       Apply Seeed rules to existing project
  kct init . --mfr jlcpcb                       Initialize in current directory
  kct init myproject --mfr jlcpcb --dry-run     Show what would be done

Available manufacturers: {", ".join(get_manufacturer_ids())}
Design types: {", ".join(design_types)}
""",
    )
    parser.add_argument(
        "project",
        help="Project name or path to .kicad_pro file (use '.' for current directory)",
    )
    parser.add_argument(
        "-m",
        "--mfr",
        required=True,
        metavar="MANUFACTURER",
        help="Manufacturer ID (jlcpcb, seeed, pcbway, oshpark)",
    )
    parser.add_argument(
        "-l",
        "--layers",
        type=int,
        default=2,
        help="Number of copper layers (default: 2)",
    )
    parser.add_argument(
        "-c",
        "--copper",
        type=float,
        default=1.0,
        help="Copper weight in oz (default: 1.0)",
    )
    parser.add_argument(
        "-t",
        "--design-type",
        choices=design_types,
        default=None,
        metavar="TYPE",
        help=f"Design type for netclass configuration ({', '.join(design_types)})",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="Show what would be done without making changes",
    )
    parser.add_argument(
        "--format",
        choices=["text", "json"],
        default="text",
        help="Output format (default: text)",
    )

    args = parser.parse_args(argv)

    return init_project(
        target=args.project,
        manufacturer=args.mfr,
        layers=args.layers,
        copper=args.copper,
        design_type=args.design_type,
        dry_run=args.dry_run,
        output_format=args.format,
    )


if __name__ == "__main__":
    sys.exit(main())
