"""OAuth 2.1 PKCE flow via navegador para autenticação CLI."""

from __future__ import annotations

import base64
import hashlib
import secrets
import socket
import threading
import webbrowser
from http.server import BaseHTTPRequestHandler, HTTPServer
from typing import Any
from urllib.parse import parse_qs, urlencode, urlparse

import httpx

MCP_SERVER_URL = "https://mcp.tecjustica.com"
OAUTH_DISCOVERY_URL = f"{MCP_SERVER_URL}/.well-known/oauth-authorization-server"
CALLBACK_TIMEOUT = 120  # segundos para o usuário fazer login


def _generate_pkce() -> tuple[str, str]:
    """Gera code_verifier e code_challenge (S256)."""
    verifier = secrets.token_urlsafe(64)
    digest = hashlib.sha256(verifier.encode("ascii")).digest()
    challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii")
    return verifier, challenge


def _find_free_port() -> int:
    """Encontra uma porta local disponível."""
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind(("127.0.0.1", 0))
        return s.getsockname()[1]


def _discover_oauth() -> dict[str, Any]:
    """Busca os endpoints OAuth do servidor MCP."""
    resp = httpx.get(OAUTH_DISCOVERY_URL, timeout=10)
    resp.raise_for_status()
    return resp.json()


def _register_client(registration_endpoint: str, redirect_uri: str) -> dict[str, Any]:
    """Registra cliente via DCR (Dynamic Client Registration)."""
    resp = httpx.post(
        registration_endpoint,
        json={
            "client_name": "TecJustica CLI",
            "redirect_uris": [redirect_uri],
            "grant_types": ["authorization_code", "refresh_token"],
            "response_types": ["code"],
            "token_endpoint_auth_method": "none",
        },
        timeout=10,
    )
    resp.raise_for_status()
    return resp.json()


class _CallbackHandler(BaseHTTPRequestHandler):
    """Handler HTTP que captura o callback OAuth."""

    auth_code: str | None = None
    error: str | None = None

    def do_GET(self) -> None:
        parsed = urlparse(self.path)
        params = parse_qs(parsed.query)

        if "code" in params:
            _CallbackHandler.auth_code = params["code"][0]
            self._respond_html(
                "<h1>Login realizado!</h1><p>Pode fechar esta aba e voltar ao terminal.</p>"
            )
        elif "error" in params:
            error_desc = params.get("error_description", params.get("error", ["Erro desconhecido"]))
            _CallbackHandler.error = error_desc[0] if isinstance(error_desc, list) else error_desc
            self._respond_html(f"<h1>Erro no login</h1><p>{_CallbackHandler.error}</p>")
        else:
            self._respond_html("<h1>Requisição inesperada</h1>")

    def _respond_html(self, body: str) -> None:
        html = f"""<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>TecJustica CLI</title>
<style>body{{font-family:system-ui;display:flex;justify-content:center;
align-items:center;height:100vh;margin:0;background:#f5f5f5}}
div{{text-align:center;padding:2rem;background:white;border-radius:12px;
box-shadow:0 2px 8px rgba(0,0,0,.1)}}</style></head>
<body><div>{body}</div></body></html>"""
        self.send_response(200)
        self.send_header("Content-Type", "text/html; charset=utf-8")
        self.end_headers()
        self.wfile.write(html.encode())

    def log_message(self, format: str, *args: Any) -> None:
        pass  # silencia logs do HTTPServer


def oauth_browser_login() -> str:
    """Executa o fluxo OAuth PKCE completo via navegador.

    Returns:
        Access token do Supabase.

    Raises:
        RuntimeError: se o login falhar ou timeout.
    """
    # 1. Descobrir endpoints OAuth
    discovery = _discover_oauth()
    authorization_endpoint = discovery["authorization_endpoint"]
    token_endpoint = discovery["token_endpoint"]
    registration_endpoint = discovery["registration_endpoint"]

    # 2. Servidor local para callback
    port = _find_free_port()
    redirect_uri = f"http://127.0.0.1:{port}/callback"

    # 3. Registrar cliente (DCR)
    client_info = _register_client(registration_endpoint, redirect_uri)
    client_id = client_info["client_id"]

    # 4. Gerar PKCE
    code_verifier, code_challenge = _generate_pkce()

    # 5. Montar URL de autorização
    state = secrets.token_urlsafe(32)
    auth_params = urlencode(
        {
            "response_type": "code",
            "client_id": client_id,
            "redirect_uri": redirect_uri,
            "code_challenge": code_challenge,
            "code_challenge_method": "S256",
            "state": state,
            "scope": "openid email profile",
        }
    )
    auth_url = f"{authorization_endpoint}?{auth_params}"

    # 6. Iniciar servidor local e abrir navegador
    _CallbackHandler.auth_code = None
    _CallbackHandler.error = None

    server = HTTPServer(("127.0.0.1", port), _CallbackHandler)
    server.timeout = CALLBACK_TIMEOUT

    webbrowser.open(auth_url)

    # Esperar um único request (o callback)
    def _serve() -> None:
        server.handle_request()

    thread = threading.Thread(target=_serve, daemon=True)
    thread.start()
    thread.join(timeout=CALLBACK_TIMEOUT)
    server.server_close()

    if _CallbackHandler.error:
        raise RuntimeError(f"Erro no login: {_CallbackHandler.error}")

    if not _CallbackHandler.auth_code:
        raise RuntimeError("Timeout: login não completado. Tente novamente.")

    # 7. Trocar authorization code por token
    token_resp = httpx.post(
        token_endpoint,
        data={
            "grant_type": "authorization_code",
            "code": _CallbackHandler.auth_code,
            "redirect_uri": redirect_uri,
            "client_id": client_id,
            "code_verifier": code_verifier,
        },
        timeout=15,
    )

    if token_resp.status_code != 200:
        raise RuntimeError(f"Erro ao obter token: {token_resp.text[:300]}")

    token_data = token_resp.json()
    access_token = token_data.get("access_token")
    if not access_token:
        raise RuntimeError("Resposta sem access_token do servidor OAuth.")

    return access_token
