from fastapi import Depends, HTTPException, Header,status
from fastapi.security import HTTPBearer
from jose import jwt, JWTError
from app.core import config
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
import asyncio
from app.core.logger import log_async
import httpx
import time
import redis.asyncio as redis

REDIS_URL = f"redis://{config.REDIS_HOST}:{config.REDIS_PORT}/0"
KEYCLOAK_URL = f"{config.KEYCLOAK_SERVER}/realms/{config.KEYCLOAK_REALM}/protocol/openid-connect/token"
CLIENT_SECRET = config.KEYCLOAK_CLIENT_SECRET
CACHE_KEY = "keycloak:access_token"

# Información de configuración de Keycloak
KEYCLOAK_SERVER_URL = config.KEYCLOAK_SERVER
REALM = config.KEYCLOAK_REALM
CLIENT_ID = config.KEYCLOAK_CLIENT_ID
KEYCLOAK_CLIENT_ID_CLIENTE_AUTH = config.KEYCLOAK_CLIENT_ID_CLIENTE_AUTH
KEYCLOAK_CLIENT_SECRET_CLIENTE_AUTH= config.KEYCLOAK_CLIENT_SECRET_CLIENTE_AUTH
REALM_PUBLIC_KEY = config.KEYCLOAK_REALM_PUBLIC_KEY  # La clave pública del Realm
ALGORITHM = "RS256"
bearer_scheme = HTTPBearer()

# Valida el token utilizando la clave pública directamente
def validate_token(token: str):
    try:
        # Agregar los prefijos requeridos para las claves RSA
        public_key = f"-----BEGIN PUBLIC KEY-----\n{REALM_PUBLIC_KEY}\n-----END PUBLIC KEY-----"
        
        # Decodificar el token JWT utilizando la clave pública y la audiencia (client_id)
        decoded_token = jwt.decode(token, public_key, algorithms=[ALGORITHM], audience=CLIENT_ID)
        
        return decoded_token
    except JWTError as e:
        raise HTTPException(status_code=401, detail="Token inválido o expirado")
    
    
    bearer_scheme = HTTPBearer()
public_key_cache = None
"""
async def get_public_key():
    global public_key_cache
    if public_key_cache:
        return public_key_cache

    url = f"{KEYCLOAK_ISSUER}/protocol/openid-connect/certs"
    async with httpx.AsyncClient() as client:
        r = await client.get(url)
        jwks = r.json()
        public_key_cache = jwt.algorithms.RSAAlgorithm.from_jwk(jwks["keys"][0])
        return public_key_cache
"""
async def verify_jwt(
    credentials: HTTPAuthorizationCredentials = Depends(bearer_scheme)
):
    token = credentials.credentials
    public_key = f"-----BEGIN PUBLIC KEY-----\n{REALM_PUBLIC_KEY}\n-----END PUBLIC KEY-----"
    try:
        decoded_token = jwt.decode(token, public_key, algorithms=[ALGORITHM], audience=CLIENT_ID)
        #asyncio.create_task(log_async("info", f"✅ Auth success for user: {username}"))
        return decoded_token
    except JWTError as e:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Token inválido",
         )
        
redis_client = redis.Redis.from_url(REDIS_URL)

async def get_client_token():
    print(KEYCLOAK_URL,KEYCLOAK_CLIENT_ID_CLIENTE_AUTH,KEYCLOAK_CLIENT_SECRET_CLIENTE_AUTH)
    # Intentar recuperar de Redis
    token = await redis_client.get(CACHE_KEY)
    if token:   
        return token.decode()

    # Si no está en Redis, pedir uno nuevo
    async with httpx.AsyncClient() as client:
        response = await client.post(
            KEYCLOAK_URL,
            data={
                "grant_type": "client_credentials",
                "client_id": KEYCLOAK_CLIENT_ID_CLIENTE_AUTH,
                "client_secret": KEYCLOAK_CLIENT_SECRET_CLIENTE_AUTH,
            }
        )
        response.raise_for_status()
        token_data = response.json()
        access_token = token_data["access_token"]
        expires_in = token_data.get("expires_in", 300)

        # Guardar en Redis (con expiración automática)
        await redis_client.set(CACHE_KEY, access_token, ex=expires_in - 10)

        return access_token
    
def validate_token(token: str):
    try:
        # Agregar los prefijos requeridos para las claves RSA
        public_key = f"-----BEGIN PUBLIC KEY-----\n{REALM_PUBLIC_KEY}\n-----END PUBLIC KEY-----"
        #print(CLIENT_ID)
        # Decodificar el token JWT utilizando la clave pública y la audiencia (client_id)
        decoded_token = jwt.decode(token, public_key, algorithms=[ALGORITHM], audience=CLIENT_ID)
        #print(decoded_token)

        return decoded_token
    except JWTError as e:
        raise HTTPException(status_code=401, detail="Token inválido o expirado")