"""CLI authentication via localhost callback — opens browser, receives tokens."""

from __future__ import annotations

import secrets
import socket
import threading
import webbrowser
from http.server import HTTPServer, BaseHTTPRequestHandler
from typing import Optional
from urllib.parse import urlparse, parse_qs, quote

from rich.console import Console

from .token_store import load_token, save_token, clear_token

console = Console()

WEB_APP_URL = "https://www.oncecheck.com"


def _find_free_port() -> int:
    """Find an available port on localhost."""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind(("127.0.0.1", 0))
        return s.getsockname()[1]


class _CallbackHandler(BaseHTTPRequestHandler):
    """Handles the OAuth callback from the web app."""

    token_data: Optional[dict] = None
    expected_state: Optional[str] = None

    def do_GET(self) -> None:
        parsed = urlparse(self.path)
        if parsed.path != "/callback":
            self.send_response(404)
            self.end_headers()
            return

        params = parse_qs(parsed.query)

        # CSRF check: verify the state parameter matches what the CLI generated
        state = params.get("state", [None])[0]
        if not state or state != _CallbackHandler.expected_state:
            self.send_response(403)
            self.send_header("Content-Type", "text/html")
            self.end_headers()
            self.wfile.write(b"<html><body><h2>Authentication failed.</h2>"
                             b"<p>Invalid state parameter (possible CSRF).</p></body></html>")
            return

        access_token = params.get("access_token", [None])[0]
        refresh_token = params.get("refresh_token", [None])[0]
        email = params.get("email", [None])[0]
        plan = params.get("plan", ["starter"])[0]

        if not access_token:
            self.send_response(400)
            self.send_header("Content-Type", "text/html")
            self.end_headers()
            self.wfile.write(b"<html><body><h2>Authentication failed.</h2>"
                             b"<p>No token received. Please try again.</p></body></html>")
            return

        # Store on the class so the caller can retrieve it
        _CallbackHandler.token_data = {
            "access_token": access_token,
            "refresh_token": refresh_token or "",
            "email": email or "",
            "plan": plan,
        }

        self.send_response(200)
        self.send_header("Content-Type", "text/html")
        self.end_headers()
        self.wfile.write(
            b"<html><body style='font-family:system-ui;text-align:center;padding:60px'>"
            b"<h2 style='color:#22c55e'>Authenticated!</h2>"
            b"<p>You can close this tab and return to the terminal.</p>"
            b"</body></html>"
        )

        # Shutdown the server in a separate thread to avoid deadlock
        threading.Thread(target=self.server.shutdown, daemon=True).start()

    def log_message(self, format: str, *args: object) -> None:
        """Suppress default HTTP logging."""
        pass


def login_flow() -> dict:
    """Run the browser-based login flow with localhost callback.

    1. Starts a temporary HTTP server on a free port.
    2. Opens the web app login page with a cli_redirect parameter.
    3. Waits for the browser to redirect back with tokens.
    4. Saves the tokens and returns the token data.
    """
    port = _find_free_port()
    state = secrets.token_urlsafe(32)
    redirect_url = f"http://localhost:{port}/callback"
    login_url = f"{WEB_APP_URL}/login?cli_redirect={quote(redirect_url, safe='')}&state={state}"

    _CallbackHandler.token_data = None
    _CallbackHandler.expected_state = state

    server = HTTPServer(("127.0.0.1", port), _CallbackHandler)

    console.print()
    console.print("[bold #eab308]Authentication required.[/bold #eab308]")
    console.print("Opening your browser to sign in...\n")
    console.print(f"  [bold #eeef20]{login_url}[/bold #eeef20]\n")
    console.print("[dim]Waiting for authentication...[/dim]")

    try:
        webbrowser.open(login_url)
    except OSError:
        console.print("[dim]Could not open browser. Please visit the URL above manually.[/dim]")

    # Run server in background thread with a 5-minute timeout
    server_thread = threading.Thread(target=server.serve_forever, daemon=True)
    server_thread.start()
    server_thread.join(timeout=300)

    if server_thread.is_alive():
        server.shutdown()
        server.server_close()
        console.print("\n[bold #ef4444]Login timed out.[/bold #ef4444] Please try again.")
        raise SystemExit(1)

    server.server_close()

    token_data = _CallbackHandler.token_data
    if not token_data or not token_data.get("access_token"):
        console.print("[bold #ef4444]Authentication failed.[/bold #ef4444] No token received.")
        raise SystemExit(1)

    save_token(token_data)
    console.print(f"[bold #22c55e]Authenticated![/bold #22c55e] ({token_data.get('email', '')} — {token_data.get('plan', 'starter').capitalize()} plan)")
    return token_data


def ensure_authenticated() -> dict:
    """Return token data if already authenticated, or trigger login flow."""
    token = load_token()
    if token and token.get("access_token"):
        return token
    return login_flow()


def logout() -> None:
    """Clear stored credentials."""
    clear_token()
    console.print("[#22c55e]Logged out successfully.[/#22c55e]")
