"""Django REST Framework adapter — one config-driven authentication class that
enables whichever paths are configured:

  * SERVICE path on  when settings.ANDREA_SVC_KEYS is set (str | {kid: secret}).
  * USER/OIDC path on when settings.OIDC_ISSUER + settings.OIDC_JWKS_URL are set.

So `chat` (API-key only) and the `viewer` (OIDC only) use the SAME class with
different settings. request.user / request.auth become the core Principal
(.id == sub/key-id, .claims, .token for pass-through). Subclass + override
`build_identity` to return an app-specific object (e.g. the viewer's grant-bearing
IdentityAuth) while reusing this validation. Fail-closed: no credential -> None
(DRF 401 via authenticate_header); bad credential -> AuthenticationFailed."""
from __future__ import annotations

import threading

from .core import DEFAULT_API_KEY_HEADER, OIDCValidator, Principal, authenticate

_oidc = None
_oidc_lock = threading.Lock()


def _get_oidc(settings):
    issuer = getattr(settings, "OIDC_ISSUER", "") or ""
    jwks = getattr(settings, "OIDC_JWKS_URL", "") or ""
    if not (issuer and jwks):
        return None
    global _oidc
    if _oidc is None:
        with _oidc_lock:
            if _oidc is None:
                _oidc = OIDCValidator(
                    issuer=issuer, jwks_url=jwks,
                    audience=getattr(settings, "OIDC_AUDIENCE", None) or None,
                    jwks_ttl=float(getattr(settings, "OIDC_JWKS_TTL", 3600)),
                    http_timeout=float(getattr(settings, "OIDC_HTTP_TIMEOUT", 5)))
    return _oidc


def _base_authentication():
    from rest_framework import authentication  # lazy
    return authentication.BaseAuthentication


def __getattr__(name):
    # Lazily synthesize the DRF class so importing this module never requires DRF
    # until the class is actually referenced.
    if name != "ServiceOrOIDCAuthentication":
        raise AttributeError(name)
    from rest_framework import authentication, exceptions

    class ServiceOrOIDCAuthentication(authentication.BaseAuthentication):
        def build_identity(self, principal: Principal):
            """Override to return an app-specific identity. Default = the Principal."""
            return principal

        def authenticate(self, request):
            from django.conf import settings
            res = authenticate(
                lambda h: request.headers.get(h, ""),
                service_keys=getattr(settings, "ANDREA_SVC_KEYS", None),
                api_key_header=getattr(settings, "ANDREA_API_KEY_HEADER",
                                       DEFAULT_API_KEY_HEADER),
                oidc=_get_oidc(settings))
            if res.principal is None:
                if res.reason == "no credential":
                    return None                       # -> 401 (default-deny perms)
                raise exceptions.AuthenticationFailed(res.reason)
            identity = self.build_identity(res.principal)
            return (identity, identity)

        def authenticate_header(self, request):
            return 'Bearer realm="andrea"'

    globals()["ServiceOrOIDCAuthentication"] = ServiceOrOIDCAuthentication
    return ServiceOrOIDCAuthentication
