"""
CLI commands for demo data management.

Commands:
  dazzle demo load      — Load seed data into a running instance
  dazzle demo validate  — Validate seed files against DSL
  dazzle demo reset     — Clear and reload demo data
  dazzle demo propose   — Propose a demo data blueprint from DSL
  dazzle demo save      — Save a blueprint JSON file
  dazzle demo generate  — Generate demo data files from the blueprint
"""

from pathlib import Path
from typing import Any

import typer

demo_app = typer.Typer(help="Demo data management commands.", no_args_is_help=True)

DEFAULT_TEST_PASSWORD = "dazzle-test-2026"


def _generate_test_personas_toml(
    project_root: Path,
    blueprint_result: dict[str, Any],
) -> None:
    """Generate .dazzle/test_personas.toml from the demo blueprint.

    Does not overwrite if the file already exists.
    """

    toml_path = project_root / ".dazzle" / "test_personas.toml"
    if toml_path.exists():
        typer.echo(
            f"test_personas.toml already exists at {toml_path}, skipping credential generation."
        )
        return

    # Extract persona info from blueprint result
    personas_data: list[dict[str, Any]] = blueprint_result.get("personas", [])
    if not personas_data:
        return

    # Try to read test_password from dazzle.toml
    test_password = DEFAULT_TEST_PASSWORD
    try:
        import tomllib

        toml_conf = project_root / "dazzle.toml"
        if toml_conf.exists():
            with toml_conf.open("rb") as f:
                config = tomllib.load(f)
            test_password = config.get("demo", {}).get("test_password", DEFAULT_TEST_PASSWORD)
    except Exception:
        pass

    lines = ["# Generated by 'dazzle demo propose' for journey testing", ""]
    for persona in personas_data:
        name = persona.get("persona_name", "").lower().replace(" ", "_")
        if not name:
            continue
        email = f"{name}@demo.example.com"
        lines.append(f"[personas.{name}]")
        lines.append(f'email = "{email}"')
        lines.append(f'password = "{test_password}"')
        lines.append("")

    if len(lines) > 2:  # More than just the header
        toml_path.parent.mkdir(parents=True, exist_ok=True)
        toml_path.write_text("\n".join(lines), encoding="utf-8")
        typer.echo(f"Generated test_personas.toml at {toml_path}")


def _find_data_dir(project_root: Path) -> Path | None:
    """Find the demo data directory, checking demo_data/ then .dazzle/demo_data/."""
    for candidate in [
        project_root / "demo_data",
        project_root / ".dazzle" / "demo_data",
    ]:
        if candidate.is_dir():
            return candidate
    return None


@demo_app.command(name="load")
def load_command(
    base_url: str = typer.Option(
        "http://localhost:8000",
        "--base-url",
        "-u",
        help="Base URL of the running Dazzle instance",
    ),
    email: str = typer.Option(
        None,
        "--email",
        "-e",
        help="Admin email for authentication",
    ),
    password: str = typer.Option(
        None,
        "--password",
        "-p",
        help="Admin password for authentication",
    ),
    data_dir: Path | None = typer.Option(
        None,
        "--data-dir",
        "-d",
        help="Path to seed data directory (auto-detected if omitted)",
    ),
    entities: str | None = typer.Option(
        None,
        "--entities",
        help="Comma-separated entity names to load (default: all)",
    ),
    project_root: Path = typer.Option(
        Path("."),
        "--project",
        help="Project root directory",
    ),
) -> None:
    """Load demo seed data into a running Dazzle instance."""
    from dazzle.demo_data.loader import DemoDataLoader, topological_sort_entities

    project_root = project_root.resolve()

    # Parse DSL to get entity graph
    typer.echo("Parsing DSL...")
    try:
        from dazzle.cli.utils import load_project_appspec

        appspec = load_project_appspec(project_root)
    except Exception as e:
        typer.echo(f"Failed to parse DSL: {e}", err=True)
        raise typer.Exit(1)

    # Find data directory
    if data_dir is None:
        data_dir = _find_data_dir(project_root)
        if data_dir is None:
            typer.echo(
                "No demo data directory found. Run 'dazzle demo_data generate' first.", err=True
            )
            raise typer.Exit(1)
    typer.echo(f"Data directory: {data_dir}")

    # Topological sort
    entity_order = topological_sort_entities(appspec.domain.entities)
    typer.echo(f"Entity load order: {', '.join(entity_order)}")

    # Parse entity filter
    entities_filter = [e.strip() for e in entities.split(",")] if entities else None

    # Load
    with DemoDataLoader(base_url=base_url, email=email, password=password) as loader:
        if email and password:
            typer.echo("Authenticating...")
            try:
                loader.authenticate()
            except RuntimeError as e:
                typer.echo(f"Authentication failed: {e}", err=True)
                raise typer.Exit(1)

        typer.echo("Loading demo data...")
        report = loader.load_all(data_dir, entity_order, entities_filter=entities_filter)

    typer.echo("")
    typer.echo(report.summary())

    if report.total_failed > 0:
        raise typer.Exit(1)


