"""
Bridge auth Supabase <-> ERGON backend.

Responsabilidades:
- Validar JWTs emitidos por Supabase Auth usando SUPABASE_JWT_SECRET (HMAC SHA256)
- Sincronizar usuario en tabla local `usuarios` cuando se autentica via Supabase
  (crea fila si no existe, actualiza last_login_at si existe)
- Resolver session dict standard a partir de un JWT valido, compatible con
  el formato que ya consume _require_session() en servidor_local.py

Convivencia con auth legacy:
- Cookie ergon_session bcrypt sigue siendo valido (admin@demo.local, etc).
- _require_session() acepta AMBOS: cookie O Authorization: Bearer <jwt>.
- Un usuario tiene UNA fuente de verdad: o supabase_uid != NULL (auth via SB)
  o password_hash != NULL (auth legacy). Nunca los dos.
"""

import base64
import os
import sqlite3
from typing import Optional, Dict, Any

try:
    import jwt as pyjwt  # PyJWT
except ImportError:
    pyjwt = None


# Cache del secret decoded (Supabase devuelve base64, PyJWT necesita bytes)
_JWT_SECRET_CACHE: Optional[bytes] = None


def get_jwt_secret() -> Optional[bytes]:
    """
    Lee SUPABASE_JWT_SECRET del environment, decodifica base64 si aplica.
    Retorna bytes para PyJWT.decode().
    """
    global _JWT_SECRET_CACHE
    if _JWT_SECRET_CACHE is not None:
        return _JWT_SECRET_CACHE

    raw = os.environ.get("SUPABASE_JWT_SECRET", "").strip()
    if not raw:
        return None

    # Supabase entrega el secret en base64. Intentar decodear; si falla,
    # usar el raw como bytes (caso de secret hex u otro formato).
    try:
        _JWT_SECRET_CACHE = base64.b64decode(raw)
    except Exception:
        _JWT_SECRET_CACHE = raw.encode("utf-8")

    return _JWT_SECRET_CACHE


def validate_supabase_jwt(token: str) -> Optional[Dict[str, Any]]:
    """
    Valida un JWT emitido por Supabase Auth. Retorna el payload si es valido,
    o None si invalido/expirado/secret faltante.

    Payload tipico de Supabase:
        {
            "sub": "uuid-del-usuario",
            "email": "...",
            "role": "authenticated",
            "aud": "authenticated",
            "iss": "https://[ref].supabase.co/auth/v1",
            "iat": int, "exp": int,
            "app_metadata": {"provider": "google|facebook|email"},
            "user_metadata": {"full_name": "...", ...}
        }
    """
    if pyjwt is None:
        return None
    secret = get_jwt_secret()
    if secret is None:
        return None

    try:
        payload = pyjwt.decode(
            token,
            secret,
            algorithms=["HS256"],
            audience="authenticated",
            options={"verify_aud": True, "verify_exp": True},
        )
        return payload
    except pyjwt.InvalidTokenError:
        return None
    except Exception:
        return None


def sync_user_from_supabase(conn: sqlite3.Connection, payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
    """
    Sincroniza el usuario en la tabla local `usuarios` a partir de un JWT payload
    validado. Crea fila si no existe (rol cliente por default), actualiza
    last_login_at siempre.

    Retorna dict con shape compatible con session dict:
        {"id": int, "email": str, "rol": str, "nombre": str,
         "supabase_uid": str, "via": "supabase"}
    """
    supabase_uid = payload.get("sub")
    email = (payload.get("email") or "").lower().strip()
    if not supabase_uid or not email:
        return None

    user_metadata = payload.get("user_metadata") or {}
    nombre = (user_metadata.get("full_name") or user_metadata.get("name") or email.split("@")[0]).strip()

    cur = conn.cursor()
    # 1. Buscar por supabase_uid
    row = cur.execute(
        "SELECT id, email, rol, nombre FROM usuarios WHERE supabase_uid = ?",
        (supabase_uid,)
    ).fetchone()

    if row:
        # Usuario ya enlazado - actualizar last_login_at + nombre si cambio
        cur.execute(
            "UPDATE usuarios SET last_login_at = datetime('now') WHERE id = ?",
            (row[0],)
        )
        conn.commit()
        return {
            "id": row[0], "email": row[1], "rol": row[2], "nombre": row[3],
            "supabase_uid": supabase_uid, "via": "supabase"
        }

    # 2. No esta enlazado: buscar usuario legacy con mismo email
    row = cur.execute(
        "SELECT id, rol, nombre FROM usuarios WHERE email = ? AND supabase_uid IS NULL",
        (email,)
    ).fetchone()

    if row:
        # Enlazar usuario legacy a Supabase
        cur.execute(
            "UPDATE usuarios SET supabase_uid = ?, last_login_at = datetime('now') WHERE id = ?",
            (supabase_uid, row[0])
        )
        conn.commit()
        return {
            "id": row[0], "email": email, "rol": row[1], "nombre": row[2],
            "supabase_uid": supabase_uid, "via": "supabase"
        }

    # 3. Usuario nuevo: crear con rol cliente por default
    # password_hash queda NULL porque la auth la maneja Supabase
    cur.execute(
        """
        INSERT INTO usuarios (email, password_hash, nombre, rol, activo, supabase_uid, last_login_at)
        VALUES (?, NULL, ?, 'cliente', 1, ?, datetime('now'))
        """,
        (email, nombre, supabase_uid)
    )
    new_id = cur.lastrowid
    conn.commit()

    return {
        "id": new_id, "email": email, "rol": "cliente", "nombre": nombre,
        "supabase_uid": supabase_uid, "via": "supabase"
    }


def session_from_authorization_header(conn: sqlite3.Connection, auth_header: str) -> Optional[Dict[str, Any]]:
    """
    Punto de entrada single-call para el middleware del servidor.
    Recibe el valor crudo del header 'Authorization', valida el Bearer JWT,
    sincroniza el usuario y devuelve el session dict.
    """
    if not auth_header:
        return None
    parts = auth_header.split(None, 1)
    if len(parts) != 2 or parts[0].lower() != "bearer":
        return None
    token = parts[1].strip()
    payload = validate_supabase_jwt(token)
    if payload is None:
        return None
    return sync_user_from_supabase(conn, payload)
