from __future__ import annotations

import json
from pathlib import Path
from typing import Annotated

import httpx
import typer
from rich.console import Console
from rich.panel import Panel
from rich.table import Table

from ..config import (
    CLAUDE_LOCAL_SETTINGS_FILE,
    CLAUDE_SETTINGS_FILE,
    AppConfig,
    ClaudeSettingsManager,
)
from .app import app, console
from .shared import DEFAULT_SOCKET_PATH, get_network_interfaces, print_qr_code


def _resolve_scope_path(scope: str) -> Path:
    if scope == "user":
        return CLAUDE_SETTINGS_FILE
    if scope == "local":
        return CLAUDE_LOCAL_SETTINGS_FILE
    raise ValueError(scope)


def _render_status_table(
    console: Console,
    *,
    app_config: AppConfig,
    user_status,
    local_status,
    server_running: bool,
) -> None:
    table = Table(title="Claude Board Status")
    table.add_column("Setting", style="cyan")
    table.add_column("Value", style="green")

    table.add_row(
        "User hooks (~/.claude/settings.json)",
        "[green]Installed[/green]"
        if user_status.installed
        else "[dim]Not installed[/dim]",
    )
    table.add_row(
        "Local hooks (.claude/settings.local.json)",
        "[green]Installed[/green]"
        if local_status.installed
        else "[dim]Not installed[/dim]",
    )

    server_url = f"http://localhost:{app_config.server.port}"
    if server_running:
        table.add_row("Server", f"[green]Running[/green] at {server_url}")
    else:
        table.add_row(
            "Server",
            "[dim]Not running[/dim] (start with [bold]claude-board serve[/bold])",
        )

    interfaces = get_network_interfaces()
    if interfaces:
        for iface, ip, is_primary in interfaces:
            label = "Primary Network" if is_primary else f"Network ({iface})"
            url = f"http://{ip}:{app_config.server.port}"
            table.add_row(label, f"[bold]{url}[/bold]" if is_primary else f"[dim]{url}[/dim]")
    else:
        table.add_row("Network", "[dim]Unknown[/dim]")

    if DEFAULT_SOCKET_PATH.exists():
        table.add_row("RPC Socket", f"[green]{DEFAULT_SOCKET_PATH}[/green]")
    else:
        table.add_row("RPC Socket", "[dim]Not active[/dim]")

    console.print(table)


@app.command(name="install")
def install_command(
    scope: str = typer.Option(
        "user",
        "--scope",
        "-s",
        help=(
            "Where to install hooks: 'user' (~/.claude/settings.json)"
            " or 'local' (project .claude/settings.local.json)."
        ),
    ),
    *,
    force: Annotated[
        bool,
        typer.Option("--force", "-f", help="Reinstall even if already installed."),
    ] = False,
) -> None:
    try:
        settings_file = _resolve_scope_path(scope)
    except ValueError:
        console.print(f"[red]Invalid scope: {scope}. Use 'user' or 'local'.[/red]")
        raise typer.Exit(1) from None

    manager = ClaudeSettingsManager(settings_file)
    if manager.is_installed() and not force:
        console.print("[yellow]Claude Board hooks are already installed.[/yellow]")
        console.print("Use [bold]--force[/bold] to reinstall.")
        raise typer.Exit(0)

    try:
        manager.install_hooks()
        console.print(
            Panel.fit(
                f"[bold green]Hooks installed successfully![/bold green]\n\n"
                f"[dim]Settings file:[/dim] {settings_file}\n\n"
                "[yellow]Important:[/yellow] Restart Claude Code to load the hooks.\n\n"
                "Then start the server with:\n"
                "  [bold]claude-board serve[/bold]",
                title="Installation Complete",
                border_style="green",
            )
        )
    except (OSError, json.JSONDecodeError, KeyError) as e:
        console.print(f"[red]Failed to install hooks: {e}[/red]")
        raise typer.Exit(1) from None


@app.command(name="uninstall")
def uninstall_command(
    scope: str = typer.Option(
        "user",
        "--scope",
        "-s",
        help="Where to uninstall from: 'user' or 'local'.",
    ),
    *,
    all_scopes: Annotated[
        bool,
        typer.Option("--all", "-a", help="Uninstall from all scopes (user and local)."),
    ] = False,
) -> None:
    if all_scopes:
        scopes_to_uninstall = [
            ("user", CLAUDE_SETTINGS_FILE),
            ("local", CLAUDE_LOCAL_SETTINGS_FILE),
        ]
    elif scope == "user":
        scopes_to_uninstall = [("user", CLAUDE_SETTINGS_FILE)]
    elif scope == "local":
        scopes_to_uninstall = [("local", CLAUDE_LOCAL_SETTINGS_FILE)]
    else:
        console.print(f"[red]Invalid scope: {scope}[/red]")
        raise typer.Exit(1)

    uninstalled: list[str] = []
    for scope_name, settings_file in scopes_to_uninstall:
        manager = ClaudeSettingsManager(settings_file)
        if manager.is_installed():
            try:
                manager.uninstall_hooks()
                uninstalled.append(scope_name)
            except (OSError, json.JSONDecodeError, KeyError) as e:
                console.print(f"[red]Failed to uninstall from {scope_name}: {e}[/red]")

    if uninstalled:
        console.print(f"[green]Hooks uninstalled from: {', '.join(uninstalled)}[/green]")
        console.print("[yellow]Restart Claude Code to apply changes.[/yellow]")
    else:
        console.print("[yellow]No hooks were installed.[/yellow]")