@demo_app.command(name="validate")
def validate_command(
    data_dir: Path | None = typer.Option(
        None,
        "--data-dir",
        "-d",
        help="Path to seed data directory (auto-detected if omitted)",
    ),
    project_root: Path = typer.Option(
        Path("."),
        "--project",
        help="Project root directory",
    ),
) -> None:
    """Validate seed files against the DSL entity definitions."""
    from dazzle.demo_data.loader import validate_seed_data

    project_root = project_root.resolve()

    # Parse DSL
    typer.echo("Parsing DSL...")
    try:
        from dazzle.cli.utils import load_project_appspec

        appspec = load_project_appspec(project_root)
    except Exception as e:
        typer.echo(f"Failed to parse DSL: {e}", err=True)
        raise typer.Exit(1)

    # Find data directory
    if data_dir is None:
        data_dir = _find_data_dir(project_root)
        if data_dir is None:
            typer.echo("No demo data directory found.", err=True)
            raise typer.Exit(1)

    typer.echo(f"Validating seed data in {data_dir}...")
    errors = validate_seed_data(data_dir, appspec.domain.entities)

    if errors:
        typer.echo(f"\n{len(errors)} validation errors found:")
        for err in errors:
            typer.echo(f"  - {err}")
        raise typer.Exit(1)
    else:
        typer.echo("All seed data is valid.")


@demo_app.command(name="reset")
def reset_command(
    base_url: str = typer.Option(
        "http://localhost:8000",
        "--base-url",
        "-u",
        help="Base URL of the running Dazzle instance",
    ),
    email: str = typer.Option(
        None,
        "--email",
        "-e",
        help="Admin email for authentication",
    ),
    password: str = typer.Option(
        None,
        "--password",
        "-p",
        help="Admin password for authentication",
    ),
    data_dir: Path | None = typer.Option(
        None,
        "--data-dir",
        "-d",
        help="Path to seed data directory (auto-detected if omitted)",
    ),
    project_root: Path = typer.Option(
        Path("."),
        "--project",
        help="Project root directory",
    ),
    yes: bool = typer.Option(
        False,
        "--yes",
        "-y",
        help="Skip confirmation prompt",
    ),
) -> None:
    """Clear all entity data and reload from seed files.

    WARNING: This deletes all existing data in reverse dependency order,
    then reloads from seed files.
    """
    from dazzle.demo_data.loader import DemoDataLoader, topological_sort_entities

    project_root = project_root.resolve()

    if not yes:
        confirm = typer.confirm("This will DELETE all entity data and reload. Continue?")
        if not confirm:
            raise typer.Abort()

    # Parse DSL
    typer.echo("Parsing DSL...")
    try:
        from dazzle.cli.utils import load_project_appspec

        appspec = load_project_appspec(project_root)
    except Exception as e:
        typer.echo(f"Failed to parse DSL: {e}", err=True)
        raise typer.Exit(1)

    # Find data directory
    if data_dir is None:
        data_dir = _find_data_dir(project_root)
        if data_dir is None:
            typer.echo("No demo data directory found.", err=True)
            raise typer.Exit(1)

    entity_order = topological_sort_entities(appspec.domain.entities)

    with DemoDataLoader(base_url=base_url, email=email, password=password) as loader:
        if email and password:
            typer.echo("Authenticating...")
            try:
                loader.authenticate()
            except RuntimeError as e:
                typer.echo(f"Authentication failed: {e}", err=True)
                raise typer.Exit(1)

        # Delete in reverse order (children first)
        client = loader._get_client()
        typer.echo("Deleting existing data (reverse dependency order)...")
        for entity_name in reversed(entity_order):
            from dazzle.core.strings import to_api_plural

            endpoint = f"/{to_api_plural(entity_name)}"
            try:
                resp = client.delete(endpoint, headers=loader._headers())
                if resp.status_code in (200, 204):
                    typer.echo(f"  Cleared {entity_name}")
                elif resp.status_code == 404:
                    typer.echo(f"  {entity_name}: no delete endpoint")
                else:
                    typer.echo(f"  {entity_name}: HTTP {resp.status_code}")
            except Exception as e:
                typer.echo(f"  {entity_name}: {e}")

        # Reload
        typer.echo("\nReloading demo data...")
        report = loader.load_all(data_dir, entity_order)

    typer.echo("")
    typer.echo(report.summary())

    if report.total_failed > 0:
        raise typer.Exit(1)


