"""FastAPI ``Depends()`` helpers for mgf-common-shaped data.

Each function is a plain callable suitable for use as a FastAPI
dependency::

    from fastapi import Depends, FastAPI
    from mgf.fastapi import get_app_context, get_request_id

    @app.get("/whoami")
    async def whoami(
        ctx: AppContext = Depends(get_app_context),
        request_id: str = Depends(get_request_id),
    ) -> dict[str, str]:
        return {
            "app": ctx.app_name,
            "request": request_id,
        }

The dependencies don't construct anything — they read state set by
:func:`setup_lifespan` and :class:`RequestIdMiddleware`. Missing
state raises :class:`ApiprobeConfigError`-shaped errors so the
consumer's exception translation maps them cleanly.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from mgf.common.exceptions import AppConfigError
from starlette.requests import (
    Request,  # noqa: TC002 — FastAPI needs runtime type for Depends introspection
)

if TYPE_CHECKING:
    from mgf.common import AppContext
    from mgf.common.settings import MgfSettings


def get_app_context(request: Request) -> AppContext:
    """Return the active :class:`AppContext` set by :func:`setup_lifespan`.

    Raises :class:`AppConfigError` if the lifespan hasn't run (test
    fixtures that bypass lifespan, missing ``setup_lifespan(...)``
    in the FastAPI constructor).
    """
    from mgf.common import AppContext as _AppContext

    ctx = getattr(request.app.state, "mgf_context", None)
    if ctx is None:
        raise AppConfigError(
            "mgf.fastapi: app.state.mgf_context is unset. "
            "Did you pass `lifespan=setup_lifespan(...)` to FastAPI()?"
        )
    if not isinstance(ctx, _AppContext):
        raise AppConfigError(
            f"mgf.fastapi: app.state.mgf_context is not an "
            f"AppContext (got {type(ctx).__name__}). Use "
            f"setup_lifespan() to wire it correctly."
        )
    return ctx


def get_settings(request: Request) -> MgfSettings:
    """Return the :class:`MgfSettings` bound to the active context.

    Convenience wrapper over :func:`get_app_context` — same error
    semantics if the lifespan didn't run.
    """
    return get_app_context(request).settings


def get_request_id(request: Request) -> str:
    """Return the current request id set by :class:`RequestIdMiddleware`.

    Returns an empty string if the middleware isn't installed (the
    consumer's choice — empty string is a valid value, not an
    error). Consumers that want a hard error should raise from the
    endpoint::

        @app.get("/x")
        async def my_endpoint(rid: str = Depends(get_request_id)):
            if not rid:
                raise AppConfigError("RequestIdMiddleware not installed")
    """
    return getattr(request.state, "request_id", "")


__all__ = [
    "get_app_context",
    "get_request_id",
    "get_settings",
]
