"""CLI commands for debot."""

import asyncio
from pathlib import Path

import typer
from rich.console import Console
from rich.table import Table

from debot import __logo__, __version__

app = typer.Typer(
    name="debot",
    help=f"{__logo__} debot - Personal AI Assistant",
    no_args_is_help=True,
)

console = Console()


def version_callback(value: bool):
    if value:
        console.print(f"{__logo__} debot v{__version__}")
        raise typer.Exit()


@app.callback()
def main(
    version: bool = typer.Option(None, "--version", "-v", callback=version_callback, is_eager=True),
):
    """debot - Personal AI Assistant."""
    pass


# ============================================================================
# Onboard / Setup
# ============================================================================


@app.command()
def onboard():
    """Initialize debot configuration and workspace."""
    from debot.config.loader import get_config_path, save_config
    from debot.config.schema import Config
    from debot.utils.helpers import get_workspace_path

    config_path = get_config_path()

    if config_path.exists():
        console.print(f"[yellow]Config already exists at {config_path}[/yellow]")
        if not typer.confirm("Overwrite?"):
            raise typer.Exit()

    # Create default config
    config = Config()
    save_config(config)
    console.print(f"[green]✓[/green] Created config at {config_path}")

    # Create workspace
    workspace = get_workspace_path()
    console.print(f"[green]✓[/green] Created workspace at {workspace}")

    # Create default bootstrap files
    _create_workspace_templates(workspace)

    console.print(f"\n{__logo__} debot is ready!")
    console.print("\nNext steps:")
    console.print("  1. Add your API key to [cyan]~/.debot/config.json[/cyan]")
    console.print("     Get one at: https://openrouter.ai/keys")
    console.print('  2. Chat: [cyan]debot agent -m "Hello!"[/cyan]')
    console.print("\n[dim]Want Telegram/WhatsApp? See: https://github.com/BotMesh/debot#-chat-apps[/dim]")


def _create_workspace_templates(workspace: Path):
    """Create default workspace template files."""
    templates = {
        "AGENTS.md": """# Agent Instructions

You are a helpful AI assistant. Be concise, accurate, and friendly.

## Guidelines

- Always explain what you're doing before taking actions
- Ask for clarification when the request is ambiguous
- Use tools to help accomplish tasks
- Remember important information in your memory files
""",
        "SOUL.md": """# Soul

I am debot, a lightweight AI assistant.

## Personality

- Helpful and friendly
- Concise and to the point
- Curious and eager to learn

## Values

- Accuracy over speed
- User privacy and safety
- Transparency in actions
""",
        "USER.md": """# User

Information about the user goes here.

## Preferences

- Communication style: (casual/formal)
- Timezone: (your timezone)
- Language: (your preferred language)
""",
    }

    for filename, content in templates.items():
        file_path = workspace / filename
        if not file_path.exists():
            file_path.write_text(content)
            console.print(f"  [dim]Created {filename}[/dim]")

    # Create memory directory and MEMORY.md
    memory_dir = workspace / "memory"
    memory_dir.mkdir(exist_ok=True)
    memory_file = memory_dir / "MEMORY.md"
    if not memory_file.exists():
        memory_file.write_text("""# Long-term Memory

This file stores important information that should persist across sessions.

## User Information

(Important facts about the user)

## Preferences

(User preferences learned over time)

## Important Notes

(Things to remember)
""")
        console.print("  [dim]Created memory/MEMORY.md[/dim]")


# ---------------------------------------------------------------------------
# Config commands
# ---------------------------------------------------------------------------
config_app = typer.Typer(help="View and edit configuration")
app.add_typer(config_app, name="config")


