"""
CLI Runner - Main entry point for Python CLI commands.

This module dispatches commands to the appropriate handlers based on cli.yaml SST.

SST Reference: packages/lightwave-core/lightwave/schema/definitions/cli.yaml
"""

from __future__ import annotations

import argparse
import json
import sys
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from typing import Any


def main() -> None:
    """Main entry point for the CLI."""
    parser = argparse.ArgumentParser(
        prog="lightwave.cli",
        description="LightWave CLI - Python runtime commands",
    )
    parser.add_argument("domain", help="Command domain (test, spec, schema, security, package, dj)")
    parser.add_argument("command", help="Command to run")
    parser.add_argument("args", nargs="*", help="Command arguments")
    parser.add_argument("--json", action="store_true", help="Output in JSON format")
    parser.add_argument("--dry-run", action="store_true", help="Preview without executing")
    parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")

    args, unknown = parser.parse_known_args()

    try:
        result = run_command(
            domain=args.domain,
            command=args.command,
            cmd_args=args.args + unknown,
            json_output=args.json,
            dry_run=args.dry_run,
            verbose=args.verbose,
        )
        if args.json and result is not None:
            print(json.dumps(result, indent=2, default=str))
        sys.exit(0)
    except CommandError as e:
        if args.json:
            print(json.dumps({"success": False, "error": str(e)}, indent=2))
        else:
            print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)
    except KeyboardInterrupt:
        print("\nInterrupted", file=sys.stderr)
        sys.exit(130)


class CommandError(Exception):
    """Raised when a command fails."""

    pass


