"""Autenticacao JWT para Supabase Auth."""

from __future__ import annotations

import logging
import os
from typing import Any, ClassVar

import httpx
from jose import JWTError, jwt
from pydantic import BaseModel

logger = logging.getLogger("tecjustica-mcp")


class TokenPayload(BaseModel):
    """Payload do token JWT do Supabase."""

    sub: str  # user_id
    email: str | None = None
    phone: str | None = None
    role: str | None = None
    aud: str | None = None
    exp: int | None = None
    iat: int | None = None


class SupabaseJWTVerifier:
    """Verificador de JWT tokens do Supabase Auth."""

    def __init__(
        self,
        supabase_url: str | None = None,
        jwt_secret: str | None = None,
    ):
        self.supabase_url = supabase_url or os.getenv("SUPABASE_URL")
        self.jwt_secret = jwt_secret or os.getenv("SUPABASE_JWT_SECRET")

        if not self.supabase_url:
            raise ValueError("SUPABASE_URL is required")
        if not self.jwt_secret:
            raise ValueError("SUPABASE_JWT_SECRET is required")

        # Remove trailing slash
        self.supabase_url = self.supabase_url.rstrip("/")
        self.issuer = f"{self.supabase_url}/auth/v1"

        # Cache para JWKS (opcional, para validacao mais robusta)
        self._jwks_cache: dict[str, Any] | None = None

    async def get_jwks(self) -> dict[str, Any]:
        """Busca JWKS do Supabase (para validacao com chave publica)."""
        if self._jwks_cache:
            return self._jwks_cache

        jwks_url = f"{self.supabase_url}/auth/v1/.well-known/jwks.json"
        async with httpx.AsyncClient() as client:
            response = await client.get(jwks_url)
            response.raise_for_status()
            self._jwks_cache = response.json()
            return self._jwks_cache

    def verify_token(self, token: str) -> TokenPayload:
        """
        Verifica e decodifica um token JWT do Supabase.

        Args:
            token: Token JWT (sem prefixo 'Bearer ')

        Returns:
            TokenPayload com os dados do usuario

        Raises:
            JWTError: Se o token for invalido
        """
        try:
            # Decodifica usando o JWT secret do Supabase
            payload = jwt.decode(
                token,
                self.jwt_secret,
                algorithms=["HS256"],
                audience="authenticated",
                issuer=self.issuer,
            )

            return TokenPayload(
                sub=payload.get("sub", ""),
                email=payload.get("email"),
                phone=payload.get("phone"),
                role=payload.get("role"),
                aud=payload.get("aud"),
                exp=payload.get("exp"),
                iat=payload.get("iat"),
            )

        except JWTError as e:
            raise JWTError(f"Token invalido: {e}") from e

    def extract_user_id(self, token: str) -> str:
        """
        Extrai o user_id de um token JWT.

        Args:
            token: Token JWT

        Returns:
            user_id (UUID string)
        """
        payload = self.verify_token(token)
        return payload.sub


def get_jwt_verifier() -> SupabaseJWTVerifier:
    """Factory para criar instancia do verificador JWT."""
    return SupabaseJWTVerifier()


def extract_token_from_header(auth_header: str | None) -> str | None:
    """
    Extrai o token do header Authorization.

    Args:
        auth_header: Header no formato 'Bearer <token>'

    Returns:
        Token sem o prefixo, ou None se invalido
    """
    if not auth_header:
        return None

    if not auth_header.startswith("Bearer "):
        return None

    return auth_header[7:]  # Remove 'Bearer '


class TrustingTokenVerifier:
    """
    Token verifier que confia nos tokens upstream do Supabase OAuth Server.

    Como o fluxo OAuth ja autenticou o usuario com Supabase, podemos confiar
    no token retornado pelo token exchange. O FastMCP gerencia esses tokens
    de forma segura internamente.

    Este verifier e usado pelo OIDCProxy para validar tokens upstream.
    """

    # Atributo requerido pelo OIDCProxy
    required_scopes: ClassVar[list[str]] = []

    def __init__(self, supabase_url: str | None = None):
        """
        Inicializa o verifier.

        Args:
            supabase_url: URL do Supabase (para logs)
        """
        self.supabase_url = supabase_url or os.getenv("SUPABASE_URL", "")
        logger.info("TrustingTokenVerifier inicializado para %s", self.supabase_url)

    def verify_token(self, token: str) -> Any:
        """
        Verifica um token upstream do Supabase.

        Como o token foi obtido via OAuth token exchange valido,
        confiamos que ele e valido. Retornamos um objeto AccessToken
        compativel com FastMCP.

        Args:
            token: Token JWT do Supabase

        Returns:
            Objeto com informacoes do token, ou None se invalido
        """
        from fastmcp.server.auth import AccessToken

        logger.info("TrustingTokenVerifier.verify_token chamado")
        logger.info("Token recebido (primeiros 50 chars): %s...", token[:50] if token else "VAZIO")

        if not token:
            logger.warning("Token vazio recebido")
            return None

        try:
            # Decodifica sem validar para extrair claims
            # O token ja foi validado pelo Supabase durante o OAuth flow
            unverified = jwt.get_unverified_claims(token)
            logger.info(
                "Claims decodificados: sub=%s, aud=%s", unverified.get("sub"), unverified.get("aud")
            )

            # Extrai informacoes do token
            subject = unverified.get("sub", "unknown")
            scopes_raw = unverified.get("scope", "")
            scopes = scopes_raw.split() if isinstance(scopes_raw, str) else []

            # Expiracao como timestamp int (requerido pelo AccessToken)
            exp = unverified.get("exp")

            logger.info(
                "Token verificado com sucesso: sub=%s, scopes=%s, exp=%s",
                subject,
                scopes,
                exp,
            )

            # client_id deve ser o user_id (sub), nao o audience
            access_token = AccessToken(
                token=token,
                client_id=subject,  # user_id
                scopes=scopes,
                expires_at=exp,  # int timestamp, nao datetime
                claims=unverified,
            )
            logger.info("AccessToken criado com sucesso")
            return access_token

        except Exception as e:
            logger.error("Erro ao decodificar token: %s", e)
            import traceback

            logger.error("Traceback: %s", traceback.format_exc())
            return None
