"""CLI entry point for exokit.

Provides three commands:
- ``init``   — Interactive wizard to scaffold a new demo project
- ``validate`` — Check prerequisites for demo development
- ``update-skills`` — Refresh bundled skills from latest ai-dev-kit

Uses questionary for interactive prompts and Rich for output formatting.
"""

from __future__ import annotations

from pathlib import Path

import questionary
import typer
from questionary import Choice, Style
from rich.console import Console
from rich.panel import Panel
from rich.table import Table

from exokit.core.apx_bridge import APXNotInstalledError
from exokit.core.apx_bridge import scaffold_app as apx_scaffold_app
from exokit.core.deployer import deploy_hello_world
from exokit.core.models import PrereqResult, QuestionnaireAnswers, ScaffoldContext
from exokit.core.prereqs import check_all
from exokit.core.questionnaire import QUESTIONS, build_context, compute_default
from exokit.core.scaffold import generate
from exokit.core.skills import bundle_skills, generate_mcp_config
from exokit.core.skills import update_skills as core_update_skills

cli_app = typer.Typer(
    name="exokit",
    help="Databricks Demo Template Generator",
    no_args_is_help=True,
    rich_markup_mode="rich",
)
console = Console()

# Style matching Databricks aesthetic
STYLE = Style(
    [
        ("qmark", "fg:#e55c3a bold"),
        ("question", "bold"),
        ("answer", "fg:#2ecc71 bold"),
        ("pointer", "fg:#e55c3a bold"),
        ("highlighted", "fg:#e55c3a bold"),
        ("selected", "fg:#2ecc71"),
        ("instruction", "fg:#858585 italic"),
    ]
)

INDUSTRY_CHOICES = [
    Choice("Financial Services (FSI)", value="fsi"),
    Choice("Telecommunications", value="telecom"),
    Choice("Retail / E-commerce", value="retail"),
    Choice("Healthcare & Life Sciences", value="healthcare"),
    Choice("Manufacturing & IoT", value="manufacturing"),
    Choice("Energy & Utilities", value="energy"),
    Choice("Media & Entertainment", value="media"),
    Choice("Public Sector", value="public_sector"),
]


# ---------------------------------------------------------------------------
# Detect Databricks profiles for autocomplete
# ---------------------------------------------------------------------------


def _detect_databricks_profiles() -> list[str]:
    """Detect available Databricks CLI profiles from ~/.databrickscfg."""
    try:
        import configparser

        cfg = configparser.ConfigParser()
        cfg.read(Path.home() / ".databrickscfg")
        profiles = list(cfg.sections())
        if cfg.defaults():
            profiles.insert(0, "DEFAULT")
        return profiles
    except Exception:  # noqa: BLE001
        return []


def _discover_from_profile(
    profile: str,
) -> tuple[str | None, list[dict[str, str]], str | None]:
    """Auto-detect workspace URL, warehouses, and user email from a profile.

    Returns:
        Tuple of (workspace_host, warehouses_list, user_email).
        Any value is None/empty if detection fails.
    """
    try:
        from databricks.sdk import WorkspaceClient

        w = WorkspaceClient(profile=profile)
        host = w.config.host

        warehouses: list[dict[str, str]] = []
        try:
            for wh in w.warehouses.list():
                warehouses.append(
                    {
                        "id": wh.id or "",
                        "name": wh.name or "",
                        "state": str(wh.state.value) if wh.state else "UNKNOWN",
                    }
                )
        except Exception:  # noqa: BLE001, S110
            pass  # Warehouse listing is optional — fails silently

        user_email: str | None = None
        try:
            me = w.current_user.me()
            user_email = me.user_name
        except Exception:  # noqa: BLE001, S110
            pass  # User email detection is optional

        return host, warehouses, user_email
    except Exception:  # noqa: BLE001
        return None, [], None


# ---------------------------------------------------------------------------
# Display helpers
# ---------------------------------------------------------------------------


def _display_prereq_results(results: list[PrereqResult]) -> None:
    """Render prerequisite check results as a Rich table."""
    table = Table(show_header=True, header_style="bold", padding=(0, 2))
    table.add_column("Tool", style="bold")
    table.add_column("Status", justify="center")
    table.add_column("Version")
    table.add_column("Details", style="dim")
    for r in results:
        status = "[green]PASS[/green]" if r.passed else "[red]FAIL[/red]"
        table.add_row(r.name, status, r.version or "—", r.message)
    console.print(table)


# ---------------------------------------------------------------------------
# Interactive wizard
# ---------------------------------------------------------------------------