@config_app.command("compaction")
def config_compaction(
    show: bool = typer.Option(True, "--show/--no-show", help="Show current compaction settings"),
    enabled: bool | None = typer.Option(None, "--enabled/--disabled", help="Enable or disable auto-compaction"),
    keep_last: int | None = typer.Option(None, "--keep-last", "-k", help="Number of recent messages to keep"),
    trigger_ratio: float | None = typer.Option(None, "--trigger-ratio", help="Trigger ratio (0.0-1.0) of model tokens"),
    silent: bool | None = typer.Option(None, "--silent/--no-silent", help="Run compactions silently (no logs)"),
    chars_per_token: int | None = typer.Option(
        None, "--chars-per-token", help="Characters per token for naive estimator"
    ),
):
    """View or update compaction settings in the config file."""
    from debot.config.loader import get_config_path, load_config, save_config

    config_path = get_config_path()
    config = load_config()

    if show:
        defaults = config.agents.defaults
        console.print("Compaction settings:")
        console.print(f"  enabled: {defaults.compaction_enabled}")
        console.print(f"  keep_last: {defaults.compaction_keep_last}")
        console.print(f"  trigger_ratio: {defaults.compaction_trigger_ratio}")
        console.print(f"  silent: {defaults.compaction_silent}")
        console.print(f"  chars_per_token: {defaults.token_chars_per_token}")

    # Update fields if provided
    updated = False
    if enabled is not None:
        config.agents.defaults.compaction_enabled = enabled
        updated = True
    if keep_last is not None:
        config.agents.defaults.compaction_keep_last = keep_last
        updated = True
    if trigger_ratio is not None:
        config.agents.defaults.compaction_trigger_ratio = trigger_ratio
        updated = True
    if silent is not None:
        config.agents.defaults.compaction_silent = silent
        updated = True
    if chars_per_token is not None:
        config.agents.defaults.token_chars_per_token = chars_per_token
        updated = True

    if updated:
        save_config(config, config_path)
        console.print(f"[green]✓[/green] Updated compaction settings in {config_path}")


@config_app.command("compaction-model")
def config_compaction_model(
    model: str = typer.Argument(..., help="Model name (e.g., anthropic/claude-opus-4-5)"),
    show: bool = typer.Option(True, "--show/--no-show", help="Show current model-specific settings"),
    keep_last: int | None = typer.Option(None, "--keep-last", "-k", help="Override keep_last for this model"),
    trigger_ratio: float | None = typer.Option(None, "--trigger-ratio", help="Override trigger_ratio for this model"),
    silent: bool | None = typer.Option(None, "--silent/--no-silent", help="Override silent for this model"),
    clear: bool = typer.Option(False, "--clear", help="Remove all overrides for this model"),
):
    """View or set per-model compaction overrides."""
    from debot.config.loader import get_config_path, load_config, save_config
    from debot.config.schema import CompactionOverride

    config_path = get_config_path()
    config = load_config()

    overrides = config.agents.defaults.compaction_model_overrides

    if show:
        if model in overrides:
            o = overrides[model]
            console.print(f"Model overrides for {model}:")
            console.print(f"  keep_last: {o.keep_last}")
            console.print(f"  trigger_ratio: {o.trigger_ratio}")
            console.print(f"  silent: {o.silent}")
        else:
            console.print(f"No overrides set for model {model}")

    if clear:
        if model in overrides:
            del overrides[model]
            save_config(config, config_path)
            console.print(f"[green]✓[/green] Cleared overrides for {model}")
        return

    # Update overrides if provided
    updated = False
    if keep_last is not None or trigger_ratio is not None or silent is not None:
        if model not in overrides:
            overrides[model] = CompactionOverride()
        if keep_last is not None:
            overrides[model].keep_last = keep_last
            updated = True
        if trigger_ratio is not None:
            overrides[model].trigger_ratio = trigger_ratio
            updated = True
        if silent is not None:
            overrides[model].silent = silent
            updated = True

    if updated:
        save_config(config, config_path)
        console.print(f"[green]✓[/green] Updated overrides for {model}")


# ============================================================================
# Gateway / Server
# ============================================================================


