"""
Obtención de un access token de Keycloak VÁLIDO para llamar a la API del Hub.

Con el patrón de sesión local (SIWEB) el Bearer de la app es un JWT propio (HS256)
que el Hub NO entiende. Para las llamadas server→Hub se usa el token KC guardado en
la sesión (valkey, vía cookie crm_sid), refrescándolo si está por expirar.
"""

import logging
import time
from typing import Optional

import httpx
from jose import jwt

from core.config import settings
from core import session_store

logger = logging.getLogger(__name__)


def _kc_exp_ok(token: str, leeway_seconds: int = 30) -> bool:
    """True si el token KC no está expirado (con margen)."""
    try:
        claims = jwt.get_unverified_claims(token)
        exp = claims.get("exp")
        return isinstance(exp, (int, float)) and (int(exp) - int(time.time())) > leeway_seconds
    except Exception:
        return False


async def get_hub_access_token(request) -> Optional[str]:
    """
    Devuelve un access token KC válido tomado de la sesión (valkey vía crm_sid),
    refrescándolo contra el Hub si está por expirar. None si no hay sesión.
    Best-effort: si el refresh falla, devuelve el token guardado (puede estar vencido).
    """
    sid = request.cookies.get("crm_sid") if request is not None else None
    if not sid:
        return None
    sess = session_store.get_session(sid)
    if not sess:
        return None

    access = sess.get("access_token")
    refresh = sess.get("refresh_token")

    if access and _kc_exp_ok(access):
        return access

    if refresh:
        try:
            async with httpx.AsyncClient(timeout=8.0) as client:
                r = await client.post(
                    f"{settings.HUB_URL}api/v1/auth/refresh",
                    json={"refresh_token": refresh},
                )
            if r.status_code == 200:
                data = r.json()
                new_access = data.get("access_token") or access
                session_store.update_session_tokens(sid, new_access, data.get("refresh_token") or refresh)
                return new_access
            logger.info("get_hub_access_token: Hub refresh devolvió %s", r.status_code)
        except Exception as exc:
            logger.warning("get_hub_access_token: refresh falló: %s", exc)

    return access
