"""
CLI authentication — device flow.

How it works:
  1. CLI generates ss_... token locally (never sent to server)
  2. CLI inserts {code, token_hash} into Supabase cli_auth_sessions
  3. CLI prints the auth URL and tries to open the browser
  4. User opens URL (same machine = browser opens automatically, VPS = copy/paste on any device)
  5. User clicks Authorize → frontend marks session authorized + inserts token into api_tokens
  6. CLI local server gets instant callback if browser is on same machine
  7. CLI poll sees authorized=true → saves token → done
"""
import hashlib
import json
import secrets
import socket
import threading
import time
import urllib.error
import urllib.request
import webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer

import typer

from . import config as cfg_module

API_TOKEN_PREFIX = "ss_"

# Public values — safe to hardcode (anon key is meant to be public)
SUPABASE_URL = "https://tosuchhpmtkjnklqajtf.supabase.co"
SUPABASE_ANON_KEY = (
    "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"
    ".eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InRvc3VjaGhwbXRram5rbHFhanRmIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTI4Nzc2MjAsImV4cCI6MjA2ODQ1MzYyMH0"
    ".IGfIn20IXTze0le-0qlGlhBSsGgkIYzV_dcUwABJ09o"
)
FRONTEND_URL = "https://sixtysix.pro"
POLL_INTERVAL = 2
TIMEOUT_SECONDS = 300  # 5 minutes


def _supabase(method: str, table: str, body: dict | None = None, params: dict | None = None) -> list | None:
    """Minimal Supabase REST helper using only stdlib."""
    url = f"{SUPABASE_URL}/rest/v1/{table}"
    if params:
        url += "?" + "&".join(f"{k}={v}" for k, v in params.items())

    headers = {
        "apikey": SUPABASE_ANON_KEY,
        "Authorization": f"Bearer {SUPABASE_ANON_KEY}",
        "Content-Type": "application/json",
        "Prefer": "return=minimal",
    }
    encoded = json.dumps(body).encode() if body else None
    req = urllib.request.Request(url, data=encoded, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req, timeout=5) as resp:
            content = resp.read()
            return json.loads(content) if content else None
    except urllib.error.HTTPError as e:
        raise RuntimeError(f"Supabase {method} error {e.code}: {e.read().decode()}")


def _free_port() -> int:
    with socket.socket() as s:
        s.bind(("", 0))
        return s.getsockname()[1]


def login() -> None:
    """Authenticate the CLI using a browser-based device flow."""
    # Token is generated entirely on the CLI — raw value never reaches the server
    raw_token = f"{API_TOKEN_PREFIX}{secrets.token_bytes(32).hex()}"
    token_hash = hashlib.sha256(raw_token.encode()).hexdigest()
    code = secrets.token_urlsafe(16)

    # Local HTTP server — gives instant callback when browser is on the same machine
    port = _free_port()
    callback_received = threading.Event()

    class _Handler(BaseHTTPRequestHandler):
        def do_GET(self):
            self.send_response(200)
            self.send_header("Content-Type", "text/html; charset=utf-8")
            self.end_headers()
            self.wfile.write(
                b"<html><body style='font-family:sans-serif;text-align:center;padding:60px'>"
                b"<h2>\xe2\x9c\x93 CLI Authorized!</h2>"
                b"<p>You can close this tab and return to your terminal.</p>"
                b"</body></html>"
            )
            callback_received.set()

        def log_message(self, *args):
            pass  # suppress access logs

    server = HTTPServer(("localhost", port), _Handler)
    server.timeout = 1  # non-blocking handle_request calls

    # Create the session in Supabase
    url = f"{FRONTEND_URL}/auth/cli?code={code}&port={port}"
    try:
        _supabase("POST", "cli_auth_sessions", {"code": code, "token_hash": token_hash})
    except RuntimeError as e:
        typer.echo(typer.style(f"Failed to start auth session: {e}", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    typer.echo()
    typer.echo("Open this URL in your browser to authorize:")
    typer.echo()
    typer.echo(f"  {typer.style(url, fg=typer.colors.CYAN)}")
    typer.echo()
    typer.echo("Waiting for authorization", nl=False)

    # Try to open the browser — works locally, silently fails on headless VPS
    webbrowser.open(url)

    # Poll until authorized or timed out
    deadline = time.time() + TIMEOUT_SECONDS
    authorized = False

    while time.time() < deadline:
        # Check for instant local callback (same machine)
        server.handle_request()
        if callback_received.is_set():
            authorized = True
            break

        # Poll Supabase (handles remote-device authorization)
        try:
            rows = _supabase(
                "GET", "cli_auth_sessions",
                params={"code": f"eq.{code}", "select": "authorized", "limit": "1"},
            )
            if rows and rows[0].get("authorized"):
                authorized = True
                break
        except Exception:
            pass

        typer.echo(".", nl=False)
        time.sleep(POLL_INTERVAL)

    typer.echo()

    if not authorized:
        typer.echo(typer.style("Timed out. Run 'sixtysix login' to try again.", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    # Persist token — it was generated locally, never transmitted
    cfg = cfg_module.load()
    cfg.api_token = raw_token
    cfg_module.save(cfg)

    token_file = cfg_module.data_dir() / ".cache" / ".api_token"
    try:
        token_file.parent.mkdir(mode=0o755, parents=True, exist_ok=True)
        tmp = token_file.with_suffix(".tmp")
        tmp.write_text(raw_token)
        tmp.replace(token_file)
    except Exception as e:
        typer.echo(
            typer.style(f"Warning: could not write token cache: {e}", fg=typer.colors.YELLOW),
            err=True,
        )

    typer.echo(typer.style("✓ Logged in!", fg=typer.colors.GREEN))
    typer.echo()
    typer.echo("Run 'sixtysix start' to launch the backend.")
