"""
Error Handlers for FastAPI Application

Updated for Python 3.12 with improved error handling, logging, and structure.
"""
import logging
from datetime import datetime
from typing import Dict, List, Tuple, Union

from fastapi import Request
from fastapi.exceptions import HTTPException, RequestValidationError
from starlette import status
from starlette.responses import JSONResponse

from src.core.exceptions import APIException
from src.apps.base.schemas.responses import ErrorDetail, ErrorResponseModel

logger = logging.getLogger(__name__)


async def api_exception_handler(request: Request, exc: APIException) -> JSONResponse:
    """
    Custom error handler for APIException.
    
    Maintains uniform response structure throughout the application
    with enhanced logging and request tracking.
    """
    # Get request ID from middleware if available
    request_id = getattr(request.state, 'request_id', None)
    
    # Log the error with context
    logger.error(
        f"API Exception: {exc.message} | "
        f"Status: {exc.status_code} | "
        f"Module: {exc.module} | "
        f"RequestID: {request_id} | "
        f"Path: {request.url.path} | "
        f"Method: {request.method}"
    )
    
    # Create standardized error response
    error_response = ErrorResponseModel(
        status_code=exc.status_code,
        message=exc.message,
        error=exc.error,
        timestamp=exc.timestamp,
        request_id=request_id or exc.request_id
    )
    
    return JSONResponse(
        status_code=exc.status_code,
        content=error_response.model_dump()
    )


async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
    """
    Handler for standard FastAPI HTTPExceptions.
    
    Converts HTTPExceptions to our standardized error response format.
    """
    request_id = getattr(request.state, 'request_id', None)
    timestamp = datetime.utcnow().isoformat() + "Z"
    
    # Log the HTTP exception
    logger.warning(
        f"HTTP Exception: {exc.detail} | "
        f"Status: {exc.status_code} | "
        f"RequestID: {request_id} | "
        f"Path: {request.url.path} | "
        f"Method: {request.method}"
    )
    
    # Create error details from HTTPException
    error_details = {
        "type": "HTTPException",
        "detail": exc.detail,
        "headers": getattr(exc, 'headers', None)
    }
    
    error_response = ErrorResponseModel(
        status_code=exc.status_code,
        message=exc.detail or "HTTP error occurred",
        error=error_details,
        timestamp=timestamp,
        request_id=request_id
    )
    
    return JSONResponse(
        status_code=exc.status_code,
        content=error_response.model_dump()
    )


def parse_validation_error(exc: RequestValidationError) -> Tuple[List[ErrorDetail], str]:
    """
    Parse Pydantic validation errors into structured error details.
    
    Converts RequestValidationError into a list of ErrorDetail objects
    with improved field name handling and error message formatting.
    
    Args:
        exc: The RequestValidationError from Pydantic
        
    Returns:
        Tuple of (parsed_errors, last_error_message)
    """
    parsed_errors: List[ErrorDetail] = []
    last_error_message = "Request validation failed"
    
    for error_dict in exc.errors():
        # Extract field location and name
        error_loc = error_dict.get("loc", ())
        field_name = str(error_loc[-1]) if error_loc else "unknown"
        
        # Create dot-notation location path, removing 'body.' prefix
        location_path = ".".join(str(loc) for loc in error_loc)
        clean_location = location_path.replace("body.", "")
        
        # Format error message with better field name handling
        raw_message = error_dict.get("msg", "Invalid value")
        formatted_message = (
            raw_message
            .replace("this value", f"'{field_name}'")
            .replace("value", f"'{field_name}'")
            .replace("values", f"'{field_name}'")
        )
        
        # Create structured error detail
        error_detail = ErrorDetail(
            field=field_name,
            message=formatted_message,
            code=error_dict.get("type", "validation_error")
        )
        
        parsed_errors.append(error_detail)
        last_error_message = formatted_message
    
    return parsed_errors, last_error_message


async def validation_error_handler(
    request: Request, exc: RequestValidationError
) -> JSONResponse:
    """
    Handler for Pydantic validation errors.
    
    Converts RequestValidationError into standardized error response
    with detailed field-level error information.
    """
    request_id = getattr(request.state, 'request_id', None)
    timestamp = datetime.utcnow().isoformat() + "Z"
    
    # Parse validation errors
    parsed_errors, summary_message = parse_validation_error(exc)
    
    # Log validation error with details
    logger.warning(
        f"Validation Error: {summary_message} | "
        f"Fields: {len(parsed_errors)} | "
        f"RequestID: {request_id} | "
        f"Path: {request.url.path} | "
        f"Method: {request.method}"
    )
    
    # Create error response
    error_response = ErrorResponseModel(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        message=summary_message,
        error=[error.model_dump() for error in parsed_errors],
        timestamp=timestamp,
        request_id=request_id
    )
    
    return JSONResponse(
        status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
        content=error_response.model_dump()
    )


async def general_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    """
    Catch-all handler for unhandled exceptions.
    
    Provides a safety net for any unexpected errors while maintaining
    consistent error response structure.
    """
    request_id = getattr(request.state, 'request_id', None)
    timestamp = datetime.utcnow().isoformat() + "Z"
    
    # Log the unexpected error with full context
    logger.error(
        f"Unhandled Exception: {type(exc).__name__}: {str(exc)} | "
        f"RequestID: {request_id} | "
        f"Path: {request.url.path} | "
        f"Method: {request.method}",
        exc_info=True  # Include stack trace
    )
    
    # Create generic error response (don't expose internal error details)
    error_response = ErrorResponseModel(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        message="An unexpected error occurred",
        error={
            "type": "InternalServerError",
            "detail": "Please try again later or contact support"
        },
        timestamp=timestamp,
        request_id=request_id
    )
    
    return JSONResponse(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        content=error_response.model_dump()
    )


# Dictionary mapping exception types to their handlers
EXCEPTION_HANDLERS = {
    APIException: api_exception_handler,
    HTTPException: http_exception_handler,
    RequestValidationError: validation_error_handler,
    Exception: general_exception_handler,  # Catch-all handler
}