@app.command()
def gateway(
    port: int = typer.Option(18790, "--port", "-p", help="Gateway port"),
    verbose: bool = typer.Option(False, "--verbose", "-v", help="Verbose output"),
):
    """Start the debot gateway."""
    from debot.agent.loop import AgentLoop
    from debot.bus import MessageBus
    from debot.channels.manager import ChannelManager
    from debot.config.loader import get_data_dir, load_config
    from debot.cron import CronJob, CronService
    from debot.heartbeat import HeartbeatService
    from debot.providers.litellm_provider import LiteLLMProvider

    if verbose:
        import logging

        logging.basicConfig(level=logging.DEBUG)

    console.print(f"{__logo__} Starting debot gateway on port {port}...")

    config = load_config()

    # Create components
    bus = MessageBus()

    # Create provider (supports OpenRouter, Anthropic, OpenAI, Bedrock)
    api_key = config.get_api_key()
    api_base = config.get_api_base()
    model = config.agents.defaults.model
    is_bedrock = model.startswith("bedrock/")

    if not api_key and not is_bedrock:
        console.print("[red]Error: No API key configured.[/red]")
        console.print("Set one in ~/.debot/config.json under providers.openrouter.apiKey")
        raise typer.Exit(1)

    provider = LiteLLMProvider(
        api_key=api_key,
        api_base=api_base,
        default_model=config.agents.defaults.model,
        all_api_keys=config.get_all_api_keys(),
    )

    # Create agent
    agent = AgentLoop(
        bus=bus,
        provider=provider,
        workspace=config.workspace_path,
        model=config.agents.defaults.model,
        max_iterations=config.agents.defaults.max_tool_iterations,
        brave_api_key=config.tools.web.search.api_key or None,
    )

    # Create cron service
    async def on_cron_job(job: CronJob) -> str | None:
        """Execute a cron job through the agent."""
        response = await agent.process_direct(job.payload.message, session_key=f"cron:{job.id}")
        # Optionally deliver to channel
        if job.payload.deliver and job.payload.to:
            from debot.bus import OutboundMessage

            await bus.publish_outbound(
                OutboundMessage(
                    channel=job.payload.channel or "whatsapp",
                    chat_id=job.payload.to,
                    content=response or "",
                )
            )
        return response

    cron_store_path = get_data_dir() / "cron" / "jobs.json"
    cron = CronService(cron_store_path, on_job=on_cron_job)

    # Create heartbeat service
    async def on_heartbeat(prompt: str) -> str:
        """Execute heartbeat through the agent."""
        return await agent.process_direct(prompt, session_key="heartbeat")

    heartbeat = HeartbeatService(
        workspace=config.workspace_path,
        on_heartbeat=on_heartbeat,
        interval_s=30 * 60,  # 30 minutes
        enabled=True,
    )

    # Create channel manager
    channels = ChannelManager(config, bus)

    if channels.enabled_channels:
        console.print(f"[green]✓[/green] Channels enabled: {', '.join(channels.enabled_channels)}")
    else:
        console.print("[yellow]Warning: No channels enabled[/yellow]")

    cron_status = cron.status()
    if cron_status["jobs"] > 0:
        console.print(f"[green]✓[/green] Cron: {cron_status['jobs']} scheduled jobs")

    console.print("[green]✓[/green] Heartbeat: every 30m")

    # Create web management UI
    from debot.web import create_app

    web_app = create_app(channel_manager=channels, cron_service=cron)

    async def _run_web_server(app, host: str, port: int):
        import uvicorn

        uvi_config = uvicorn.Config(app, host=host, port=port, log_level="info")
        server = uvicorn.Server(uvi_config)
        await server.serve()

    web_host = config.gateway.host
    console.print(f"[green]✓[/green] Web UI: http://{web_host}:{port}")

    async def run():
        try:
            # Run all services concurrently (they all have infinite loops)
            await asyncio.gather(
                cron.start(),
                heartbeat.start(),
                agent.run(),
                channels.start_all(),
                _run_web_server(web_app, host=web_host, port=port),
            )
        except KeyboardInterrupt:
            console.print("\nShutting down...")
        finally:
            heartbeat.stop()
            cron.stop()
            agent.stop()
            await channels.stop_all()

    asyncio.run(run())


# ============================================================================
# Agent Commands
# ============================================================================


