"""OIDC (OpenID Connect) client setup — used for Authentik SSO today,
generic enough to register other providers later.

Authlib handles the Authorization Code + PKCE flow, including discovery
via the provider's `.well-known/openid-configuration` document. We keep
a single registered client named "authentik" regardless of label, so the
router code always knows where to find it.

If OIDC isn't fully configured the module stays inert — `is_enabled()`
returns False and the registry has no client. Callers must guard.
"""
from typing import Optional

from authlib.integrations.starlette_client import OAuth

from app.config import settings


PROVIDER_NAME = "authentik"


def is_configured() -> bool:
    """True when every required OIDC setting is present."""
    return bool(
        settings.oidc_issuer
        and settings.oidc_client_id
        and settings.oidc_client_secret
        and settings.oidc_redirect_uri
    )


def is_enabled() -> bool:
    """True when OIDC is both configured and the admin flipped it on."""
    return bool(settings.oidc_enabled) and is_configured()


def provider_label() -> str:
    return settings.oidc_provider_label or "Authentik"


def _build_oauth() -> OAuth:
    """Build the Authlib OAuth registry. Always safe to call; only
    registers the provider when credentials are present so importing
    this module never fails on a fresh install.
    """
    registry = OAuth()
    if not is_configured():
        return registry

    # Authentik exposes discovery at `{issuer}/.well-known/openid-configuration`.
    # Trailing slash handling varies by Authentik version, so normalise.
    issuer = settings.oidc_issuer.rstrip("/")
    discovery_url = f"{issuer}/.well-known/openid-configuration"

    registry.register(
        name=PROVIDER_NAME,
        client_id=settings.oidc_client_id,
        client_secret=settings.oidc_client_secret,
        server_metadata_url=discovery_url,
        client_kwargs={
            "scope": settings.oidc_scopes,
            # Force PKCE — cheap win for public clients, harmless for
            # confidential ones.
            "code_challenge_method": "S256",
        },
    )
    return registry


oauth: OAuth = _build_oauth()


def get_client() -> Optional[object]:
    """Return the registered provider client, or None when not configured."""
    if not is_configured():
        return None
    return oauth.create_client(PROVIDER_NAME)