def run_command(
    domain: str,
    command: str,
    cmd_args: list[str] | None = None,
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Run a CLI command.

    Args:
        domain: Command domain (test, spec, schema, security, package, dj)
        command: Command name
        cmd_args: Command arguments
        json_output: Return structured data for JSON output
        dry_run: Preview without executing
        verbose: Verbose output

    Returns:
        Result dict if json_output=True, else None

    Raises:
        CommandError: If the command fails
    """
    cmd_args = cmd_args or []

    # Dispatch to domain handlers
    # SST: domains.<domain>.runtime == python
    handlers = {
        "test": _handle_test,
        "spec": _handle_spec,
        "schema": _handle_schema,
        "security": _handle_security,
        "package": _handle_package,
        "dj": _handle_dj,
        "docker": _handle_docker,
        "db": _handle_db,
        "quality": _handle_quality,
    }

    handler = handlers.get(domain)
    if handler is None:
        raise CommandError(f"Unknown domain: {domain}")

    return handler(command, cmd_args, json_output=json_output, dry_run=dry_run, verbose=verbose)


# =============================================================================
# Domain Handlers
# =============================================================================


def _handle_test(
    command: str,
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Handle test domain commands.

    SST: domains.test.commands (runtime: python)
    - run: Run pytest on specified path
    - coverage: Run tests with coverage report
    - adversarial: Run adversarial test suite
    - drill: Run security attack drill
    """
    from lightwave.cli.test_runner import run_adversarial, run_coverage, run_drill, run_pytest

    if command == "run":
        return run_pytest(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "coverage":
        return run_coverage(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "adversarial":
        return run_adversarial(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "drill":
        return run_drill(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    else:
        raise CommandError(f"Unknown test command: {command}")


def _handle_spec(
    command: str,
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Handle spec domain commands.

    SST: domains.spec.commands (runtime: python)
    - show: Show test specification
    - create: Create new specification
    - generate-tasks: Generate tasks from story
    - validate-production: Validate against production
    - coverage: Show spec coverage
    """
    from lightwave.cli.spec_commands import (
        create_spec,
        generate_tasks,
        show_spec,
        spec_coverage,
        validate_production,
    )

    if command == "show":
        return show_spec(args, json_output=json_output, verbose=verbose)
    elif command == "create":
        return create_spec(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "generate-tasks":
        return generate_tasks(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "validate-production":
        return validate_production(args, json_output=json_output, verbose=verbose)
    elif command == "coverage":
        return spec_coverage(args, json_output=json_output, verbose=verbose)
    else:
        raise CommandError(f"Unknown spec command: {command}")


def _handle_schema(
    command: str,
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Handle schema domain commands.

    SST: domains.schema.commands (runtime: python)
    - validate: Validate YAML definitions against Pydantic schemas
    - drift: Generate drift report
    - reconcile: Reconcile differences
    - generate: Generate types for target language
    - coverage: Show SST coverage metrics
    """
    from lightwave.cli.schema_commands import (
        drift_report,
        generate_types,
        reconcile_schema,
        schema_coverage,
        validate_schema,
    )

    if command == "validate":
        return validate_schema(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "drift":
        return drift_report(args, json_output=json_output, verbose=verbose)
    elif command == "reconcile":
        return reconcile_schema(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "generate":
        return generate_types(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "coverage":
        return schema_coverage(args, json_output=json_output, verbose=verbose)
    else:
        raise CommandError(f"Unknown schema command: {command}")


def _handle_security(
    command: str,
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Handle security domain commands.

    SST: domains.security.commands (runtime: python)
    - drill: Run attack drill
    - audit: Generate security audit report
    - payloads: List available attack payloads
    """
    from lightwave.cli.security_commands import list_payloads, run_attack_drill, security_audit

    if command == "drill":
        return run_attack_drill(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "audit":
        return security_audit(args, json_output=json_output, verbose=verbose)
    elif command == "payloads":
        return list_payloads(args, json_output=json_output, verbose=verbose)
    else:
        raise CommandError(f"Unknown security command: {command}")


def _handle_package(
    command: str,
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Handle package domain commands.

    SST: domains.package.commands (runtime: python)
    - status: Show publication state
    - validate: Check publication criteria
    - ci-mode: Output CI install method
    - publish: Build and publish to PyPI
    - update-state: Update publication state
    - align: Validate version alignment
    """
    from lightwave.cli.package_commands import (
        package_align,
        package_ci_mode,
        package_publish,
        package_status,
        package_update_state,
        package_validate,
    )

    if command == "status":
        return package_status(args, json_output=json_output, verbose=verbose)
    elif command == "validate":
        return package_validate(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "ci-mode":
        return package_ci_mode(args, json_output=json_output, verbose=verbose)
    elif command == "publish":
        return package_publish(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "update-state":
        return package_update_state(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "align":
        return package_align(args, json_output=json_output, verbose=verbose)
    else:
        raise CommandError(f"Unknown package command: {command}")


def _handle_dj(
    command: str,
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Handle Django management commands.

    SST: domains.dj.commands (runtime: python override)
    - migrate: Run Django migrations
    - makemigrations: Create new migrations
    - check: Run Django system checks
    - shell: Open Django shell
    - collectstatic: Collect static files
    """
    from lightwave.cli.django_commands import run_django_command

    if command in ("migrate", "makemigrations", "check", "shell", "collectstatic"):
        return run_django_command(command, args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    else:
        raise CommandError(f"Unknown dj command for Python runtime: {command}")


def _handle_docker(
    command: str,
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Handle docker domain commands.

    SST: domains.docker.commands (runtime: python)
    - up: Start development stack
    - down: Stop development stack
    - logs: View service logs
    - rebuild: Rebuild and restart a service
    - shell: Open shell in running container
    """
    from lightwave.cli.docker_commands import (
        docker_down,
        docker_logs,
        docker_rebuild,
        docker_shell,
        docker_up,
    )

    if command == "up":
        return docker_up(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "down":
        return docker_down(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "logs":
        return docker_logs(args, json_output=json_output, verbose=verbose)
    elif command == "rebuild":
        return docker_rebuild(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "shell":
        return docker_shell(args, json_output=json_output, verbose=verbose)
    else:
        raise CommandError(f"Unknown docker command: {command}")


def _handle_db(
    command: str,
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Handle db domain commands.

    SST: domains.db.commands (runtime: python)
    - shell: Open database shell (psql)
    - dump: Dump database to file
    - restore: Restore database from dump
    - reset: Reset database (drop + migrate)
    """
    from lightwave.cli.db_commands import (
        db_dump,
        db_reset,
        db_restore,
        db_shell,
    )

    if command == "shell":
        return db_shell(args, json_output=json_output, verbose=verbose)
    elif command == "dump":
        return db_dump(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "restore":
        return db_restore(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "reset":
        return db_reset(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    else:
        raise CommandError(f"Unknown db command: {command}")


def _handle_quality(
    command: str,
    args: list[str],
    *,
    json_output: bool = False,
    dry_run: bool = False,
    verbose: bool = False,
) -> dict[str, Any] | None:
    """
    Handle quality domain commands.

    SST: domains.quality.commands (runtime: python)
    - lint: Run ruff + eslint
    - format: Run ruff format + prettier
    - ci: Run full CI pipeline locally
    - pre-commit: Run pre-commit hooks
    """
    from lightwave.cli.quality_commands import (
        quality_ci,
        quality_format,
        quality_lint,
        quality_precommit,
    )

    if command == "lint":
        return quality_lint(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "format":
        return quality_format(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "ci":
        return quality_ci(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    elif command == "pre-commit":
        return quality_precommit(args, json_output=json_output, dry_run=dry_run, verbose=verbose)
    else:
        raise CommandError(f"Unknown quality command: {command}")
