from contextlib import asynccontextmanager

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse

from app import __version__
from app.api.v1 import api_router, health
from app.core.config import settings
from app.core.logging import configure_logging, get_logger
from shared.exceptions import AppError
from shared.responses import ErrorDetail, ErrorResponse

configure_logging()
logger = get_logger(__name__)


@asynccontextmanager
async def lifespan(app: FastAPI):
    logger.info("service_starting", extra={"service": settings.service_name})
    yield


def create_app() -> FastAPI:
    app = FastAPI(
        title="Identity & Access Service",
        version=__version__,
        description="سرویس احراز هویت و دسترسی (فاز ۲) — SSO با Keycloak",
        lifespan=lifespan,
    )
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.cors_origin_list,
        allow_origin_regex=r"https?://([a-z0-9-]+\.)*torbatyar\.ir",
        allow_credentials=False,
        allow_methods=["*"],
        allow_headers=["*"],
    )

    def _cors_headers(request: Request) -> dict[str, str]:
        """هدرهای CORS برای پاسخ‌های خطا.

        هندلر خطای عمومی (۵۰۰) بیرون از CORSMiddleware اجرا می‌شود، بنابراین
        هدر CORS را دستی اضافه می‌کنیم تا خطاهای سرور در مرورگر به‌اشتباه
        «خطای CORS» نمایش داده نشوند و پیام واقعی به کاربر برسد.
        """
        import re

        origin = request.headers.get("origin")
        if not origin:
            return {}
        if origin in settings.cors_origin_list or re.fullmatch(
            r"https?://([a-z0-9-]+\.)*torbatyar\.ir", origin
        ):
            return {
                "Access-Control-Allow-Origin": origin,
                "Vary": "Origin",
            }
        return {}

    @app.exception_handler(AppError)
    async def app_error_handler(request: Request, exc: AppError) -> JSONResponse:
        return JSONResponse(
            status_code=exc.status_code,
            content=ErrorResponse(
                error=ErrorDetail(code=exc.error_code, message=exc.message, details=exc.details)
            ).model_dump(),
            headers=_cors_headers(request),
        )

    @app.exception_handler(Exception)
    async def unhandled_handler(request: Request, exc: Exception) -> JSONResponse:
        logger.error("unhandled_exception", extra={"error": str(exc)}, exc_info=exc)
        return JSONResponse(
            status_code=500,
            content=ErrorResponse(
                error=ErrorDetail(code="internal_error", message="خطای داخلی سرور")
            ).model_dump(),
            headers=_cors_headers(request),
        )

    app.include_router(health.router)
    app.include_router(api_router, prefix=settings.api_v1_prefix)
    return app


app = create_app()