@app.command()
def agent(
    message: str = typer.Option(None, "--message", "-m", help="Message to send to the agent"),
    session_id: str = typer.Option("cli:default", "--session", "-s", help="Session ID"),
):
    """Interact with the agent directly."""
    from debot.agent.loop import AgentLoop
    from debot.bus import MessageBus
    from debot.config.loader import load_config
    from debot.providers.litellm_provider import LiteLLMProvider

    config = load_config()

    api_key = config.get_api_key()
    api_base = config.get_api_base()
    model = config.agents.defaults.model
    is_bedrock = model.startswith("bedrock/")

    if not api_key and not is_bedrock:
        console.print("[red]Error: No API key configured.[/red]")
        raise typer.Exit(1)

    bus = MessageBus()
    provider = LiteLLMProvider(
        api_key=api_key,
        api_base=api_base,
        default_model=config.agents.defaults.model,
        all_api_keys=config.get_all_api_keys(),
    )

    agent_loop = AgentLoop(
        bus=bus,
        provider=provider,
        workspace=config.workspace_path,
        brave_api_key=config.tools.web.search.api_key or None,
    )

    if message:
        # Single message mode
        async def run_once():
            response = await agent_loop.process_direct(message, session_id)
            console.print(f"\n{__logo__} {response}")

        asyncio.run(run_once())
    else:
        # Interactive mode
        console.print(f"{__logo__} Interactive mode (Ctrl+C to exit)\n")

        async def run_interactive():
            while True:
                try:
                    user_input = console.input("[bold blue]You:[/bold blue] ")
                    if not user_input.strip():
                        continue

                    response = await agent_loop.process_direct(user_input, session_id)
                    console.print(f"\n{__logo__} {response}\n")
                except KeyboardInterrupt:
                    # Show session routing summary
                    try:
                        import debot_rust

                        metrics_json = debot_rust.get_router_metrics()
                        import json as _json

                        metrics = _json.loads(metrics_json)
                        if metrics["total_calls"] > 0:
                            console.print(
                                f"\n[dim]Session: {metrics['total_calls']} routed calls, "
                                f"est. cost ${metrics['total_estimated_cost']:.4f}/M tokens[/dim]"
                            )
                    except Exception:
                        pass
                    console.print("\nGoodbye!")
                    break

        asyncio.run(run_interactive())


# ============================================================================
# Channel Commands
# ============================================================================


channels_app = typer.Typer(help="Manage channels")
app.add_typer(channels_app, name="channels")


sessions_app = typer.Typer(help="Manage sessions")
app.add_typer(sessions_app, name="sessions")


@sessions_app.command("compact")
def sessions_compact(
    session_key: str = typer.Argument(..., help="Session key, e.g. telegram:12345"),
    keep_last: int = typer.Option(50, "--keep-last", "-k", help="Number of recent messages to keep"),
    instruction: str | None = typer.Option(None, "--instruction", "-i", help="Optional compaction instruction"),
):
    """Compact an existing session's history into a compact summary entry."""
    from debot.config.loader import load_config

    config = load_config()
    sm = __import__("debot.session._manager_py", fromlist=["SessionManager"]).SessionManager(config.workspace_path)

    try:
        compacted = sm.compact_session(session_key, keep_last=keep_last, instruction=instruction)
        if compacted:
            console.print(f"[green]✓[/green] Compacted {compacted} messages for session {session_key}")
        else:
            console.print(f"[yellow]No messages to compact for session {session_key}[/yellow]")
    except Exception as e:
        console.print(f"[red]Error compacting session: {e}[/red]")


@channels_app.command("status")
def channels_status():
    """Show channel status."""
    from debot.config.loader import load_config

    config = load_config()

    table = Table(title="Channel Status")
    table.add_column("Channel", style="cyan")
    table.add_column("Enabled", style="green")
    table.add_column("Configuration", style="yellow")

    # WhatsApp
    wa = config.channels.whatsapp
    table.add_row("WhatsApp", "✓" if wa.enabled else "✗", wa.bridge_url)

    # Telegram
    tg = config.channels.telegram
    tg_config = f"token: {tg.token[:10]}..." if tg.token else "[dim]not configured[/dim]"
    table.add_row("Telegram", "✓" if tg.enabled else "✗", tg_config)

    console.print(table)


