"""Proxy commands: proxy start, proxy stop, proxy status, proxy logs.

These preserve the existing WebSocket tunnel functionality from the
original mcpbundles-proxy, now under the 'proxy' subcommand group.
"""

import json
import os
import time
from pathlib import Path
from typing import Optional

import click

from mcpbundles.config import load_token, CONFIG_DIR
from mcpbundles.daemon import start_daemon, stop_daemon, get_status, get_pid_file
from mcpbundles.output import render_success, render_error, render_warning, stdout_console

_EXPOSED_PORTS_FILE = CONFIG_DIR / "exposed_ports.json"


def _save_exposed_port(port: int, conn_name: Optional[str]) -> None:
    """Persist an exposed port so the daemon can auto-refresh its TTL."""
    ports = _load_exposed_ports()
    ports[str(port)] = {"conn_name": conn_name}
    _EXPOSED_PORTS_FILE.parent.mkdir(parents=True, exist_ok=True)
    _EXPOSED_PORTS_FILE.write_text(json.dumps(ports))


def _remove_exposed_port(port: int) -> None:
    ports = _load_exposed_ports()
    ports.pop(str(port), None)
    if ports:
        _EXPOSED_PORTS_FILE.write_text(json.dumps(ports))
    else:
        _EXPOSED_PORTS_FILE.unlink(missing_ok=True)


def load_exposed_ports() -> dict:
    """Public accessor for the daemon to read exposed ports."""
    return _load_exposed_ports()


def _load_exposed_ports() -> dict:
    try:
        if _EXPOSED_PORTS_FILE.exists():
            return json.loads(_EXPOSED_PORTS_FILE.read_text())
    except Exception:
        pass
    return {}


def _is_running() -> bool:
    """Check if tunnel daemon is running."""
    pid_file = get_pid_file()
    if not pid_file.exists():
        return False
    try:
        pid = int(pid_file.read_text())
        os.kill(pid, 0)
        return True
    except (ValueError, ProcessLookupError, PermissionError):
        pid_file.unlink(missing_ok=True)
        return False


@click.group()
def proxy() -> None:
    """Manage the local WebSocket tunnel (browser, SQLite, Claude Code)."""
    pass


@proxy.command("start")
def proxy_start() -> None:
    """Start tunnel daemon in background."""
    if _is_running():
        render_warning("Proxy is already running. Use 'mcpbundles proxy status' to see details.")
        return

    token = load_token()
    if not token:
        render_error("Not authenticated. Run 'mcpbundles login' first.")
        raise SystemExit(1)

    stdout_console.print("Starting tunnel daemon...")
    start_daemon(token)
    render_success("Tunnel started")
    stdout_console.print("  Use [cyan]mcpbundles proxy status[/] for details")
    stdout_console.print("  Use [cyan]mcpbundles proxy logs[/] to view logs")


@proxy.command("stop")
def proxy_stop() -> None:
    """Stop tunnel daemon."""
    if not _is_running():
        render_warning("Proxy is not running.")
        return

    stdout_console.print("Stopping tunnel daemon...")
    stop_daemon()
    render_success("Tunnel stopped")


@proxy.command("restart")
def proxy_restart() -> None:
    """Restart tunnel daemon."""
    if _is_running():
        stdout_console.print("Stopping tunnel daemon...")
        stop_daemon()
        time.sleep(1)

    token = load_token()
    if not token:
        render_error("Not authenticated. Run 'mcpbundles login' first.")
        raise SystemExit(1)

    stdout_console.print("Starting tunnel daemon...")
    start_daemon(token)
    render_success("Tunnel restarted")


@proxy.command("status")
def proxy_status() -> None:
    """Show tunnel connection status."""
    status_data = get_status()

    if status_data["running"]:
        from rich.panel import Panel

        lines = [
            f"  [bold]PID:[/bold]     {status_data.get('pid', '?')}",
        ]
        if "uptime" in status_data:
            lines.append(f"  [bold]Uptime:[/bold]  {status_data['uptime']}")
        if "tunnel_status" in status_data:
            icon = "[green]●[/]" if status_data["tunnel_status"] == "connected" else "[yellow]●[/]"
            lines.append(f"  [bold]Tunnel:[/bold]  {icon} {status_data['tunnel_status']}")

        stdout_console.print(Panel("\n".join(lines), title="Proxy Status", border_style="green", padding=(1, 2)))
    else:
        stdout_console.print("[red]●[/] Proxy is not running")
        stdout_console.print("  Use [cyan]mcpbundles proxy start[/] to start the tunnel")


