import logging

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import JSONResponse
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
from sqlalchemy.exc import DBAPIError

from app.config import settings
from app.dependencies import limiter
from app.routers import (
    blog,
    care_services,
    collections,
    health,
    products,
    site,
    submissions,
)

logger = logging.getLogger("larcare")

RATE_LIMIT_MSG = "rate limit exceeded"


def create_app() -> FastAPI:
    app = FastAPI(
        title="LarCare API",
        version="1.0.0",
        docs_url="/api/docs" if settings.debug else None,
        redoc_url=None,
        openapi_url="/api/openapi.json" if settings.debug else None,
    )

    app.state.limiter = limiter
    app.add_middleware(SlowAPIMiddleware)
    app.add_middleware(GZipMiddleware, minimum_size=1000)

    # Same-origin in production via the Nginx path split, so CORS is a
    # safety net for staging/preview hosts rather than a load-bearing config.
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.allowed_origins,
        allow_credentials=False,
        allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE"],
        allow_headers=["Content-Type", "Authorization"],
    )

    @app.exception_handler(RateLimitExceeded)
    async def rate_limit_handler(request: Request, exc: RateLimitExceeded):
        return JSONResponse({"detail": "rate limit exceeded"}, status_code=429)

    @app.exception_handler(DBAPIError)
    async def dbapi_error_handler(request: Request, exc: DBAPIError):
        orig = getattr(exc, "orig", None)
        sqlstate = getattr(orig, "sqlstate", None) or getattr(orig, "args", [None])[0]
        message = str(getattr(orig, "args", ["", ""])[-1] or "")

        # 45000 == deliberate SIGNAL from one of our procedures
        if sqlstate == "45000" or "45000" in str(orig):
            status = 429 if RATE_LIMIT_MSG in message else 422
            return JSONResponse({"detail": message}, status_code=status)

        logger.exception("unhandled database error", extra={"path": request.url.path})
        return JSONResponse(
            {"detail": "A server error occurred. Please try again."},
            status_code=500,
        )

    for module in (site, care_services, blog, products, collections,
                   submissions, health):
        app.include_router(module.router)

    return app


app = create_app()
