"""FastAPI application.

The API is mounted behind Caddy at /api (design D4), so every route is declared with that
prefix and the browser and API share one origin — there is no CORS configuration.
"""

import logging
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager

from fastapi import FastAPI

from app.config import get_settings
from app.routers import (
    admin_cards,
    admin_data,
    admin_events,
    admin_guests,
    admin_messaging,
    admin_stats,
    admin_users,
    admin_wedding,
    auth,
    calendar,
    health,
    invitations,
    rsvp,
    unsubscribe,
)

settings = get_settings()
logging.basicConfig(level=settings.log_level)


@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
    """Provision the bootstrap super admin before serving (design D8).

    Failing to provision must not take the API down: an operator locked out of the roster
    can still be handed a row by hand, whereas an API that will not start leaves them with
    nothing at all. So this logs and continues.
    """
    from app.db import get_session_factory
    from app.services.bootstrap import ensure_bootstrap_super_admin

    try:
        async with get_session_factory()() as session:
            await ensure_bootstrap_super_admin(session)
    except Exception:  # pragma: no cover - startup resilience
        logging.getLogger(__name__).exception("bootstrap super admin provisioning failed")
    yield


app = FastAPI(
    title="Wedding RSVP API",
    version="0.1.0",
    docs_url="/api/docs" if not settings.is_prod else None,
    redoc_url=None,
    # Withdrawn in production alongside the docs page. Leaving the schema up while hiding
    # the UI that renders it is not a hardening measure: the JSON is the interesting half —
    # every admin route, its parameters and its response shape, served unauthenticated.
    # `make client` regenerates the frontend types against a dev stack, so nothing needs it
    # in production.
    openapi_url=None if settings.is_prod else "/api/openapi.json",
    lifespan=lifespan,
)

app.include_router(health.router, prefix="/api")
app.include_router(invitations.router, prefix="/api")
app.include_router(rsvp.router, prefix="/api")
app.include_router(calendar.router, prefix="/api")
app.include_router(auth.router, prefix="/api")
app.include_router(admin_stats.router, prefix="/api")
app.include_router(admin_guests.router, prefix="/api")
app.include_router(admin_data.router, prefix="/api")
app.include_router(admin_messaging.router, prefix="/api")
app.include_router(admin_events.router, prefix="/api")
app.include_router(admin_cards.router, prefix="/api")
app.include_router(admin_users.router, prefix="/api")
app.include_router(admin_wedding.router, prefix="/api")
app.include_router(unsubscribe.router, prefix="/api")


@app.get("/api")
async def root() -> dict[str, str]:
    return {"service": "rsvp-api", "environment": settings.environment}