def _get_bridge_dir() -> Path:
    """Get the bridge directory, setting it up if needed."""
    import shutil
    import subprocess

    # User's bridge location
    user_bridge = Path.home() / ".debot" / "bridge"

    # Check if already built
    if (user_bridge / "dist" / "index.js").exists():
        return user_bridge

    # Check for npm
    if not shutil.which("npm"):
        console.print("[red]npm not found. Please install Node.js >= 18.[/red]")
        raise typer.Exit(1)

    # Find source bridge: first check package data, then source dir
    pkg_bridge = Path(__file__).parent / "bridge"  # debot/bridge (installed)
    src_bridge = Path(__file__).parent.parent.parent / "bridge"  # repo root/bridge (dev)

    source = None
    if (pkg_bridge / "package.json").exists():
        source = pkg_bridge
    elif (src_bridge / "package.json").exists():
        source = src_bridge

    if not source:
        console.print("[red]Bridge source not found.[/red]")
        console.print("Try reinstalling: pip install --force-reinstall debot")
        raise typer.Exit(1)

    # Copy to user directory
    user_bridge.parent.mkdir(parents=True, exist_ok=True)
    if user_bridge.exists():
        shutil.rmtree(user_bridge)
    shutil.copytree(source, user_bridge, ignore=shutil.ignore_patterns("node_modules", "dist"))

    # Install and build
    try:
        console.print("  Installing dependencies...")
        subprocess.run(["npm", "install"], cwd=user_bridge, check=True, capture_output=True)

        console.print("  Building...")
        subprocess.run(["npm", "run", "build"], cwd=user_bridge, check=True, capture_output=True)

        console.print("[green]✓[/green] Bridge ready\n")
    except subprocess.CalledProcessError as e:
        console.print(f"[red]Build failed: {e}[/red]")
        if e.stderr:
            console.print(f"[dim]{e.stderr.decode()[:500]}[/dim]")
        raise typer.Exit(1)

    return user_bridge


# ============================================================================
# Skills commands
# ============================================================================

skills_app = typer.Typer(help="Manage skills")
app.add_typer(skills_app, name="skills")


@skills_app.command("install")
def skills_install(
    name_or_url: str = typer.Argument(..., help="Skill name (system) or URL to .skill file"),
    workspace: str | None = typer.Option(None, "--workspace", "-w", help="Workspace path"),
):
    """Install a system-bundled skill or an explicit .skill URL.

    Examples:
      debot skills install find-skills
      debot skills install file:///path/to/my.skill
    """
    from debot.utils.helpers import get_workspace_path

    ws = get_workspace_path(workspace)

    # Decide whether it's a URL (support http(s) and file:// local URLs)
    if name_or_url.startswith(("http://", "https://", "file://")):
        url = name_or_url
        console.print(f"Downloading skill from {url}...")
        try:
            from debot.skills.installer import install_from_url

            installed = install_from_url(url, workspace=ws)
            console.print(f"[green]✓[/green] Installed skill to {installed}")
        except Exception as e:
            console.print(f"[red]Error:[/red] {e}")
            raise typer.Exit(1)
    else:
        # System-installed skills: install the named bundled skill (if available)
        console.print(f"Installing system skill '{name_or_url}'...")
        try:
            from debot.skills.installer import install_from_system

            installed = install_from_system(name_or_url, workspace=ws)
            console.print(f"[green]✓[/green] Installed skill to {installed}")
        except Exception as e:
            console.print(f"[red]Error:[/red] {e}")
            raise typer.Exit(1)


