"""
Init command for MetaSpec CLI.

Implements unified `metaspec init` command that directly creates toolkit projects.
"""

import re
import shutil
import subprocess
import sys
from pathlib import Path

import typer
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.prompt import Confirm, Prompt

from metaspec.generator import create_generator
from metaspec.models import MetaSpecDefinition

# Built-in starter presets for quick start
# Used when: metaspec init <name> --template default
# For domain-specific toolkits, use interactive mode or create custom YAML
STARTER_PRESETS = {
    "default": {
        "name": "my-speckit",
        "version": "0.1.0",
        "domain": "generic",
        "description": "Generic speckit - manage specifications for any domain",
        "entity": {
            "name": "Spec",
            "fields": [
                {
                    "name": "name",
                    "type": "string",
                    "required": True,
                    "description": "Spec name",
                },
                {
                    "name": "version",
                    "type": "string",
                    "required": False,
                    "description": "Spec version",
                },
                {
                    "name": "description",
                    "type": "string",
                    "required": False,
                    "description": "Spec description",
                },
                {
                    "name": "status",
                    "type": "string",
                    "required": False,
                    "description": "Spec status (draft, review, approved)",
                },
            ],
        },
        "cli_commands": [
            {"name": "info", "description": "Show speckit information"},
        ],
        "slash_commands": [],  # Empty - users can add commands from dev or other sources
        "dependencies": ["pydantic>=2.0.0", "typer>=0.9.0"],
    },
}

console = Console()


def init_command(
    name: str = typer.Argument(
        None,
        help="Speckit name (e.g., 'my-spec-kit'). If not provided, starts interactive mode.",
    ),
    template: str = typer.Argument(
        "default",
        help="Template name (currently only 'default' is available).",
    ),
    output: Path | None = typer.Option(
        None,
        "--output",
        "-o",
        help="Output directory (default: ./<name>)",
    ),
    spec_kit: bool = typer.Option(
        False,
        "--spec-kit",
        help="[DEPRECATED] specs/ now generated by default. This option will call external 'specify init' for compatibility.",
    ),
    dry_run: bool = typer.Option(
        False,
        "--dry-run",
        help="Preview generation without creating files",
    ),
    force: bool = typer.Option(
        False,
        "--force",
        "-f",
        help="Overwrite existing output directory",
    ),
) -> None:
    """
    Create a new spec-driven speckit (interactive or template-based).

    This command combines speckit definition and generation in one step:
    1. Define speckit (interactive wizard or template)
    2. Generate complete project structure
    3. Optionally initialize with spec-kit (deprecated)

    Examples:
        # Interactive mode (recommended - most flexible)
        metaspec init

        # Quick start with default template
        metaspec init my-spec-kit

        # Explicit template (same as above)
        metaspec init my-spec-kit default

        # Preview before creating
        metaspec init my-spec-kit --dry-run

        # Specify custom output directory
        metaspec init my-spec-kit -o ./custom-path
    """
    try:
        # Determine toolkit name
        toolkit_name = name

        # Mode 1: Interactive mode (no name provided)
        if not toolkit_name:
            console.print(
                Panel.fit(
                    "[bold cyan]MetaSpec - Create Spec-Driven Toolkit[/bold cyan]\n\n"
                    "This wizard will guide you through creating a toolkit.\n"
                    "Press Ctrl+C at any time to cancel.",
                    border_style="cyan",
                )
            )

            meta_spec = _interactive_wizard()
            toolkit_name = meta_spec.name

        # Mode 2: Template mode (name provided)
        else:
            meta_spec = _create_from_preset(template, toolkit_name)

        # Determine output directory
        if output is None:
            output = Path(f"./{toolkit_name}")

        # Check if directory exists
        if output.exists() and not force and not dry_run:
            console.print(f"\n[red]Error:[/red] Directory '{output}' already exists")
            console.print(
                "Use [cyan]--force[/cyan] to overwrite or choose a different name"
            )
            raise typer.Exit(1)

        # Show preview if dry-run
        if dry_run:
            _show_preview(meta_spec, output, spec_kit)
            console.print("\n[dim]This was a dry-run. No files were created.[/dim]")
            console.print(f"[dim]Run without --dry-run to create: {output}[/dim]")
            raise typer.Exit(0)

        # Generate toolkit
        console.print("\n[bold]Generating toolkit...[/bold]")

        with Progress(
            SpinnerColumn(),
            TextColumn("[progress.description]{task.description}"),
            console=console,
        ) as progress:
            gen_task = progress.add_task("Generating files...", total=None)

            # Create generator
            generator = create_generator()

            # Generate project
            project = generator.generate(
                meta_spec=meta_spec,
                output_dir=output,
                force=force,
                dry_run=False,
            )

            progress.update(gen_task, completed=True)
            console.print(f"[green]✓[/green] Generated {len(project.files)} files")

            # Initialize spec-kit if requested
            if spec_kit:
                # Show deprecation warning
                console.print(
                    "\n[yellow]⚠ Warning: --spec-kit is deprecated[/yellow]\n"
                    "[dim]MetaSpec now generates specs/ directory by default.[/dim]\n"
                    "[dim]External 'specify init' will still be called for compatibility.[/dim]\n"
                )

                speckit_task = progress.add_task("Initializing spec-kit...", total=None)

                # Check if specify command is available
                if not shutil.which("specify"):
                    console.print(
                        "\n[red]✗[/red] spec-kit not installed\n\n"
                        "To use --spec-kit, please install spec-kit first:\n"
                        "  pip install git+https://github.com/github/spec-kit.git\n\n"
                        "Or continue without spec-kit capabilities."
                    )
                    sys.exit(1)

                # Run specify init in the output directory
                try:
                    subprocess.run(
                        ["specify", "init"],
                        cwd=output,
                        check=True,
                        capture_output=True,
                        text=True,
                    )
                    progress.update(speckit_task, completed=True)
                    console.print("[green]✓[/green] Initialized spec-kit")
                except subprocess.CalledProcessError as e:
                    console.print(
                        f"\n[red]✗[/red] Failed to initialize spec-kit: {e.stderr}"
                    )
                    sys.exit(1)

        # Success message
        _show_success(meta_spec, output, spec_kit)

        sys.exit(0)

    except KeyboardInterrupt:
        console.print("\n[yellow]Cancelled by user[/yellow]")
        sys.exit(130)
    except Exception as e:
        console.print(f"\n[red]Unexpected error:[/red] {type(e).__name__}: {e}")
        if "--debug" in sys.argv:
            raise
        sys.exit(1)


