"""Unified CLI for Proxym — AI proxy management.

Top-level commands (serve, status, models, chat, logs, providers, routing-test,
projects) are defined here.  All group commands (accounts, vm, browser, vscode,
ticket, sprint, context, tests, diagnose) live in proxym.cli._groups/*.

Command handlers (business logic) live in proxym.cli/ sub-modules:
  - system.py      — serve, status, models
  - accounts.py    — accounts list/add/get/costs/delete/bind-tool
  - vms.py         — vm list/create/action/switch/open/ssh/logs/delete/bulk-delete/console
  - browser.py     — browser list/assign/sync/snapshot
  - vscode.py      — vscode start/stop/open/logs/status/build
  - tickets.py     — ticket & sprint management
  - context.py     — context delta/detect/stats, sessions, mcp
  - diagnostics.py — system tests & diagnostics
"""

from __future__ import annotations

from types import SimpleNamespace

import click

from proxym.config import get_settings
from proxym.cli import chat as chat_cli
from proxym.cli import system as system_cli


def _make_args(ctx: click.Context, **kwargs) -> SimpleNamespace:
    """Create Namespace from ctx.obj with optional overrides."""
    d = dict(ctx.obj) if ctx.obj else {}
    d.update(kwargs)
    return SimpleNamespace(**d)


@click.group(invoke_without_command=True)
@click.option("--proxy", default=None, help="Proxy URL")
@click.option("--key", default=None, help="API key")
@click.option("--format", "fmt", default="table", type=click.Choice(["table", "yaml"]), help="Output format")
@click.pass_context
def cli(ctx: click.Context, proxy: str | None, key: str | None, fmt: str):
    """Proxym — intelligent AI proxy with VM isolation."""
    if ctx.invoked_subcommand is None:
        click.echo(ctx.get_help())
        ctx.exit(0)
    settings = get_settings()
    ctx.ensure_object(dict)
    ctx.obj["proxy"] = proxy or settings.default_proxy_url
    ctx.obj["key"] = key or settings.default_api_key
    ctx.obj["format"] = fmt


# ── Top-level commands ────────────────────────────────────────

@cli.command(name="serve")
@click.option("--host", default=None, help="Bind host")
@click.option("--port", type=int, default=None, help="Bind port")
@click.option("--reload", is_flag=True, help="Auto-reload on changes")
def serve(host: str | None, port: int | None, reload: bool):
    """Start the proxy server."""
    args = SimpleNamespace(host=host, port=port, reload=reload)
    system_cli.cmd_serve(args)


@cli.command(name="status")
@click.option("--format", "fmt", default="table", type=click.Choice(["table", "yaml"]))
@click.pass_context
def status(ctx: click.Context, fmt: str):
    """Show system status."""
    args = _make_args(ctx, format=fmt)
    system_cli.cmd_status(args)


@cli.command(name="models")
@click.option("--format", "fmt", default="table", type=click.Choice(["table", "yaml"]))
@click.pass_context
def models(ctx: click.Context, fmt: str):
    """List available models."""
    args = _make_args(ctx, format=fmt)
    system_cli.cmd_models(args)


@cli.command(name="chat")
@click.option("--voice", is_flag=True, help="Use microphone input (Whisper STT)")
@click.option("--tts", is_flag=True, help="Enable text-to-speech responses")
@click.pass_context
def chat(ctx: click.Context, voice: bool, tts: bool):
    """Interactive chat — DSL first, LLM fallback."""
    args = _make_args(ctx, voice=voice, tts=tts)
    chat_cli.cmd_chat(args)


@cli.command(name="logs")
@click.option("-n", "--lines", default=50, type=int, help="Number of log lines")
@click.option("--level", default=None, help="Filter by level (debug|info|warning|error)")
@click.pass_context
def logs_cmd(ctx: click.Context, lines: int, level: str | None):
    """Show proxy logs."""
    from proxym.cli._client import make_client, print_json
    args = _make_args(ctx)
    c = make_client(args.proxy, args.key)
    params: dict = {"lines": lines}
    if level:
        params["level"] = level
    r = c.get("/dashboard/logs", params=params)
    data = r.json()
    if args.format == "yaml":
        print_json(data)
        return
    for entry in data.get("logs", []):
        ts = entry.get("timestamp", "")[:19]
        lvl = entry.get("level", "INFO").upper()
        msg = entry.get("message", "")
        click.echo(f"  {ts}  {lvl:7s}  {msg}")


@cli.command(name="providers")
@click.pass_context
def providers_cmd(ctx: click.Context):
    """List configured providers and their status."""
    from proxym.cli._client import make_client, print_json
    args = _make_args(ctx)
    c = make_client(args.proxy, args.key)
    r = c.get("/dashboard/providers")
    data = r.json()
    if args.format == "yaml":
        print_json(data)
        return
    for p in data.get("providers", []):
        icon = "\033[32m\u2713\033[0m" if p.get("has_key") else "\033[31m\u2717\033[0m"
        click.echo(f"  {icon}  {p['name']:<15s}  models: {p.get('model_count', 0)}")