def _run_wizard() -> QuestionnaireAnswers | None:
    """Run the interactive questionnaire and return answers, or None if cancelled."""
    console.print()
    console.print(
        Panel(
            "[bold]Configure your new demo project[/bold]\n\n"
            "Answer each question below. Press [bold]Ctrl+C[/bold] to cancel anytime.",
            style="blue",
            padding=(1, 2),
        )
    )
    console.print()

    answers: dict[str, str] = {}
    profiles = _detect_databricks_profiles()

    try:
        # 1. Company (optional)
        company = questionary.text(
            "Company name:",
            instruction="(press Enter to skip for a generic industry demo)",
            style=STYLE,
        ).ask()
        if company is None:
            return None
        answers["company"] = company.strip()

        # 2. Industry
        industry = questionary.select(
            "Industry vertical:",
            choices=INDUSTRY_CHOICES,
            style=STYLE,
        ).ask()
        if industry is None:
            return None
        answers["industry"] = industry

        # 3. Demo description
        console.print(
            "\n[bold]Describe your demo:[/bold] [dim]What should this demo cover? "
            "Scope, departments, use cases, special requirements. "
            "Claude will use this to plan everything.[/dim]\n"
        )
        description = questionary.text(
            "Demo description:",
            instruction="(e.g., 'Comprehensive FSI demo spanning all departments...')",
            validate=lambda x: True if x.strip() else "Please describe what you need",
            style=STYLE,
        ).ask()
        if description is None:
            return None
        answers["demo_description"] = description.strip()

        # 4. Databricks profile → auto-detect workspace + warehouses
        if profiles:
            profile = questionary.select(
                "Databricks CLI profile:",
                choices=profiles,
                style=STYLE,
            ).ask()
        else:
            profile = questionary.text(
                "Databricks CLI profile:",
                default="DEFAULT",
                instruction="(no profiles found in ~/.databrickscfg)",
                style=STYLE,
            ).ask()
        if profile is None:
            return None
        answers["databricks_profile"] = profile.strip()

        # Auto-detect workspace URL, warehouses, and email from profile
        console.print("  [dim]Connecting to workspace...[/dim]")
        workspace_host, warehouses, user_email = _discover_from_profile(
            answers["databricks_profile"]
        )

        if workspace_host:
            console.print(f"  [green]Workspace:[/green] {workspace_host}")
            answers["workspace_host"] = workspace_host
        else:
            # Fallback to manual input
            workspace = questionary.text(
                "Databricks workspace URL:",
                instruction="(could not auto-detect — enter manually)",
                validate=lambda x: (
                    True if x.strip().startswith("https://") else "Must start with https://"
                ),
                style=STYLE,
            ).ask()
            if workspace is None:
                return None
            answers["workspace_host"] = workspace.strip()

        # 5. SQL Warehouse — select from list or enter manually
        if warehouses:
            warehouse_choices = [
                Choice(
                    f"{w['name']} ({w['id']}) — {w['state']}",
                    value=w["id"],
                )
                for w in warehouses
            ]
            warehouse_id = questionary.select(
                "SQL Warehouse:",
                choices=warehouse_choices,
                style=STYLE,
            ).ask()
            if warehouse_id is None:
                return None
            answers["warehouse_id"] = warehouse_id
        else:
            warehouse = questionary.text(
                "SQL Warehouse ID:",
                instruction="(could not list warehouses — enter ID manually)",
                validate=lambda x: True if x.strip() else "Required",
                style=STYLE,
            ).ask()
            if warehouse is None:
                return None
            answers["warehouse_id"] = warehouse.strip()

        # 6. Catalog (auto-computed default)
        default_catalog = compute_default(QUESTIONS[2], answers)
        catalog = questionary.text(
            "Unity Catalog name:",
            default=default_catalog or "",
            validate=lambda x: True if x.strip() else "Required",
            style=STYLE,
        ).ask()
        if catalog is None:
            return None
        answers["catalog"] = catalog.strip()

        # 7. Email (pre-filled from SDK if available)
        email = questionary.text(
            "Notification email:",
            default=user_email or "",
            instruction="(for job failure alerts)",
            validate=lambda x: True if "@" in x else "Must be a valid email",
            style=STYLE,
        ).ask()
        if email is None:
            return None
        answers["notification_email"] = email.strip()

    except KeyboardInterrupt:
        return None

    return QuestionnaireAnswers(
        company=answers["company"],
        industry=answers["industry"],
        demo_description=answers["demo_description"],
        catalog=answers["catalog"],
        warehouse_id=answers["warehouse_id"],
        workspace_host=answers["workspace_host"],
        databricks_profile=answers.get("databricks_profile") or None,
        lakebase_url=None,
        notification_email=answers["notification_email"],
    )


# ---------------------------------------------------------------------------
# Catalog creation
# ---------------------------------------------------------------------------


