"""
Global Error Handling Middleware
Catches all unhandled exceptions and standardizes API error responses.
"""

import logging
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response, JSONResponse
from fastapi import HTTPException
from starlette.exceptions import HTTPException as StarletteHTTPException

# Import APIKeyError for handling
from middleware.api_key_auth import APIKeyError

from core.logging import get_logger

logger = get_logger(__name__)


def format_error_response(
    status_code: int,
    message: str,
    error_code: str = None,
    details: dict = None,
    request_id: str = "unknown",
    headers: dict = None,
) -> JSONResponse:
    """
    Standardizes the error response format.
    Format: {error: "CODE", message: "...", details: {...}}
    """
    if not error_code:
        error_code = _map_http_status_to_code(status_code)

    content = {"error": error_code, "message": message, "details": details or {}}

    # Always include request_id in details if not present
    if "request_id" not in content["details"]:
        content["details"]["request_id"] = request_id

    return JSONResponse(status_code=status_code, content=content, headers=headers)


def _map_http_status_to_code(status_code: int) -> str:
    """Map HTTP status codes to architectural error codes."""
    mapping = {
        400: "INVALID_FORMAT",
        401: "UNAUTHORIZED",
        403: "FORBIDDEN",
        404: "NOT_FOUND",
        405: "METHOD_NOT_ALLOWED",
        413: "FILE_TOO_LARGE",
        422: "VALIDATION_ERROR",
        429: "QUOTA_EXCEEDED",
        502: "PROVIDER_ERROR",
        503: "SERVICE_UNAVAILABLE",
    }
    return mapping.get(status_code, "INTERNAL_ERROR")


class ErrorHandlingMiddleware(BaseHTTPMiddleware):
    """
    Catch all unhandled exceptions (Exception) that bubble up to the top.
    Note: HTTPException is often caught by FastAPI handlers before reaching here.
    """

    async def dispatch(self, request: Request, call_next) -> Response:
        try:
            return await call_next(request)
        except APIKeyError as e:
            # Handle APIKeyError with structured response using to_response()
            request_id = getattr(request.state, "request_id", "unknown")
            logger.info(f"[{request_id}] API Key authentication error: {e.code}")
            return e.to_response()
        except Exception as e:
            request_id = getattr(request.state, "request_id", "unknown")

            # If it's already an HTTPException, we might want to handle it specifically if it leaked through
            if isinstance(e, (HTTPException, StarletteHTTPException)):
                detail = e.detail if hasattr(e, "detail") and e.detail else {}
                if isinstance(detail, dict):
                    return format_error_response(
                        status_code=e.status_code,
                        message=detail.get("message", "Une erreur s'est produite."),
                        error_code=detail.get("error"),
                        request_id=request_id,
                    )
                return format_error_response(
                    status_code=e.status_code,
                    message=str(detail) if detail else "Une erreur s'est produite.",
                    request_id=request_id,
                )

            # Log the full stack trace for internal debugging
            logger.exception(f"[{request_id}] Unhandled internal exception: {str(e)}")

            # Return generic error in French to user (AC4, AC5)
            return format_error_response(
                status_code=500,
                message="Une erreur inattendue s'est produite. Veuillez réessayer plus tard.",
                error_code="INTERNAL_ERROR",
                request_id=request_id,
            )