def _create_from_preset(preset_name: str, toolkit_name: str) -> MetaSpecDefinition:
    """Create MetaSpecDefinition from starter preset."""
    if preset_name not in STARTER_PRESETS:
        console.print(f"[red]Error:[/red] Unknown preset '{preset_name}'")
        console.print(f"Available presets: {', '.join(STARTER_PRESETS.keys())}")
        raise typer.Exit(1)

    console.print(f"\n[dim]Using preset: {preset_name}[/dim]")

    # Get preset data
    preset_data = STARTER_PRESETS[preset_name].copy()
    preset_data["name"] = toolkit_name

    # Create MetaSpecDefinition directly from dict (no YAML round-trip)
    meta_spec = MetaSpecDefinition.from_dict(preset_data)
    return meta_spec


def _interactive_wizard(pre_filled_name: str | None = None) -> MetaSpecDefinition:
    """Run interactive wizard to create MetaSpecDefinition."""
    # Step 1: Basic Information
    console.print("\n[bold]Step 1/5: Basic Information[/bold]")
    console.print("─" * 60)

    domain = _prompt_domain()

    if pre_filled_name:
        toolkit_name = pre_filled_name
        console.print(f"Name: [cyan]{toolkit_name}[/cyan]")
    else:
        toolkit_name = _prompt_toolkit_name(domain)

    description = _prompt_description()

    # Step 2: Entity Design
    console.print("\n[bold]Step 2/5: Entity Design[/bold]")
    console.print("─" * 60)

    entity_name, entity_fields = _prompt_entity_design(domain)

    # Step 3: Commands
    console.print("\n[bold]Step 3/5: Command Definition[/bold]")
    console.print("─" * 60)

    commands = _prompt_commands()

    # Step 4: Dependencies
    console.print("\n[bold]Step 4/5: Dependencies[/bold]")
    console.print("─" * 60)

    dependencies = _prompt_dependencies(domain)

    # Step 5: Review
    console.print("\n[bold]Step 5/5: Review[/bold]")
    console.print("─" * 60)

    _show_summary(toolkit_name, domain, entity_name, len(entity_fields), len(commands))

    if not Confirm.ask(
        "\n[cyan]Create toolkit with these settings?[/cyan]", default=True
    ):
        console.print("[yellow]Cancelled by user[/yellow]")
        raise typer.Exit(0)

    # Create MetaSpecDefinition from collected data
    meta_spec_dict = {
        "name": toolkit_name,
        "version": "0.1.0",
        "domain": domain,
        "description": description,
        "entity": {
            "name": entity_name,
            "fields": entity_fields,
        },
        "cli_commands": commands,
        "dependencies": dependencies,
    }

    # Create MetaSpecDefinition directly from dict (no YAML round-trip)
    meta_spec = MetaSpecDefinition.from_dict(meta_spec_dict)
    return meta_spec


