from urllib.parse import urlencode

from django.conf import settings
from django.shortcuts import redirect
from django.urls import NoReverseMatch, reverse

from .services import SESSION_2FA_VERIFIED, confirmed_device, two_factor_required


class UnifiedLoginAndTwoFactorMiddleware:
    """Centraliza a entrada e protege sessões que tenham 2FA configurado."""

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        path = request.path
        static_prefixes = (settings.STATIC_URL, settings.MEDIA_URL)
        if any(path.startswith(prefix) for prefix in static_prefixes if prefix):
            return self.get_response(request)

        try:
            login_url = reverse("security:login")
            verify_url = reverse("security:verify")
            setup_url = reverse("security:setup")
            logout_url = reverse("security:logout")
        except NoReverseMatch:
            return self.get_response(request)

        legacy_login_paths = {"/admin/login/"}
        if request.method == "GET" and not request.user.is_authenticated and path in legacy_login_paths:
            query = urlencode({"next": request.GET.get("next") or ("/admin/" if path.startswith("/admin") else "/portal/")})
            return redirect(f"{login_url}?{query}")

        exempt = {
            login_url,
            verify_url,
            setup_url,
            logout_url,
            reverse("security:recovery_codes"),
            reverse("security:password_reset"),
            reverse("security:password_reset_done"),
            reverse("security:password_reset_complete"),
            reverse("health"),
            reverse("health_run"),
            "/admin/logout/",
            "/portal/logout/",
        }
        if path in exempt or path.startswith("/recuperar-palavra-passe/") or path.startswith("/security/static/"):
            return self.get_response(request)

        if request.user.is_authenticated:
            device = confirmed_device(request.user)
            if device and not request.session.get(SESSION_2FA_VERIFIED):
                return redirect(f"{verify_url}?{urlencode({'next': request.get_full_path()})}")
            if two_factor_required(request.user) and not device:
                return redirect(setup_url)

        return self.get_response(request)