@app.command(name="status")
def status_command() -> None:
    user_status = ClaudeSettingsManager(CLAUDE_SETTINGS_FILE).get_hooks_status()
    local_status = ClaudeSettingsManager(CLAUDE_LOCAL_SETTINGS_FILE).get_hooks_status()
    app_config = AppConfig.load()

    server_running = False
    try:
        httpx.get(f"http://localhost:{app_config.server.port}/api/health", timeout=2)
        server_running = True
    except (httpx.ConnectError, httpx.TimeoutException, OSError):
        pass

    _render_status_table(
        console,
        app_config=app_config,
        user_status=user_status,
        local_status=local_status,
        server_running=server_running,
    )

    if user_status.installed or local_status.installed:
        console.print("\n[dim]Installed hooks:[/dim]")
        for hook in user_status.our_hooks + local_status.our_hooks:
            console.print(f"  • {hook.event}: {hook.matcher}")

        total_other = user_status.other_hooks_count + local_status.other_hooks_count
        if total_other > 0:
            console.print(f"\n[dim]Other hooks preserved: {total_other}[/dim]")

    if server_running:
        interfaces = get_network_interfaces()
        for _iface, ip, is_primary in interfaces:
            if is_primary:
                print_qr_code(console, f"http://{ip}:{app_config.server.port}")
                break


@app.command(name="config")
def config_command(
    port: int | None = typer.Option(None, "--port", "-p", help="Set default server port."),
    *,
    show: Annotated[
        bool, typer.Option("--show/--no-show", help="Show current configuration.")
    ] = True,
    yolo: Annotated[
        bool | None, typer.Option("--yolo/--no-yolo", help="Set default YOLO mode.")
    ] = None,
) -> None:
    app_config = AppConfig.load()

    changed = False
    if port is not None:
        app_config.server.port = port
        changed = True
    if yolo is not None:
        app_config.yolo_mode_default = yolo
        changed = True

    if changed:
        config_path = Path.home() / ".claude-board" / "config.json"
        app_config.save(config_path)
        console.print(f"[green]Configuration saved to {config_path}[/green]")

    if show:
        table = Table(title="Configuration")
        table.add_column("Setting", style="cyan")
        table.add_column("Value", style="green")
        table.add_row("Server Host", app_config.server.host)
        table.add_row("Server Port", str(app_config.server.port))
        table.add_row("Hook Timeout", f"{app_config.server.hook_timeout}s")
        table.add_row("YOLO Default", str(app_config.yolo_mode_default))
        table.add_row("Safe Tools", ", ".join(app_config.hooks.safe_tools[:5]) + "...")
        table.add_row("Bluetooth", "[dim]Not yet implemented[/dim]")
        console.print(table)


@app.command(name="qr")
def qr_command(
    port: int | None = typer.Option(
        None,
        "--port",
        "-p",
        help="Port number (default: from config or 8765).",
    ),
) -> None:
    app_config = AppConfig.load()
    actual_port = port or app_config.server.port
    interfaces = get_network_interfaces()
    if not interfaces:
        console.print("[red]No network interfaces found.[/red]")
        raise typer.Exit(1)

    primary_url = None
    for iface, ip, is_primary in interfaces:
        if is_primary:
            primary_url = f"http://{ip}:{actual_port}"
            console.print(f"[bold]Primary interface:[/bold] {iface} ({ip})")
            break
    if not primary_url:
        iface, ip, _ = interfaces[0]
        primary_url = f"http://{ip}:{actual_port}"
        console.print(f"[bold]Interface:[/bold] {iface} ({ip})")

    print_qr_code(console, primary_url)
    if len(interfaces) > 1:
        console.print("[dim]Other interfaces:[/dim]")
        for iface, ip, is_primary in interfaces:
            if not is_primary:
                console.print(f"  [dim]{iface}: http://{ip}:{actual_port}[/dim]")


@app.command(name="ble", hidden=True)
def ble_command() -> None:
    console.print(
        Panel.fit(
            "[yellow]Bluetooth support is planned for Phase 2.[/yellow]\n\n"
            "The physical console will feature:\n"
            "• 4 mechanical key switches (Approve, Deny, Retry, YOLO)\n"
            "• E-ink display for task status\n"
            "• Bluetooth LE connection\n"
            "• Battery powered\n\n"
            "[dim]Stay tuned![/dim]",
            title="Coming Soon",
            border_style="yellow",
        )
    )