def _show_preview(meta_spec: MetaSpecDefinition, output_dir: Path, spec_kit: bool) -> None:
    """Show preview of what will be generated."""
    console.print()
    console.print(
        Panel.fit(
            f"[bold cyan]Preview: {meta_spec.name}[/bold cyan]\n\n"
            f"[bold]Version:[/bold] {meta_spec.version}\n"
            f"[bold]Domain:[/bold] {meta_spec.domain}\n"
            f"[bold]Output:[/bold] {output_dir}\n"
            f"[bold]Spec-Kit:[/bold] {'Enabled' if spec_kit else 'Disabled'}",
            title="[bold]Speckit Preview[/bold]",
            border_style="cyan",
        )
    )

    console.print("\n[bold]What will be generated:[/bold]")
    console.print("  📁 Project structure with CLI, parser, validator")
    console.print("  📝 README.md and AGENTS.md")
    console.print("  🐍 Python package with pyproject.toml")
    console.print("  📋 Templates and constitution")
    if spec_kit:
        console.print("  ✨ Spec-Kit integration (specs/ directory)")


def _show_success(meta_spec: MetaSpecDefinition, output_dir: Path, spec_kit: bool) -> None:
    """Show success message with next steps."""
    package_name = _name_to_package_name(meta_spec.name)

    next_steps = [
        f"cd {output_dir.name}",
        "./scripts/init.sh           # Initialize with uv (recommended)",
        "# Or: pip install -e .      # Use pip if uv not available",
        f"{package_name} --help       # Test CLI",
    ]

    if spec_kit:
        next_steps.insert(1, "")
        next_steps.insert(2, "# Spec-driven development enabled!")
        next_steps.insert(3, "# Edit specs/spec.md to define requirements")
        next_steps.insert(4, "")

    console.print()
    console.print(
        Panel(
            f"[green]✓ Successfully created speckit![/green]\n\n"
            f"[bold]Name:[/bold] {meta_spec.name}\n"
            f"[bold]Package:[/bold] {package_name}\n"
            f"[bold]Version:[/bold] {meta_spec.version}\n"
            f"[bold]Domain:[/bold] {meta_spec.domain}\n"
            f"[bold]Location:[/bold] {output_dir}\n\n"
            f"[dim]Next steps:[/dim]\n" + "\n".join(f"  {step}" for step in next_steps),
            title="[bold green]Speckit Created[/bold green]",
            border_style="green",
        )
    )


# ============================================================================
# Utility Functions
# ============================================================================


def _name_to_package_name(name: str) -> str:
    """
    Convert speckit name to Python package name (snake_case).

    Reuses the same logic as generator.py to ensure consistency.
    """
    # Replace any non-alphanumeric characters (except underscores) with underscores
    package_name = re.sub(r"[^a-z0-9_]", "_", name.lower())
    # Remove leading/trailing underscores and collapse multiple underscores
    package_name = re.sub(r"_{2,}", "_", package_name).strip("_")
    return package_name


# ============================================================================
# Interactive Mode Helper Functions
# ============================================================================


def _prompt_domain() -> str:
    """Prompt for domain selection."""
    domains = {
        "1": ("generic", "Generic toolkit (for any domain)"),
        "2": ("testing", "Testing and QA toolkits"),
        "3": ("api", "API development and testing"),
        "4": ("design", "Design systems and components"),
        "5": ("data", "Data processing and pipelines"),
        "6": ("custom", "Enter your own domain name"),
    }

    console.print("\n[cyan]Available domains:[/cyan]")
    for key, (domain, desc) in domains.items():
        console.print(f"  {key}. [bold]{domain}[/bold] - {desc}")

    choice = Prompt.ask(
        "\n[cyan]Select domain[/cyan]", choices=list(domains.keys()), default="1"
    )

    domain = domains[choice][0]

    # Allow custom domain input
    if domain == "custom":
        domain = Prompt.ask("[cyan]Enter custom domain name[/cyan]", default="testing")

    return domain


def _prompt_toolkit_name(domain: str) -> str:
    """Prompt for speckit name."""
    default_name = f"{domain}-speckit" if domain != "generic" else "my-speckit"

    name = Prompt.ask(
        "\n[cyan]Speckit name[/cyan] (lowercase-with-dashes)", default=default_name
    )

    # Validate name format
    if not name.replace("-", "").replace("_", "").isalnum():
        console.print(
            "[yellow]Warning: Name should only contain letters, numbers, hyphens, and underscores[/yellow]"
        )

    return name


