"""
sudo.views
~~~~~~~~~~

:copyright: (c) 2020 by Matt Robenolt.
:license: BSD, see LICENSE for more details.
"""
try:
    from urllib.parse import urlparse, urlunparse
except ImportError:  # pragma: no cover
    # Python 2 fallback
    from urlparse import urlparse, urlunparse  # noqa

from django.contrib.auth.decorators import login_required
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponseRedirect, QueryDict
from django.shortcuts import resolve_url
from django.template.response import TemplateResponse
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.views.generic import View
from django.utils.decorators import method_decorator
from django.utils.http import is_safe_url
from django.utils.module_loading import import_string

from sudo.settings import REDIRECT_FIELD_NAME, REDIRECT_URL, REDIRECT_TO_FIELD_NAME, URL
from sudo.utils import grant_sudo_privileges
from sudo.forms import SudoForm


class SudoView(View):
    """
    The default view for the sudo mode page. The role of this page is to
    prompt the user for their password again, and if successful, redirect
    them back to ``next``.
    """

    form_class = SudoForm
    template_name = "sudo/sudo.html"
    extra_context = None

    def handle_sudo(self, request, redirect_to, context):
        return request.method == "POST" and context["form"].is_valid()

    def grant_sudo_privileges(self, request, redirect_to):
        grant_sudo_privileges(request)
        # Restore the redirect destination from the GET request
        redirect_to = request.session.pop(REDIRECT_TO_FIELD_NAME, redirect_to)
        # Double check we're not redirecting to other sites
        if not is_safe_url(url=redirect_to, host=request.get_host()):
            redirect_to = resolve_url(REDIRECT_URL)
        return HttpResponseRedirect(redirect_to)

    @method_decorator(sensitive_post_parameters())
    @method_decorator(never_cache)
    @method_decorator(csrf_protect)
    @method_decorator(login_required)
    def dispatch(self, request):
        redirect_to = request.GET.get(REDIRECT_FIELD_NAME, REDIRECT_URL)

        # Make sure we're not redirecting to other sites
        if not is_safe_url(url=redirect_to, host=request.get_host()):
            redirect_to = resolve_url(REDIRECT_URL)

        if request.is_sudo():
            return HttpResponseRedirect(redirect_to)

        if request.method == "GET":
            request.session[REDIRECT_TO_FIELD_NAME] = redirect_to

        context = {
            "form": self.form_class(request.user, request.POST or None),
            "request": request,
            REDIRECT_FIELD_NAME: redirect_to,
        }
        if self.handle_sudo(request, redirect_to, context):
            return self.grant_sudo_privileges(request, redirect_to)
        if self.extra_context is not None:
            context.update(self.extra_context)
        return TemplateResponse(request, self.template_name, context)


def sudo(request, **kwargs):
    return SudoView(**kwargs).dispatch(request)


def redirect_to_sudo(next_url, sudo_url=None):
    """
    Redirects the user to the login page, passing the given 'next' page
    """
    if sudo_url is None:
        sudo_url = URL

    try:
        # django 1.10 and greater can't resolve the string 'sudo.views.sudo' to a URL
        # https://docs.djangoproject.com/en/1.10/releases/1.10/#removed-features-1-10
        sudo_url = import_string(sudo_url)
    except (ImportError, ImproperlyConfigured):
        pass  # wasn't a dotted path

    sudo_url_parts = list(urlparse(resolve_url(sudo_url)))

    querystring = QueryDict(sudo_url_parts[4], mutable=True)
    querystring[REDIRECT_FIELD_NAME] = next_url
    sudo_url_parts[4] = querystring.urlencode(safe="/")

    return HttpResponseRedirect(urlunparse(sudo_url_parts))