from typing import *

import logging

from contextlib import contextmanager
from django.http import Http404
from django.core.exceptions import PermissionDenied, BadRequest
from django.utils.translation import pgettext
from pydantic_core import PydanticCustomError, InitErrorDetails
from pydantic import ValidationInfo, ValidationError


__all__ = (
    'make_validation_error',
    'prefix_validation_error',
    'prefix_validation',
    'format_pydantic_errors',
    'exceptions_handler',
)
logger = logging.getLogger(__name__)


def make_validation_error(
    type: str, message: str, loc: List[Hashable] = (),
    line_errors: List[InitErrorDetails] = [],
):
    return ValidationError.from_exception_data(
        message,
        line_errors=[InitErrorDetails(
            type=PydanticCustomError(type, message),
            loc=loc,
        )] + line_errors,
    )


def prefix_validation_error(exc: ValidationError, loc: Collection[str]):
    return ValidationError.from_exception_data(
        exc.title,
        line_errors=[
            InitErrorDetails(
                type=PydanticCustomError(error['type'], error['msg']),
                loc=tuple(loc) + error['loc'],
                input=error['input'],
                ctx=error.get('ctx'),
            )
            for error in exc.errors()
        ]
    )


@contextmanager
def prefix_validation(loc: Collection[str]):
    try:
        yield
    except ValidationError as e:
        raise prefix_validation_error(e, loc)


def format_pydantic_errors(exception: ValidationError):
    return [
        {
            'path': error['loc'],
            'message': error['msg'],
            'reason': error['type'],
        }
        for error in exception.errors()
    ]

def exceptions_handler(exc: Exception, context: Optional[dict] = None):
    reason = 'unknown'
    message = pgettext('wcd_settings:error', 'Unknown error.')
    code = 400

    if isinstance(exc, ValidationError):
        return format_pydantic_errors(exc), code

    message = str(exc)
    if isinstance(exc, Http404):
        reason = 'not_found'
        code = 404
    elif isinstance(exc, PermissionDenied):
        reason = 'permission_denied'
        code = 403
    elif isinstance(exc, BadRequest):
        reason = 'bad_request'
    else:
        logger.error(exc, stack_info=True, exc_info=True)

    return [{'path': [], 'message': message, 'reason': reason}], code