@proxy.command("expose")
@click.argument("port", type=int)
@click.option("--as", "conn_name", default=None, help="Named connection to use")
def proxy_expose(port: int, conn_name: str | None) -> None:
    """Expose a localhost port through the proxy tunnel.

    Creates a public URL that routes HTTP traffic to localhost:PORT
    through your running proxy tunnel. Use this so hosted browsers
    (Remote Browser) or mobile devices can reach your local dev server.

    Example: mcpbundles proxy expose 3000
    """
    if port < 1 or port > 65535:
        render_error(f"Invalid port: {port}")
        raise SystemExit(1)

    if not _is_running():
        render_error("Proxy is not running. Start it first: mcpbundles proxy start")
        raise SystemExit(1)

    from mcpbundles.auth_resolve import resolve_auth
    from mcpbundles.config import CLISettings

    settings = CLISettings.resolve()
    try:
        auth = resolve_auth(settings, conn_name)
    except SystemExit:
        raise
    except Exception as e:
        render_error(f"Auth failed: {e}")
        raise SystemExit(1)

    import httpx

    is_api_key = auth.token.startswith("mb_")
    auth_header = f"ApiKey {auth.token}" if is_api_key else f"Bearer {auth.token}"

    try:
        resp = httpx.post(
            f"{auth.url}/api/tunnel/expose",
            json={"port": port},
            headers={"Authorization": auth_header},
            timeout=10.0,
        )
    except httpx.HTTPError as e:
        render_error(f"Failed to register expose: {e}")
        raise SystemExit(1)

    if resp.status_code != 200:
        detail = resp.json().get("detail", resp.text) if resp.headers.get("content-type", "").startswith("application/json") else resp.text
        render_error(f"Expose failed ({resp.status_code}): {detail}")
        raise SystemExit(1)

    data = resp.json()
    public_url = data["url"]

    _save_exposed_port(port, conn_name)

    from rich.panel import Panel
    stdout_console.print(Panel(
        f"  [bold]Port:[/bold]  {port}\n"
        f"  [bold]URL:[/bold]   [cyan]{public_url}[/cyan]\n\n"
        f"  Pass this URL to Remote Browser or Mobile Device tools\n"
        f"  instead of http://localhost:{port}",
        title="Port Exposed",
        border_style="green",
        padding=(1, 2),
    ))


@proxy.command("unexpose")
@click.argument("port", type=int)
@click.option("--as", "conn_name", default=None, help="Named connection to use")
def proxy_unexpose(port: int, conn_name: str | None) -> None:
    """Stop exposing a localhost port."""
    from mcpbundles.auth_resolve import resolve_auth
    from mcpbundles.config import CLISettings

    settings = CLISettings.resolve()
    try:
        auth = resolve_auth(settings, conn_name)
    except SystemExit:
        raise
    except Exception as e:
        render_error(f"Auth failed: {e}")
        raise SystemExit(1)

    import httpx

    is_api_key = auth.token.startswith("mb_")
    auth_header = f"ApiKey {auth.token}" if is_api_key else f"Bearer {auth.token}"

    try:
        resp = httpx.request(
            "DELETE",
            f"{auth.url}/api/tunnel/expose",
            json={"port": port},
            headers={"Authorization": auth_header},
            timeout=10.0,
        )
    except httpx.HTTPError as e:
        render_error(f"Failed to revoke: {e}")
        raise SystemExit(1)

    if resp.status_code == 200:
        _remove_exposed_port(port)
        render_success(f"Port {port} unexposed")
    else:
        render_error(f"Unexpose failed ({resp.status_code}): {resp.text}")
        raise SystemExit(1)


@proxy.command("logs")
@click.option("-n", "--lines", default=50, help="Number of lines to show (default: 50)")
@click.option("-f", "--follow", is_flag=True, help="Follow log output (like tail -f)")
def proxy_logs(lines: int, follow: bool) -> None:
    """View tunnel logs."""
    log_file = CONFIG_DIR / "tunnel.log"

    if not log_file.exists():
        stdout_console.print("No logs found.")
        stdout_console.print("  Logs appear after running [cyan]mcpbundles proxy start[/]")
        return

    if follow:
        try:
            with open(log_file, "r") as f:
                f.seek(0, 2)
                stdout_console.print("Following logs (Ctrl+C to stop)...")
                while True:
                    line = f.readline()
                    if line:
                        print(line.rstrip())
                    else:
                        time.sleep(0.1)
        except KeyboardInterrupt:
            print("\nStopped following logs")
    else:
        with open(log_file, "r") as f:
            log_lines = f.readlines()
            for line in log_lines[-lines:]:
                print(line.rstrip())