def _prompt_description() -> str:
    """Prompt for description."""
    return Prompt.ask("\n[cyan]Brief description[/cyan]", default="Spec-driven toolkit")


def _prompt_entity_design(domain: str) -> tuple[str, list[dict]]:
    """Prompt for entity design."""
    # Suggest entity name based on domain
    suggestions = {
        "generic": "Spec",
        "testing": "TestCase",
        "api": "Endpoint",
        "design": "Component",
        "data": "Pipeline",
        "mcp": "MCPServer",
        "web": "Component",
        "ai": "Agent",
    }

    entity_name = Prompt.ask(
        "\n[cyan]Main entity name[/cyan] (what does your toolkit manage?)",
        default=suggestions.get(domain, "Entity"),
    )

    console.print(f"\n[cyan]Define fields for '{entity_name}':[/cyan]")
    console.print(
        "[dim]Tips: Start with 3-5 essential fields. You can add more later.[/dim]"
    )

    fields = []

    # Common required fields
    console.print("\n[dim]Adding common required fields...[/dim]")
    fields.append(
        {
            "name": "name",
            "type": "string",
            "required": True,
            "description": f"{entity_name} name",
        }
    )

    # Add custom fields
    console.print(
        "\n[cyan]Add custom fields (press Enter with empty name to finish):[/cyan]"
    )

    while True:
        field_name = Prompt.ask("\n  Field name", default="")

        if not field_name:
            break

        field_type = Prompt.ask(
            "  Field type (common: string, number, boolean, array, object)",
            default="string",
        )

        required = Confirm.ask("  Required?", default=False)

        description = Prompt.ask("  Description", default=f"{field_name} value")

        fields.append(
            {
                "name": field_name,
                "type": field_type,
                "required": required,
                "description": description,
            }
        )

        console.print(f"[green]  ✓ Added field: {field_name} ({field_type})[/green]")

    return entity_name, fields


def _prompt_commands() -> list[dict]:
    """Prompt for command definitions."""
    console.print("\n[cyan]Define commands for your toolkit:[/cyan]")
    console.print("[dim]Common commands: init, validate, generate[/dim]")

    commands = []

    # Default commands
    default_commands = [
        {"name": "init", "description": "Initialize new spec file"},
        {"name": "validate", "description": "Validate spec file"},
        {"name": "generate", "description": "Generate from spec"},
    ]

    if Confirm.ask(
        "\n[cyan]Use default commands (init, validate, generate)?[/cyan]", default=True
    ):
        commands = default_commands
        console.print("[green]✓ Added 3 default commands[/green]")
    else:
        console.print(
            "\n[cyan]Add custom commands (press Enter with empty name to finish):[/cyan]"
        )

        while True:
            cmd_name = Prompt.ask("\n  Command name", default="")

            if not cmd_name:
                break

            cmd_desc = Prompt.ask("  Description", default=f"{cmd_name} operation")

            commands.append({"name": cmd_name, "description": cmd_desc})

            console.print(f"[green]  ✓ Added command: {cmd_name}[/green]")

    return commands


def _prompt_dependencies(domain: str) -> list[str]:
    """Prompt for dependencies."""
    console.print("\n[cyan]Python dependencies:[/cyan]")

    # Default dependencies
    defaults = [
        "pydantic>=2.0.0",
        "typer>=0.9.0",
    ]

    console.print("\n[dim]Recommended dependencies:[/dim]")
    for dep in defaults:
        console.print(f"  • {dep}")

    if Confirm.ask("\n[cyan]Use recommended dependencies?[/cyan]", default=True):
        return defaults

    console.print("\n[cyan]Enter dependencies (one per line, empty to finish):[/cyan]")
    deps = []

    while True:
        dep = Prompt.ask("  Dependency", default="")
        if not dep:
            break
        deps.append(dep)

    return deps if deps else defaults


def _show_summary(
    name: str, domain: str, entity: str, field_count: int, command_count: int
) -> None:
    """Show summary of configuration."""
    console.print("\n[cyan]Summary:[/cyan]")
    console.print(f"  • Toolkit: [bold]{name}[/bold]")
    console.print(f"  • Domain: [bold]{domain}[/bold]")
    console.print(f"  • Entity: [bold]{entity}[/bold] ({field_count} fields)")
    console.print(f"  • Commands: {command_count}")