@skills_app.command("list")
def skills_list(
    workspace: str | None = typer.Option(None, "--workspace", "-w", help="Workspace path"),
    query: str | None = typer.Option(None, "--query", "-q", help="Filter skills by name substring"),
    json_out: bool = typer.Option(False, "--json", help="Output JSON"),
):
    """List available system and workspace skills (uses find-skills).

    Optionally filter by `--query` and output JSON with `--json`.
    """
    from debot.utils.helpers import get_workspace_path

    ws = get_workspace_path(workspace)
    try:
        # Try to use the bundled find-skills runtime
        from debot.skills.find_skills.run import list_skills

        skills = list_skills(ws, query=query)
        if json_out:
            import json

            console.print(json.dumps(skills, indent=2, ensure_ascii=False))
            return

        console.print("[green]System skills:[/green]")
        for s in skills.get("system", []):
            console.print(f"  - {s}")
        console.print("\n[green]Workspace skills:[/green]")
        for s in skills.get("workspace", []):
            console.print(f"  - {s}")
    except Exception as e:
        console.print(f"[red]Error listing skills:[/red] {e}")
        raise typer.Exit(1)


@channels_app.command("login")
def channels_login():
    """Link device via QR code."""
    import subprocess

    bridge_dir = _get_bridge_dir()

    console.print(f"{__logo__} Starting bridge...")
    console.print("Scan the QR code to connect.\n")

    try:
        subprocess.run(["npm", "start"], cwd=bridge_dir, check=True)
    except subprocess.CalledProcessError as e:
        console.print(f"[red]Bridge failed: {e}[/red]")
    except FileNotFoundError:
        console.print("[red]npm not found. Please install Node.js.[/red]")


# ============================================================================
# Cron Commands
# ============================================================================

cron_app = typer.Typer(help="Manage scheduled tasks")
app.add_typer(cron_app, name="cron")


@cron_app.command("list")
def cron_list(
    all: bool = typer.Option(False, "--all", "-a", help="Include disabled jobs"),
):
    """List scheduled jobs."""
    from debot.config.loader import get_data_dir
    from debot.cron import CronService

    store_path = get_data_dir() / "cron" / "jobs.json"
    service = CronService(store_path)

    jobs = service.list_jobs(include_disabled=all)

    if not jobs:
        console.print("No scheduled jobs.")
        return

    table = Table(title="Scheduled Jobs")
    table.add_column("ID", style="cyan")
    table.add_column("Name")
    table.add_column("Schedule")
    table.add_column("Status")
    table.add_column("Next Run")

    import time

    for job in jobs:
        # Format schedule
        if job.schedule.kind == "every":
            sched = f"every {(job.schedule.every_ms or 0) // 1000}s"
        elif job.schedule.kind == "cron":
            sched = job.schedule.expr or ""
        else:
            sched = "one-time"

        # Format next run
        next_run = ""
        if job.state.next_run_at_ms:
            next_time = time.strftime("%Y-%m-%d %H:%M", time.localtime(job.state.next_run_at_ms / 1000))
            next_run = next_time

        status = "[green]enabled[/green]" if job.enabled else "[dim]disabled[/dim]"

        table.add_row(job.id, job.name, sched, status, next_run)

    console.print(table)


@cron_app.command("add")
def cron_add(
    name: str = typer.Option(..., "--name", "-n", help="Job name"),
    message: str = typer.Option(..., "--message", "-m", help="Message for agent"),
    every: int = typer.Option(None, "--every", "-e", help="Run every N seconds"),
    cron_expr: str = typer.Option(None, "--cron", "-c", help="Cron expression (e.g. '0 9 * * *')"),
    at: str = typer.Option(None, "--at", help="Run once at time (ISO format)"),
    deliver: bool = typer.Option(False, "--deliver", "-d", help="Deliver response to channel"),
    to: str = typer.Option(None, "--to", help="Recipient for delivery"),
    channel: str = typer.Option(None, "--channel", help="Channel for delivery (e.g. 'telegram', 'whatsapp')"),
):
    """Add a scheduled job."""
    from debot.config.loader import get_data_dir
    from debot.cron import CronSchedule, CronService

    # Determine schedule type
    if every:
        schedule = CronSchedule(kind="every", every_ms=every * 1000)
    elif cron_expr:
        schedule = CronSchedule(kind="cron", expr=cron_expr)
    elif at:
        import datetime

        dt = datetime.datetime.fromisoformat(at)
        schedule = CronSchedule(kind="at", at_ms=int(dt.timestamp() * 1000))
    else:
        console.print("[red]Error: Must specify --every, --cron, or --at[/red]")
        raise typer.Exit(1)

    store_path = get_data_dir() / "cron" / "jobs.json"
    service = CronService(store_path)

    job = service.add_job(
        name=name,
        schedule=schedule,
        message=message,
        deliver=deliver,
        to=to,
        channel=channel,
    )

    console.print(f"[green]✓[/green] Added job '{job.name}' ({job.id})")