def _create_catalog(context: ScaffoldContext) -> None:
    """Create Unity Catalog on Databricks.

    The DLT pipeline resource validates that the catalog exists at deploy time,
    so the catalog must exist before ``bundle deploy``. Schemas and volumes are
    created by the ``setup_catalog`` notebook when the data generation job runs.
    """
    try:
        from databricks.sdk import WorkspaceClient
        from databricks.sdk.service.sql import ExecuteStatementRequestOnWaitTimeout

        w = WorkspaceClient(profile=context.databricks_profile or None)
        result = w.statement_execution.execute_statement(
            warehouse_id=context.warehouse_id,
            statement=f"CREATE CATALOG IF NOT EXISTS {context.catalog}",
            wait_timeout="30s",
            on_wait_timeout=ExecuteStatementRequestOnWaitTimeout.CONTINUE,
        )
        if result.status and result.status.error:
            console.print(f"  [yellow]{result.status.error}[/yellow]")
        else:
            console.print(f"  [green]Catalog '{context.catalog}' is ready[/green]")

    except ImportError:
        console.print("  [yellow]Databricks SDK not available — skipping[/yellow]")
    except Exception as exc:  # noqa: BLE001
        console.print(f"  [yellow]Could not create catalog: {exc}[/yellow]")
        console.print("  [dim]The setup_catalog job will create it on first run[/dim]")


# ---------------------------------------------------------------------------
# Scaffold execution
# ---------------------------------------------------------------------------


def _run_scaffold(answers: QuestionnaireAnswers, output_dir: Path) -> None:
    """Execute the full scaffold + deploy pipeline.

    Phase 1 (Scaffold): catalog → templates → APX app → skills → MCP config
    Phase 2 (Deploy): bundle deploy → data gen → pipeline → ML → app → Genie
    """
    context = build_context(answers)

    console.print()
    console.print(
        Panel(
            f"[bold]Company:[/bold]   {context.company}\n"
            f"[bold]Industry:[/bold]  {context.industry}\n"
            f"[bold]Catalog:[/bold]   {context.catalog}\n"
            f"[bold]Bundle:[/bold]    {context.bundle_name}\n"
            f"[bold]App:[/bold]       {context.app_name}\n"
            f"[bold]Location:[/bold]  {output_dir.resolve()}",
            title="Configuration",
            style="blue",
            padding=(1, 2),
        )
    )

    # ===== Phase 1: Scaffold =====
    console.print()
    console.print("[bold]Phase 1: Scaffold[/bold]")

    # 1/5. Create Unity Catalog
    console.print("\n[bold blue]  1/5[/bold blue] Creating Unity Catalog...")
    _create_catalog(context)

    # 2/5. Generate templates
    console.print("\n[bold blue]  2/5[/bold blue] Rendering templates...")
    result = generate(context, output_dir)
    console.print(f"    [green]Created {len(result.files_created)} files[/green]")

    # 3/5. APX app scaffold
    console.print("\n[bold blue]  3/5[/bold blue] Scaffolding Databricks App...")
    app_dir = output_dir / "apps" / "main-app"
    has_app = False
    try:
        apx_scaffold_app(context, app_dir)
        console.print("    [green]App scaffolded with 3-layer architecture[/green]")
        has_app = True
    except APXNotInstalledError:
        console.print(
            "    [yellow]APX not installed — skipping app scaffold.[/yellow]\n"
            "    Install with: [cyan]uv tool install apx[/cyan]"
        )

    # 4/5. Bundle skills
    console.print("\n[bold blue]  4/5[/bold blue] Bundling skills...")
    bundled = bundle_skills(output_dir)
    if bundled:
        console.print(f"    [green]Bundled {len(bundled)} skills[/green]")
    else:
        console.print("    [dim]No skills source found — skipping[/dim]")

    # 5/5. MCP config
    console.print("\n[bold blue]  5/5[/bold blue] Generating MCP configuration...")
    generate_mcp_config(output_dir, databricks_profile=context.databricks_profile)
    console.print("    [green]MCP config created[/green]")

    # ===== Phase 2: Deploy =====
    console.print()
    console.print("[bold]Phase 2: Deploy hello-world end-to-end[/bold]")

    def on_status(step: int, msg: str) -> None:
        console.print(f"\n[bold blue]  {step}/2[/bold blue] {msg}")

    deploy_result = deploy_hello_world(
        output_dir,
        bundle_name=context.bundle_name,
        app_name=context.app_name if has_app else "",
        catalog=context.catalog,
        warehouse_id=context.warehouse_id,
        workspace_host=context.workspace_host,
        target="dev",
        profile=context.databricks_profile,
        on_status=on_status,
    )

    # ===== Summary =====
    console.print()

    # Resource links table
    if deploy_result.resources:
        link_table = Table(
            show_header=True, header_style="bold", padding=(0, 2), title="Deployed Resources"
        )
        link_table.add_column("Resource", style="bold")
        link_table.add_column("Type")
        link_table.add_column("Status", justify="center")
        link_table.add_column("URL", style="cyan")
        for r in deploy_result.resources:
            status_style = {
                "success": "[green]OK[/green]",
                "deployed": "[green]OK[/green]",
                "created": "[green]OK[/green]",
                "failed": "[red]FAIL[/red]",
                "pending": "[yellow]PENDING[/yellow]",
            }.get(r.status, f"[dim]{r.status}[/dim]")
            link_table.add_row(r.name, r.kind, status_style, r.url or "—")
        console.print(link_table)

    # Warnings
    if deploy_result.warnings:
        console.print()
        for w in deploy_result.warnings:
            console.print(f"  [yellow]WARNING:[/yellow] {w}")

    # Final panel
    console.print()
    console.print(
        Panel(
            f"[bold green]Demo project scaffolded and deployed![/bold green]\n\n"
            f"[bold]Location:[/bold]  {output_dir.resolve()}\n"
            f"[bold]Files:[/bold]     {len(result.files_created)}\n"
            f"[bold]Bundle:[/bold]    {context.bundle_name}\n\n"
            f"[bold]Next steps:[/bold]\n"
            f"  1. [cyan]cd {output_dir}[/cyan]\n"
            f"  2. [cyan]claude[/cyan]                  # Open Claude Code\n"
            f"  3. [cyan]/plan[/cyan]                   # Research + design + approve\n"
            f"  4. [cyan]/build-next[/cyan]             # Build wave by wave\n\n"
            f"[dim]The hello-world data, pipeline, ML model, dashboard, app,\n"
            f"and Genie space are already live. /plan will replace them\n"
            f"with industry-specific content.[/dim]",
            title=" Init Complete ",
            style="green",
            padding=(1, 2),
        )
    )


