"""
Built-in identity for FastAPI — Django-equivalent automatic `request.state.user`.

Opt in via `Depends(mizan_auth())` on a route/router, or mount `MizanAuthMiddleware`
app-wide. Both decode a bearer-JWT (`Authorization: Bearer`) or MWT (`X-Mizan-Token`)
via the shared core and set `request.state.user`. A present-but-invalid token is
rejected (401) rather than silently downgraded — the `INVALID` sentinel contract.

If you'd rather resolve identity yourself, set `request.state.user` upstream and skip
these; dispatch reads it directly.
"""

from __future__ import annotations

from typing import Callable

from fastapi import Request
from starlette.middleware.base import BaseHTTPMiddleware

from mizan_core.auth import INVALID, authenticate
from mizan_core.errors import Unauthorized

from .config import get_config


def _resolve(request: Request) -> None:
    ident = authenticate(request.headers, get_config(request).auth)
    if ident is INVALID:
        raise Unauthorized("Invalid or expired token")
    if ident is not None:
        request.state.user = ident


def mizan_auth() -> Callable:
    """FastAPI dependency that populates `request.state.user` from a token."""
    async def _dep(request: Request) -> None:
        _resolve(request)
    return _dep


class MizanAuthMiddleware(BaseHTTPMiddleware):
    """App-wide variant of `mizan_auth` — resolves identity on every request."""

    async def dispatch(self, request, call_next):
        try:
            _resolve(request)
        except Unauthorized:
            from .router import _no_store
            from mizan_core.errors import ErrorCode
            return _no_store(
                {"error": {"code": ErrorCode.UNAUTHORIZED.value, "message": "Invalid or expired token"}},
                status_code=401,
            )
        return await call_next(request)
