"""
Module: dev_command
Description: Development session with local sandbox server and cloud execution

Implements:
    - docs/cli/reference.md#huitzo-dev
    - docs/guides/developer-environment.md#development-session-flow
"""

import asyncio
import logging
import site
import subprocess
import sys
import threading
import time

import typer
from rich.console import Console

from huitzo_cli.auth import AuthClient
from huitzo_cli.config import ConfigManager
from huitzo_cli.errors import AuthenticationError, NetworkError, ServerError
from huitzo_cli.update_checker import collect_and_warn, should_skip, start_update_check
from huitzo_cli.validator import PackValidator

console = Console()
logger = logging.getLogger(__name__)


def dev(
    docs: bool = typer.Option(False, "--docs", help="Start docs server"),
    port: int = typer.Option(8080, "--port", help="Sandbox port"),
    repl: bool = typer.Option(
        True, "--repl/--no-repl", help="Start interactive REPL for command testing"
    ),
    no_tls: bool = typer.Option(False, "--no-tls", help="Run sandbox without TLS (disables auth)"),
    use_platform: bool = typer.Option(
        False,
        "--use-platform",
        help="Enable real platform services (LLM, HTTP, files) using local API keys",
    ),
    skip_update_check: bool = typer.Option(
        False,
        "--skip-update-check",
        help="Skip checking for outdated dependencies",
    ),
) -> None:
    """Start pack development session with local sandbox.

    1. Validates pack structure
    2. Installs pack in editable mode
    3. Discovers commands from entry_points
    4. Starts HTTPS sandbox at localhost:{port}
    5. Optionally starts docs server at localhost:8124
    6. Starts interactive REPL (default) or blocks until Ctrl+C

    Args:
        docs: Start local docs server
        port: Local sandbox port
        repl: Start interactive REPL (default True)
    """
    # Require authentication — validate token before proceeding
    config = ConfigManager()
    auth = AuthClient(config)
    valid, error_msg = auth.validate_token()
    if not valid:
        console.print(f"[red]{error_msg}[/red]")
        raise typer.Exit(1)
    token = auth.get_token()
    assert token is not None  # validate_token() guarantees a token exists

    # Start background update check
    update_thread = (
        None if should_skip(skip_update_check) else start_update_check(["huitzo", "huitzo-sdk"])
    )

    # Validate pack
    console.print("[bold]Validating pack...[/bold]")
    validator = PackValidator()
    result = validator.validate()

    if not result.valid:
        console.print("[red]Pack validation failed[/red]")
        for error in result.errors:
            console.print(f"  {error}")
        raise typer.Exit(1)

    console.print("[green]Pack validation passed[/green]")

    # Display update warnings (non-blocking)
    if update_thread is not None:
        collect_and_warn(update_thread, console)

    # Editable install
    console.print("[bold]Installing pack in editable mode...[/bold]")
    install_result = subprocess.run(
        [sys.executable, "-m", "pip", "install", "-e", "."],
        capture_output=True,
        text=True,
        check=False,
    )
    if install_result.returncode != 0:
        # Truncate pip stderr to last 10 lines; log full output at DEBUG
        stderr_text = install_result.stderr or ""
        logger.debug("Full pip stderr:\n%s", stderr_text)
        truncated = "\n".join(stderr_text.splitlines()[-10:])
        console.print(f"[red]Editable install failed:[/red]\n{truncated}")
        raise typer.Exit(1)

    console.print("[green]Pack installed[/green]")

    # Re-process .pth files so the parent process sees paths added by
    # `pip install -e .` (which wrote .pth files that only take effect at
    # interpreter startup).  Without this, discover_commands() fails with
    # ModuleNotFoundError because sys.path doesn't include the pack's src/.
    for sp in site.getsitepackages():
        site.addsitedir(sp)
    user_sp = site.getusersitepackages()
    if user_sp:
        site.addsitedir(user_sp)

    # Start sandbox
    from huitzo_cli.sandbox.proxy import LocalSandbox

    sandbox = LocalSandbox(auth_token=token, use_platform=use_platform)
    use_tls = not no_tls
    commands = sandbox.discover_commands()

    # Surface discovery failures
    failures = sandbox.get_discovery_failures()
    if failures:
        console.print(f"[yellow]Warning: {len(failures)} command(s) failed to load:[/yellow]")
        for failure in failures:
            console.print(f"  [yellow]{failure}[/yellow]")

    if not commands:
        console.print(
            "[yellow]No commands found. Check your entry_points in pyproject.toml.[/yellow]"
        )
    else:
        console.print(f"[bold]Discovered {len(commands)} command(s):[/bold]")
        for name, meta in commands.items():
            console.print(f"  {meta.get('namespace', name)} - {meta.get('description', '')[:60]}")

    try:
        base_url = sandbox.start(port=port, use_tls=use_tls)
    except RuntimeError as exc:
        msg = str(exc)
        if "crashed during startup" in msg or "did not start" in msg:
            console.print(
                f"\n[red]Could not start sandbox on port {port}.[/red]\n"
                f"The port may already be in use. Try: [bold]huitzo pack dev --port {port + 1}[/bold]"
            )
        else:
            console.print(f"\n[red]Sandbox failed to start: {msg}[/red]")
        raise typer.Exit(1)

    console.print(f"\n[green]Sandbox ready at {base_url}[/green]")
    if base_url.startswith("https"):
        console.print("[dim]Note: Uses a self-signed certificate (browser warnings expected)[/dim]")
    console.print("  GET  /commands      - List available commands")
    console.print("  POST /commands/NAME - Execute a command")
    console.print("  GET  /health        - Health check")

    if use_platform:
        import os

        console.print("\n[bold]Platform services enabled:[/bold]")
        console.print("  [green]LLM[/green]   - OpenAI/Anthropic (auto-detected from model name)")
        console.print("  [green]HTTP[/green]  - Unrestricted outbound requests")
        console.print("  [green]Files[/green] - Local filesystem (temp directory)")

        has_openai = bool(os.environ.get("OPENAI_API_KEY"))
        has_anthropic = bool(os.environ.get("ANTHROPIC_API_KEY"))
        if has_openai or has_anthropic:
            keys = []
            if has_openai:
                keys.append("OPENAI_API_KEY")
            if has_anthropic:
                keys.append("ANTHROPIC_API_KEY")
            console.print(f"  [dim]API keys detected: {', '.join(keys)}[/dim]")
        else:
            console.print(
                "  [yellow]No API keys detected. Set OPENAI_API_KEY or ANTHROPIC_API_KEY "
                "to use ctx.llm[/yellow]"
            )
    else:
        console.print(
            "\n[dim]Platform services stubbed. Use --use-platform to enable LLM, HTTP, files.[/dim]"
        )

    # Use the sandbox's active auth token (may have been auto-generated)
    active_token = sandbox._auth_token or token
    truncated_token = active_token[:8] + "..." if len(active_token) > 8 else active_token
    console.print("\n[bold]Auth token for this session:[/bold]")
    console.print(f'  export HUITZO_DEV_TOKEN="{truncated_token}"', soft_wrap=True)
    console.print("[dim]Full token: huitzo config get --reveal auth.token[/dim]")
    console.print("\n[dim]Example:[/dim]")
    console.print(
        f'  curl -k -H "Authorization: Bearer $HUITZO_DEV_TOKEN" {base_url}/commands',
        soft_wrap=True,
    )

    # Register dev session — mandatory (no offline mode)
    from huitzo_cli.session_heartbeat import SessionHeartbeat

    api_url = auth.get_api_url()
    # Derive pack namespace from first discovered command
    pack_namespace = "unknown"
    if commands:
        first_meta = next(iter(commands.values()))
        ns = first_meta.get("namespace", "")
        # namespace format: "@org/pack/command" — take first 2 parts
        parts = ns.split("/")
        if len(parts) >= 2:
            pack_namespace = f"{parts[0]}/{parts[1]}"
        elif ns:
            pack_namespace = ns

    freeze_event = threading.Event()
    heartbeat = SessionHeartbeat(
        api_url=api_url,
        auth_token=token,
        auth_client=auth,
        freeze_event=freeze_event,
    )
    try:
        session_id = heartbeat.register(
            pack_namespace=pack_namespace,
            port=port,
            host="localhost",
            command_count=len(commands),
        )
    except AuthenticationError as e:
        console.print(f"[red]Authentication failed: {e.user_message}[/red]")
        sandbox.stop()
        raise typer.Exit(1)
    except NetworkError as e:
        console.print(f"[red]Cannot reach backend: {e.user_message}[/red]")
        console.print("[dim]Check your connection and API URL.[/dim]")
        sandbox.stop()
        raise typer.Exit(1)
    except ServerError as e:
        console.print(f"[red]Server error: {e.user_message}[/red]")
        sandbox.stop()
        raise typer.Exit(1)

    heartbeat.start()
    console.print(f"[dim]Session registered: {session_id[:8]}...[/dim]")

    # Start docs server if requested
    docs_server = None
    if docs:
        try:
            from huitzo_cli.docs_server.server import DocsServer

            docs_server = DocsServer(port=8124)
            docs_server.start()
            console.print("\n[green]Docs server at http://localhost:8124[/green]")
        except (OSError, ImportError) as e:
            console.print(f"[yellow]Could not start docs server: {e}[/yellow]")

    # Start freeze monitor — watches for token refresh failure
    def _freeze_monitor() -> None:
        freeze_event.wait()
        sandbox.freeze()
        console.print(
            "\n[bold red]SESSION FROZEN[/bold red]\n"
            "Token refresh failed. Sandbox is returning 503.\n"
            "  1. Run [bold]huitzo login[/bold] in another terminal\n"
            "  2. Press Ctrl+C to stop this session\n"
            "  3. Restart with [bold]huitzo pack dev[/bold]"
        )

    freeze_thread = threading.Thread(target=_freeze_monitor, daemon=True, name="huitzo-freeze")
    freeze_thread.start()

    # Use try/finally to guarantee cleanup on any exit path
    try:
        if repl:
            from huitzo_cli.repl.session import REPLSession

            # Pass the CA cert path for TLS verification when available
            repl_verify: str | bool = sandbox._ca_cert_path or (not use_tls)
            session = REPLSession(
                base_url,
                auth_token=active_token,
                console=console,
                verify=repl_verify,
            )
            try:
                asyncio.run(session.run())
            except (KeyboardInterrupt, EOFError):
                console.print("\nGoodbye!")
        else:
            console.print("\nPress Ctrl+C to stop...")
            while True:
                if freeze_event.is_set():
                    # Block until Ctrl+C after freeze
                    time.sleep(1)
                time.sleep(1)
    except (KeyboardInterrupt, SystemExit):
        pass
    finally:
        console.print("\n[yellow]Shutting down...[/yellow]")
        heartbeat.stop()
        sandbox.stop()
        if docs_server:
            docs_server.stop()
