"""FastAPI dependencies: `current_user` and `require_roles`.

The app mounts these without knowing anything about Keycloak beyond the OIDC env vars.
"""

from typing import Annotated

import jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from pydantic import BaseModel

from .config import OIDCSettings, get_settings
from .jwks import JWKSCache

_bearer = HTTPBearer(auto_error=True)
_jwks_cache: JWKSCache | None = None


def _cache(settings: OIDCSettings) -> JWKSCache:
    global _jwks_cache
    if _jwks_cache is None:
        _jwks_cache = JWKSCache(settings.jwks_uri, settings.jwks_ttl)
    return _jwks_cache


class CurrentUser(BaseModel):
    sub: str
    email: str | None = None
    tenant_id: str | None = None
    roles: list[str] = []
    raw: dict


def _dig(data: dict, dotted_path: str) -> object:
    cur: object = data
    for part in dotted_path.split("."):
        if not isinstance(cur, dict):
            return None
        cur = cur.get(part)
    return cur


async def current_user(
    creds: Annotated[HTTPAuthorizationCredentials, Depends(_bearer)],
    settings: Annotated[OIDCSettings, Depends(get_settings)],
) -> CurrentUser:
    token = creds.credentials
    try:
        header = jwt.get_unverified_header(token)
        key = await _cache(settings).get_key(header["kid"])
        claims = jwt.decode(
            token,
            key.key,
            algorithms=["RS256"],
            audience=settings.audience,
            issuer=settings.issuer,
        )
    except (jwt.PyJWTError, KeyError) as exc:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=f"invalid token: {exc}",
            headers={"WWW-Authenticate": "Bearer"},
        ) from exc

    roles = _dig(claims, settings.roles_claim_path) or []
    return CurrentUser(
        sub=claims["sub"],
        email=claims.get("email"),
        tenant_id=claims.get(settings.tenant_claim),
        roles=list(roles) if isinstance(roles, list) else [],
        raw=claims,
    )


def require_roles(*required: str):
    """Local authorization guard (Option A: the app decides, not Keycloak)."""

    async def _guard(user: Annotated[CurrentUser, Depends(current_user)]) -> CurrentUser:
        if not set(required).issubset(set(user.roles)):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"requires roles {required}",
            )
        return user

    return _guard