@demo_app.command(name="propose")
def propose_command(
    domain_description: str = typer.Option(
        "",
        "--domain",
        "-d",
        help="Domain description (e.g. 'solar energy installer')",
    ),
    tenant_count: int = typer.Option(
        2,
        "--tenants",
        "-t",
        help="Number of demo tenants to generate",
    ),
    entities: str | None = typer.Option(
        None,
        "--entities",
        "-e",
        help="Comma-separated entity names to include (default: all)",
    ),
    include_metadata: bool = typer.Option(
        True,
        "--metadata/--no-metadata",
        help="Include tenant and persona metadata",
    ),
    quick_mode: bool = typer.Option(
        False,
        "--quick",
        "-q",
        help="Quick mode: only entities with surfaces/references",
    ),
    project_root: Path = typer.Option(
        Path("."),
        "--project",
        help="Project root directory",
    ),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
) -> None:
    """Analyze DSL and propose a Demo Data Blueprint."""
    from dazzle.cli._output import format_output
    from dazzle.mcp.server.handlers.demo_data import demo_propose_impl

    project_root = project_root.resolve()

    filter_entities: list[str] | None = None
    if entities:
        filter_entities = [e.strip() for e in entities.split(",")]

    try:
        result = demo_propose_impl(
            project_root,
            domain_description=domain_description,
            tenant_count=tenant_count,
            filter_entities=filter_entities,
            include_metadata=include_metadata,
            quick_mode=quick_mode,
        )
    except Exception as e:
        typer.echo(f"Error: {e}", err=True)
        raise typer.Exit(code=1)

    typer.echo(format_output(result, as_json=json_output))

    # Generate test_personas.toml for journey testing
    _generate_test_personas_toml(project_root, result)


@demo_app.command(name="save")
def save_command(
    blueprint_file: Path = typer.Option(
        ...,
        "--blueprint-file",
        "-f",
        help="Path to JSON file containing the blueprint data",
    ),
    merge_entities: bool = typer.Option(
        False,
        "--merge",
        help="Merge new entities with existing blueprint",
    ),
    validate_coverage: bool = typer.Option(
        True,
        "--validate/--no-validate",
        help="Validate entity coverage against DSL",
    ),
    project_root: Path = typer.Option(
        Path("."),
        "--project",
        help="Project root directory",
    ),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
) -> None:
    """Save a Demo Data Blueprint to .dazzle/demo_data/blueprint.json."""
    import json

    from dazzle.cli._output import format_output
    from dazzle.mcp.server.handlers.demo_data import demo_save_impl

    project_root = project_root.resolve()

    blueprint_path = Path(blueprint_file).resolve()
    if not blueprint_path.exists():
        typer.echo(f"File not found: {blueprint_path}", err=True)
        raise typer.Exit(code=1)

    try:
        blueprint_data = json.loads(blueprint_path.read_text())
    except (json.JSONDecodeError, OSError) as e:
        typer.echo(f"Error reading blueprint file: {e}", err=True)
        raise typer.Exit(code=1)

    try:
        result = demo_save_impl(
            project_root,
            blueprint_data=blueprint_data,
            merge_entities=merge_entities,
            validate_coverage=validate_coverage,
        )
    except Exception as e:
        typer.echo(f"Error: {e}", err=True)
        raise typer.Exit(code=1)

    typer.echo(format_output(result, as_json=json_output))


@demo_app.command(name="generate")
def generate_command(
    output_format: str = typer.Option(
        "csv",
        "--format",
        help="Output format (csv or json)",
    ),
    output_dir: str = typer.Option(
        "demo_data",
        "--output-dir",
        "-o",
        help="Output directory relative to project root",
    ),
    entities: str | None = typer.Option(
        None,
        "--entities",
        "-e",
        help="Comma-separated entity names to generate (default: all)",
    ),
    project_root: Path = typer.Option(
        Path("."),
        "--project",
        help="Project root directory",
    ),
    json_output: bool = typer.Option(False, "--json", help="Output as JSON"),
) -> None:
    """Generate demo data files from the blueprint."""
    from dazzle.cli._output import format_output
    from dazzle.mcp.server.handlers.demo_data import demo_generate_impl

    project_root = project_root.resolve()

    filter_entities: list[str] | None = None
    if entities:
        filter_entities = [e.strip() for e in entities.split(",")]

    try:
        result = demo_generate_impl(
            project_root,
            output_format=output_format,
            output_dir=output_dir,
            filter_entities=filter_entities,
        )
    except Exception as e:
        typer.echo(f"Error: {e}", err=True)
        raise typer.Exit(code=1)

    typer.echo(format_output(result, as_json=json_output))
