from typing import Dict, List, Tuple, Union

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

from src.utils.helpers.response import ErrorResponseModel


class APIException(Exception):
    def __init__(
        self,
        module: str,
        error: Union[Dict, List],
        status_code: int = 400,
        message: str = "Something went wrong!",
    ):
        self.module = module
        self.error = error
        self.status_code = status_code
        self.message = message

    def __str__(self) -> str:
        return self.message


### GENERIC ERROR HANDLER ###
async def APIErrorHandler(request: Request, exc: APIException) -> JSONResponse:
    """Custom error handler for all API errors

    Maintains uniform response structure throughout application
    """
    code = exc.status_code
    error = ErrorResponseModel(error=exc.error, status_code=code, message=exc.message)
    return JSONResponse(status_code=code, content=error.model_dump())


### HTTP ERROR HANDLER ###
async def HttpErrorHandler(request: Request, exc: HTTPException) -> JSONResponse:
    """This is obsolete. Planning to remove this from application"""
    code = exc.status_code
    error = ErrorResponseModel(status_code=code, error=exc.__dict__, message=exc.detail)
    return JSONResponse(status_code=code, content=error.model_dump())


def parse_validation_error(exc: RequestValidationError) -> Tuple[List[Dict], str]:
    """Parses validation errors of Pydantic schema (dto) into custom error response"""
    parsed_errors: List[Dict] = []
    last_error_message: str = "Request validation failed"

    for _err in exc.errors():
        field_name = _err.get("loc", [])[-1]
        field_error_msg = (
            _err.get("msg", last_error_message)
            .replace("this value", str(field_name))
            .replace("value", str(field_name))
            .replace("values", str(field_name))
        )

        parsed_errors.append(
            {
                "name": str(field_name),
                "loc": ".".join([str(loc) for loc in _err.get("loc", [])]).replace("body.", ""),
                "error": field_error_msg,
            }
        )
        last_error_message = field_error_msg
    return parsed_errors, last_error_message


async def ValidationErrorHandler(request: Request, exc: RequestValidationError) -> JSONResponse:
    """REQUEST VALIDATION ERROR HANDLER

    Typically takes effect when a pydantic schema (dto) validation error occurs
    """
    code = status.HTTP_422_UNPROCESSABLE_ENTITY
    parsed_errors, last_error_message = parse_validation_error(exc)
    error = ErrorResponseModel(status_code=code, error=parsed_errors, message=last_error_message)
    return JSONResponse(status_code=code, content=error.model_dump())