@cli.command(name="routing-test")
@click.pass_context
def routing_test_cmd(ctx: click.Context):
    """Dry-run routing test — check which model would be selected."""
    from proxym.cli._client import make_client, print_json
    args = _make_args(ctx)
    c = make_client(args.proxy, args.key)
    r = c.get("/api/v1/routing/test")
    data = r.json()
    if args.format == "yaml":
        print_json(data)
        return
    summary = data.get("summary", {})
    click.echo(f"\n  Routing: {summary.get('ok', 0)}/{summary.get('total', 0)} aliases OK\n")
    for alias, info in data.get("routing", {}).items():
        st = info.get("status", "?")
        icon = "\033[32m\u2713\033[0m" if st == "ok" else "\033[31m\u2717\033[0m"
        click.echo(f"  {icon}  {alias:<12s} -> {info.get('model', '?'):<35s}  [{info.get('provider', '?')}]")
    click.echo()


@cli.command(name="projects")
@click.pass_context
def projects_cmd(ctx: click.Context):
    """List available projects (shortcut for 'project list')."""
    from proxym.cli._client import make_client, print_json, print_table
    args = _make_args(ctx)
    with make_client(args.proxy, args.key) as c:
        r = c.get("/dashboard/projects")
        data = r.json()
    projects = data if isinstance(data, list) else data.get("projects", [])
    if args.format == "yaml":
        print_json(data)
        return
    click.echo(f"\n  Projects ({len(projects)})\n")
    if not projects:
        click.echo("  No projects found.\n")
        return
    for p in projects:
        icon = {"local": "\U0001f4c1", "git": "\U0001f517", "ssh": "\U0001f5a5", "ftp": "\U0001f4e1"}.get(p.get("source_type"), "\U0001f4c1")
        stack = ", ".join(p.get("stack", []))
        tool = f"  tool={p['default_tool']}" if p.get("default_tool") else ""
        click.echo(f"  {icon} {p['name']:<20s} {p.get('source_type', 'local'):<6s} {stack}{tool}")


# ── Register group commands from _groups/ ─────────────────────

from proxym.cli._groups.accounts_ctl import accounts       # noqa: E402
from proxym.cli._groups.vms_ctl import vm                  # noqa: E402
from proxym.cli._groups.browser_ctl import browser         # noqa: E402
from proxym.cli._groups.vscode_ctl import vscode           # noqa: E402
from proxym.cli._groups.tickets_ctl import ticket, sprint  # noqa: E402
from proxym.cli._groups.projects_ctl import project        # noqa: E402
from proxym.cli._groups.diagnostics_ctl import tests, diagnose  # noqa: E402
from proxym.cli._groups.context_ctl import context         # noqa: E402
from proxym.cli._groups.observability_ctl import obs       # noqa: E402
from proxym.cli._groups.tools_ctl import tools             # noqa: E402
from proxym.cli._groups.do_ctl import do_cmd               # noqa: E402

cli.add_command(accounts)
cli.add_command(vm)
cli.add_command(browser)
cli.add_command(vscode)
cli.add_command(ticket)
cli.add_command(sprint)
cli.add_command(project)
cli.add_command(tests)
cli.add_command(diagnose)
cli.add_command(context)
cli.add_command(obs)
cli.add_command(tools)
cli.add_command(do_cmd)


# ── Convenience aliases ──────────────────────────────────────

@cli.command("fix")
@click.argument("description")
@click.option("--on", "project_name", default=None, help="Project name")
@click.pass_context
def fix_cmd(ctx, description, project_name):
    """Shortcut: fix a bug.  = proxym do "..." --type bug"""
    ctx.invoke(do_cmd, task=description, project_name=project_name, task_type="bug")


@cli.command("refactor")
@click.argument("description")
@click.option("--on", "project_name", default=None, help="Project name")
@click.pass_context
def refactor_cmd(ctx, description, project_name):
    """Shortcut: refactor code.  = proxym do "..." --type refactor --with claude-code"""
    ctx.invoke(do_cmd, task=description, project_name=project_name,
               task_type="refactor", tool_name="claude-code")


@cli.command("q")
@click.pass_context
def queue_shortcut(ctx):
    """Shortcut: show task queue.  = proxym tools queue"""
    from proxym.cli._client import make_client, print_json
    args = _make_args(ctx)
    c = make_client(args.proxy, args.key)
    r = c.get("/dashboard/tools/queue")
    data = r.json()
    if args.format == "yaml":
        print_json(data)
        return
    click.echo(f"\n  Queue: {data.get('queue_size', 0)} pending  |  "
               f"Total: {data.get('total_jobs', 0)}  |  "
               f"{'● Running' if data.get('running') else '○ Stopped'}")
    by = data.get("by_status", {})
    if by:
        parts = [f"{k}: {v}" for k, v in by.items()]
        click.echo(f"  Status: {', '.join(parts)}")
    for j in (data.get("recent_jobs") or [])[:5]:
        icon = {"done": "✅", "failed": "❌", "running": "⏳", "queued": "○"}.get(j.get("status"), "?")
        click.echo(f"  {icon} {j.get('id','?'):8s}  {j.get('tool','?'):12s}  {j.get('status','?')}")
    click.echo()


@cli.command("web")
@click.argument("tab", required=False, default="")
@click.pass_context
def web_cmd(ctx, tab):
    """Open dashboard in browser.  proxym web [home|projects|tasks|system|voice]"""
    import webbrowser
    args = _make_args(ctx)
    url = f"{args.proxy}/dashboard-ui"
    if tab:
        url += f"#{tab}"
    webbrowser.open(url)
    click.echo(f"  Opening {url}")


@cli.command("log")
@click.option("-n", "--lines", default=20, type=int)
@click.pass_context
def log_shortcut(ctx, lines):
    """Shortcut: show recent logs.  = proxym logs -n 20"""
    ctx.invoke(logs_cmd, lines=lines, level=None)


# CLI entry point
main = cli
