"""
Global exception handlers.

Registered on the FastAPI app in main.py. These guarantee that NOTHING
ever reaches the client as a raw stack trace or an unstructured error —
every error response has the same shape (ErrorResponse), and every
unexpected exception is logged with full detail server-side while the
client sees only a safe, generic message.
"""

from fastapi import Request
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse

from app.core.exceptions import AppException
from app.core.logging_config import get_logger, request_id_ctx

logger = get_logger("app.exceptions")


async def app_exception_handler(request: Request, exc: AppException) -> JSONResponse:
    request_id = request_id_ctx.get()
    logger.warning(
        f"AppException [{exc.error_code}] on {request.url.path}: {exc.detail}",
        extra={"extra_data": exc.extra},
    )
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "error_code": exc.error_code,
            "message": exc.message,
            "request_id": request_id,
        },
    )


async def validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
    """
    Pydantic validation errors from request body parsing. We reshape
    FastAPI's default (verbose, implementation-detail-heavy) error format
    into the same ErrorResponse contract as every other error, so the
    client's error handling code only needs one code path.
    """
    request_id = request_id_ctx.get()
    first_error = exc.errors()[0] if exc.errors() else {}
    field = ".".join(str(loc) for loc in first_error.get("loc", []) if loc != "body")
    message = first_error.get("msg", "Invalid request data")
    full_message = f"{field}: {message}" if field else message

    logger.info(f"Validation failed on {request.url.path}: {full_message}")

    return JSONResponse(
        status_code=422,
        content={
            "error_code": "VALIDATION_FAILED",
            "message": full_message,
            "request_id": request_id,
        },
    )


async def unhandled_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    """
    The last line of defense. If we reach here, something we did not
    anticipate broke. Full traceback goes to logs/Sentry; the client gets
    a generic, safe message plus the request_id so they can report it.
    """
    request_id = request_id_ctx.get()
    logger.error(
        f"Unhandled exception on {request.url.path}: {exc}",
        exc_info=True,
    )
    return JSONResponse(
        status_code=500,
        content={
            "error_code": "INTERNAL_ERROR",
            "message": "An unexpected error occurred. Please try again or contact support "
                       "with the request id below.",
            "request_id": request_id,
        },
    )
