import logging

from cubicweb import AuthenticationError, Forbidden, Unauthorized, QueryError
from pyramid.config import Configurator
from pyramid.request import Request
from rql import RQLException
from yams import ValidationError, UnknownType

from cubicweb_api.httperrors import get_http_error, get_http_500_error
from cubicweb_api.transaction import InvalidTransaction

log = logging.getLogger(__name__)


class ApiException(Exception):
    def __init__(self, original_exception: Exception):
        self.original_exception = original_exception


def api_exception_view(api_exception: ApiException, request: Request):
    wrapped_exception = api_exception.original_exception
    exception_name = api_exception.original_exception.__class__.__name__
    if isinstance(wrapped_exception, ValidationError):
        wrapped_exception.translate(request.cw_cnx._)
    # RQL errors -> 400
    if isinstance(
        wrapped_exception,
        (ValidationError, QueryError, UnknownType, RQLException, InvalidTransaction),
    ):
        return get_http_error(
            400,
            exception_name,
            str(wrapped_exception),
        )
    # Authentication and Unauthorized -> 401
    if isinstance(wrapped_exception, (AuthenticationError, Unauthorized)):
        return get_http_error(
            401,
            exception_name,
            str(wrapped_exception),
        )
    # Forbidden -> 403
    if isinstance(wrapped_exception, Forbidden):
        return get_http_error(
            403,
            exception_name,
            str(wrapped_exception),
        )
    # Default case -> 500
    log.exception(
        f"Request to {request.path_qs} raised the following exception: ",
        exc_info=wrapped_exception,
    )
    return get_http_500_error()


def includeme(config: Configurator):
    config.add_exception_view(api_exception_view, context=ApiException)