# ---------------------------------------------------------------------------
# Commands
# ---------------------------------------------------------------------------


@cli_app.command()
def init(
    output_dir: Path = typer.Argument(  # noqa: B008
        ..., help="Output directory for the new demo project"
    ),
) -> None:
    """Scaffold a new demo project with an interactive wizard.

    Example: exokit init ~/demos/acme-fsi-demo
    """
    # Safety check
    resolved = output_dir.resolve()
    if (resolved / "src" / "exokit").exists():
        console.print(
            "\n[red]Error:[/red] Output directory is the exokit generator repo.\n"
            f"  Try: exokit init {resolved.parent / 'my-demo'}\n"
        )
        raise typer.Exit(code=1)

    # Prereqs
    console.print("\n[bold]Checking prerequisites...[/bold]")
    results = check_all()
    failed = [r for r in results if not r.passed]
    if failed:
        _display_prereq_results(results)
        console.print(f"\n[yellow]{len(failed)} prerequisite(s) not met.[/yellow]")

    # Wizard
    answers = _run_wizard()
    if answers is None:
        console.print("\n[yellow]Cancelled.[/yellow]")
        raise typer.Exit(code=0)

    # Confirm
    confirm = questionary.confirm("Generate project?", default=True, style=STYLE).ask()
    if not confirm:
        console.print("\n[yellow]Cancelled.[/yellow]")
        raise typer.Exit(code=0)

    _run_scaffold(answers, output_dir)


@cli_app.command()
def validate(
    profile: str | None = typer.Option(None, help="Databricks CLI profile"),  # noqa: B008
) -> None:
    """Check prerequisites for demo development."""
    console.print()
    results = check_all(databricks_profile=profile or "DEFAULT")
    _display_prereq_results(results)
    passed = sum(1 for r in results if r.passed)
    total = len(results)
    console.print()
    if passed == total:
        console.print(f"[bold green]All {total} checks passed![/bold green]")
    else:
        console.print(f"[yellow]{passed}/{total} checks passed.[/yellow]")


@cli_app.command(name="update-skills")
def update_skills(
    project_dir: Path = typer.Argument(default=Path("."), help="Project directory"),  # noqa: B008
    source: Path | None = typer.Option(None, help="Skills source directory"),  # noqa: B008
) -> None:
    """Refresh bundled skills from latest ai-dev-kit."""
    updated = core_update_skills(project_dir, skills_source=source)
    if updated:
        console.print(f"[bold green]Updated {len(updated)} skills:[/bold green]")
        for skill in updated:
            console.print(f"  [green]+[/green] {skill}")
    else:
        console.print("[yellow]No skills updated.[/yellow]")


app = cli_app

if __name__ == "__main__":
    cli_app()