@cron_app.command("remove")
def cron_remove(
    job_id: str = typer.Argument(..., help="Job ID to remove"),
):
    """Remove a scheduled job."""
    from debot.config.loader import get_data_dir
    from debot.cron import CronService

    store_path = get_data_dir() / "cron" / "jobs.json"
    service = CronService(store_path)

    if service.remove_job(job_id):
        console.print(f"[green]✓[/green] Removed job {job_id}")
    else:
        console.print(f"[red]Job {job_id} not found[/red]")


@cron_app.command("enable")
def cron_enable(
    job_id: str = typer.Argument(..., help="Job ID"),
    disable: bool = typer.Option(False, "--disable", help="Disable instead of enable"),
):
    """Enable or disable a job."""
    from debot.config.loader import get_data_dir
    from debot.cron import CronService

    store_path = get_data_dir() / "cron" / "jobs.json"
    service = CronService(store_path)

    job = service.enable_job(job_id, enabled=not disable)
    if job:
        status = "disabled" if disable else "enabled"
        console.print(f"[green]✓[/green] Job '{job.name}' {status}")
    else:
        console.print(f"[red]Job {job_id} not found[/red]")


@cron_app.command("run")
def cron_run(
    job_id: str = typer.Argument(..., help="Job ID to run"),
    force: bool = typer.Option(False, "--force", "-f", help="Run even if disabled"),
):
    """Manually run a job."""
    from debot.config.loader import get_data_dir
    from debot.cron import CronService

    store_path = get_data_dir() / "cron" / "jobs.json"
    service = CronService(store_path)

    async def run():
        return await service.run_job(job_id, force=force)

    if asyncio.run(run()):
        console.print("[green]✓[/green] Job executed")
    else:
        console.print(f"[red]Failed to run job {job_id}[/red]")


# ============================================================================
# Status Commands
# ============================================================================


@app.command()
def status():
    """Show debot status."""
    from debot.config.loader import get_config_path, load_config

    config_path = get_config_path()
    config = load_config()
    workspace = config.workspace_path

    console.print(f"{__logo__} debot Status\n")

    console.print(f"Config: {config_path} {'[green]✓[/green]' if config_path.exists() else '[red]✗[/red]'}")
    console.print(f"Workspace: {workspace} {'[green]✓[/green]' if workspace.exists() else '[red]✗[/red]'}")

    if config_path.exists():
        console.print(f"Model: {config.agents.defaults.model}")

        # Check API keys
        has_openrouter = bool(config.providers.openrouter.api_key)
        has_anthropic = bool(config.providers.anthropic.api_key)
        has_openai = bool(config.providers.openai.api_key)
        has_gemini = bool(config.providers.gemini.api_key)
        has_vllm = bool(config.providers.vllm.api_base)

        console.print(f"OpenRouter API: {'[green]✓[/green]' if has_openrouter else '[dim]not set[/dim]'}")
        console.print(f"Anthropic API: {'[green]✓[/green]' if has_anthropic else '[dim]not set[/dim]'}")
        console.print(f"OpenAI API: {'[green]✓[/green]' if has_openai else '[dim]not set[/dim]'}")
        console.print(f"Gemini API: {'[green]✓[/green]' if has_gemini else '[dim]not set[/dim]'}")
        vllm_status = f"[green]✓ {config.providers.vllm.api_base}[/green]" if has_vllm else "[dim]not set[/dim]"
        console.print(f"vLLM/Local: {vllm_status}")

        # Router status
        try:
            import debot_rust

            console.print("Router: [green]✓ available[/green]")
            try:
                import json as _json

                metrics_json = debot_rust.get_router_metrics()
                metrics = _json.loads(metrics_json)
                if metrics["total_calls"] > 0:
                    console.print(f"  Routed calls: {metrics['total_calls']}")
                    console.print(f"  Est. cost: ${metrics['total_estimated_cost']:.4f}/M tokens")
                    for tier, count in sorted(metrics.get("tier_counts", {}).items()):
                        console.print(f"  {tier}: {count} calls")
            except Exception:
                pass
        except ImportError:
            console.print("Router: [dim]not available (Rust extension not built)[/dim]")


