from urllib.parse import urlparse

from django import http
from django.utils.deprecation import MiddlewareMixin
from django.utils.encoding import uri_to_iri

from wagtail.contrib.redirects import models
from wagtail.models import Site


def _get_redirect(request, path):
    if (
        "\0" in path
    ):  # reject URLs with null characters, which crash on Postgres (#4496)
        return None

    site = Site.find_for_request(request)
    try:
        return models.Redirect.get_for_site(site).get(old_path=path)
    except models.Redirect.MultipleObjectsReturned:
        # We have a site-specific and a site-ambivalent redirect; prefer the specific one
        return models.Redirect.objects.get(site=site, old_path=path)
    except models.Redirect.DoesNotExist:
        return None


def get_redirect(request, encoded_path):
    # Receives the ASCII percent-encoded path as obtained from request.get_full_path()
    decoded_path = uri_to_iri(encoded_path)
    redirect = _get_redirect(request, decoded_path)
    if not redirect and decoded_path != encoded_path:
        redirect = _get_redirect(request, encoded_path)
    return redirect


# Originally pinched from: https://github.com/django/django/blob/main/django/contrib/redirects/middleware.py
class RedirectMiddleware(MiddlewareMixin):
    def process_response(self, request, response):
        # No need to check for a redirect for non-404 responses.
        if response.status_code != 404:
            return response

        # Normalise the path, but without decoding unicode characters. get_redirect() will take care of that
        # if it cannot find a match for the path that was actually requested.
        path = models.Redirect.normalise_path(
            request.get_full_path(), decode_unicode=False
        )

        # Find redirect
        redirect = get_redirect(request, path)
        if redirect is None:
            # Get the path without the query string or params
            path_without_query = urlparse(path).path

            if path == path_without_query:
                # don't try again if we know we will get the same response
                return response

            redirect = get_redirect(request, path_without_query)
            if redirect is None:
                return response

        if redirect.link is None:
            return response

        if redirect.is_permanent:
            return http.HttpResponsePermanentRedirect(redirect.link)
        else:
            return http.HttpResponseRedirect(redirect.link)
