from typing import Callable

from cms.views import details
from django.conf import settings
from django.http import HttpRequest, Http404
from django.http.response import HttpResponseBase
from django.shortcuts import render

__all__ = ('handler403', 'handler404', 'handler500', 'handler503', 'get_error_handler')


def get_error_handler(code: int) -> Callable[[HttpRequest], HttpResponseBase]:
    """
    Decorator creating error handler for specific HTTP status code.

    When CMS page with slug ``errorCODE`` exists, then is shown that
    page, otherwise generic one. Page is not cached because of that.

    Possible slugs:

     * error403 (forbidden)
     * error404 (not found)
     * error503 (service unavailable)
    """
    # pylint: disable=missing-docstring,unused-argument
    def error_handler(request: HttpRequest, *args, **kwds) -> HttpResponseBase:
        slug = 'error{}'.format(code)

        if hasattr(request, '_current_page_cache'):
            delattr(request, '_current_page_cache')

        try:
            response = details(request, slug)
        except Http404:
            response = render(request, 'cms_qe/error.html', context={'slug': slug})

        # In debug any response with status_code 404 is transformed to Django error
        # page and is not possible to check that page. We want to be able to check
        # not found page in debug as well.
        if response and not settings.DEBUG:
            response.status_code = code

        return response
    return error_handler


# pylint: disable=invalid-name
handler403 = get_error_handler(403)
handler404 = get_error_handler(404)
handler503 = get_error_handler(503)


def handler500(request: HttpRequest) -> HttpResponseBase:
    """
    When application fail on internal error, Django CMS has problem
    to render any page. They don't have any API for rendering any page
    so it's imposible to get it right. Solution could be just redirect
    to page with given slug but it could also hang in infinite loop of
    redirection if there would be problem on page with slug error500.

    That's why we simply just use different template which cannot be
    changed in admin by user. To override default template, just create
    your own ``cms_qe/internal_error.html`` or change ``handler500``
    to your own view.
    """
    return render(request, 'cms_qe/internal_error.html')