# ============================================================================
# Router Commands
# ============================================================================


router_app = typer.Typer(help="Router information and metrics")
app.add_typer(router_app, name="router")


@router_app.command("test")
def router_test(
    prompt: str = typer.Argument(..., help="Prompt text to route"),
):
    """Test the router with a prompt and show the decision."""
    import json as _json

    try:
        import debot_rust

        decision_json = debot_rust.route_text(prompt, 4096)
        dec = _json.loads(decision_json)

        # Decision summary
        tier = dec["tier"]
        tier_colors = {
            "SIMPLE": "green",
            "MEDIUM": "yellow",
            "COMPLEX": "red",
            "REASONING": "magenta",
        }
        tier_color = tier_colors.get(tier, "white")

        console.print(f"\nModel:      [cyan]{dec['model']}[/cyan]")
        console.print(f"Tier:       [{tier_color}]{tier}[/{tier_color}]")
        console.print(f"Confidence: {dec['confidence']:.3f}")
        console.print(f"Cost:       ${dec['cost_estimate']:.2f}/M tokens")
        console.print(f"Explain:    {dec['explain']}")

        # Dimension scores table
        scores = dec.get("scores", {})
        if scores:
            console.print()
            table = Table(title="Dimension Scores")
            table.add_column("Dimension", style="cyan", min_width=12)
            table.add_column("Score", justify="right", min_width=6)
            table.add_column("", min_width=20)

            for dim, score in sorted(scores.items(), key=lambda x: -x[1]):
                bar_len = int(score * 20)
                bar = "[green]" + "\u2588" * bar_len + "[/green]" + "[dim]" + "\u2591" * (20 - bar_len) + "[/dim]"
                table.add_row(dim, f"{score:.3f}", bar)

            console.print(table)

    except ImportError:
        console.print("[red]Router not available (Rust extension not built)[/red]")
    except Exception as e:
        console.print(f"[red]Error: {e}[/red]")


@router_app.command("metrics")
def router_metrics():
    """Show router metrics for the current session."""
    import json as _json

    try:
        import debot_rust

        metrics_json = debot_rust.get_router_metrics()
        metrics = _json.loads(metrics_json)

        if metrics["total_calls"] == 0:
            console.print("No routing decisions recorded yet.")
            return

        # Summary table
        table = Table(title="Router Metrics")
        table.add_column("Metric", style="cyan")
        table.add_column("Value", style="green", justify="right")

        table.add_row("Total calls", str(metrics["total_calls"]))
        table.add_row("Est. cost", f"${metrics['total_estimated_cost']:.4f}/M tokens")

        # Tier breakdown
        total = metrics["total_calls"]
        for tier, count in sorted(metrics.get("tier_counts", {}).items()):
            pct = count / total * 100 if total > 0 else 0
            table.add_row(f"  {tier}", f"{count} ({pct:.0f}%)")

        # Model breakdown
        table.add_section()
        for model, count in sorted(metrics.get("model_counts", {}).items()):
            pct = count / total * 100 if total > 0 else 0
            table.add_row(f"  {model}", f"{count} ({pct:.0f}%)")

        console.print(table)

        # Last decision
        last = metrics.get("last_decision")
        if last:
            console.print(
                f"\nLast: [cyan]{last['model']}[/cyan] (tier={last['tier']}, confidence={last['confidence']:.2f})"
            )

    except ImportError:
        console.print("[red]Router not available (Rust extension not built)[/red]")
    except Exception as e:
        console.print(f"[red]Error: {e}[/red]")


if __name__ == "__main__":
    app()
