"""
Application entry point.

`uvicorn app.main:app` (dev) or `gunicorn -k uvicorn.workers.UvicornWorker
app.main:app` (production, see deployment/systemd/) both load this file.

The lifespan context manager is where the production-fail-fast principle
is enforced: if reference data, the feature builder, or any model fails
to load, the application refuses to start at all rather than starting
in a broken state. This is intentional and correct — see
app/models_loader/registry.py for the reasoning.
"""

from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from prometheus_client import make_asgi_app

from app.api.v1.router import api_router
from app.core.config import get_settings
from app.core.exceptions import AppException
from app.core.logging_config import get_logger, setup_logging
from app.db.session import dispose_engine
from app.middleware.exception_handlers import (
    app_exception_handler,
    unhandled_exception_handler,
    validation_exception_handler,
)
from app.middleware.metrics import PrometheusMiddleware
from app.middleware.request_context import RequestContextMiddleware
from app.models_loader.registry import model_registry
from app.services.cache_service import cache_service
from app.services.feature_builder import feature_builder
from app.services.llm_service import llm_service
from app.services.reference_data import reference_data

settings = get_settings()

setup_logging()
logger = get_logger("app.main")


@asynccontextmanager
async def lifespan(app: FastAPI):
    logger.info(f"Starting {settings.APP_NAME} v{settings.APP_VERSION} ({settings.ENVIRONMENT})")

    if settings.is_production and not settings.ANTHROPIC_API_KEY:
        # Hard stop — a production deployment with no LLM key would silently
        # serve every single request with the fallback template, which is
        # not what was promised to the client. Fail loudly at boot instead.
        raise RuntimeError(
            "ANTHROPIC_API_KEY is not set. Refusing to start in production "
            "without it. Set the key or explicitly accept fallback-only mode "
            "by changing ENVIRONMENT for a controlled rollout."
        )

    # Order matters: reference data and feature builder are pure-Python/
    # pandas and load fast; models are the expensive, fail-prone step,
    # loaded last so the cheaper checks fail fast first.
    reference_data.load()
    feature_builder.load()
    model_registry.load_all()
    llm_service.init()
    await cache_service.init()

    logger.info("Startup complete — service is ready to accept traffic")

    yield

    logger.info("Shutting down — closing database connections")
    await dispose_engine()


app = FastAPI(
    title=settings.APP_NAME,
    version=settings.APP_VERSION,
    description=(
        "Production API for the AI Crop Decision Support System. "
        "Evaluates crop suitability for a given location and month using "
        "6 ML models, with LLM-generated explanations."
    ),
    lifespan=lifespan,
    docs_url="/docs" if not settings.is_production else None,
    redoc_url="/redoc" if not settings.is_production else None,
    # Auto-generated docs are disabled in production deliberately — an
    # interactive API explorer publicly reachable on the client's domain
    # is an unnecessary attack-surface and information-disclosure risk.
    # Re-enable behind auth if the client specifically wants it available.
)

app.add_middleware(
    CORSMiddleware,
    allow_origins=settings.cors_origins_list,
    allow_credentials=True,
    allow_methods=["GET", "POST"],
    allow_headers=["*"],
)
app.add_middleware(RequestContextMiddleware)
if settings.ENABLE_METRICS:
    app.add_middleware(PrometheusMiddleware)
    app.mount("/metrics", make_asgi_app())

app.add_exception_handler(AppException, app_exception_handler)
app.add_exception_handler(RequestValidationError, validation_exception_handler)
app.add_exception_handler(Exception, unhandled_exception_handler)

app.include_router(api_router, prefix=settings.API_V1_PREFIX)


@app.get("/")
async def root() -> dict:
    return {
        "service": settings.APP_NAME,
        "version": settings.APP_VERSION,
        "status": "running",
        "docs": "/docs" if not settings.is_production else "disabled in production",
    }
