from django.contrib import messages
from django.contrib.auth import authenticate, login
from django.contrib.auth.decorators import permission_required
from django.contrib.auth.models import User
from django.contrib.auth.views import redirect_to_login
from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import Prefetch
from django.http import QueryDict
from django.shortcuts import redirect, render
from django.urls import reverse, reverse_lazy
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import require_http_methods
from django.views.generic import View
from django.views.generic.edit import CreateView, UpdateView

from .forms import GroupForm
from .models import GenericTeamToken, KFetGroup


class GenericLoginView(View):
    """
    View to authenticate as kfet generic user.

    It is a 2-step view. First, issue a token if user is a team member and send
    him to the logout view (for proper disconnect) with callback url to here.
    Then authenticate the token to log in as the kfet generic user.

    Token is stored in COOKIES to avoid share it with the authentication
    provider, which can be external. Session is unusable as it will be cleared
    on logout.
    """

    TOKEN_COOKIE_NAME = "kfettoken"

    @method_decorator(require_http_methods(["GET", "POST"]))
    def dispatch(self, request, *args, **kwargs):
        token = request.get_signed_cookie(self.TOKEN_COOKIE_NAME, None)
        if not token:
            if not request.user.has_perm("kfet.is_team"):
                return redirect_to_login(request.get_full_path())

            if request.method == "POST":
                # Step 1: set token and logout user.
                return self.prepare_auth()
            else:
                # GET request should not change server/client states. Send a
                # confirmation template to emit a POST request.
                return render(
                    request,
                    "kfet/confirm_form.html",
                    {
                        "title": _("Ouvrir une session partagée"),
                        "text": _(
                            "Êtes-vous sûr·e de vouloir ouvrir une session "
                            "partagée ?"
                        ),
                    },
                )
        else:
            # Step 2: validate token.
            return self.validate_auth(token)

    def prepare_auth(self):
        # Issue token.
        token = GenericTeamToken.objects.create_token()

        # Prepare callback of logout.
        here_url = reverse(login_generic)
        if "next" in self.request.GET:
            # Keep given next page.
            here_qd = QueryDict(mutable=True)
            here_qd["next"] = self.request.GET["next"]
            here_url += "?{}".format(here_qd.urlencode())

        logout_url = reverse("cof-logout")
        logout_qd = QueryDict(mutable=True)
        logout_qd["next"] = here_url
        logout_url += "?{}".format(logout_qd.urlencode(safe="/"))

        resp = redirect(logout_url)
        resp.set_signed_cookie(self.TOKEN_COOKIE_NAME, token.token, httponly=True)
        return resp

    def validate_auth(self, token):
        # Authenticate with GenericBackend.
        user = authenticate(request=self.request, kfet_token=token)

        if user:
            # Log in generic user.
            login(self.request, user)
            messages.success(
                self.request, _("K-Fêt — Ouverture d'une session partagée.")
            )
            resp = redirect(self.get_next_url())
        else:
            # Try again.
            resp = redirect(self.request.get_full_path())

        # Prevents blocking due to an invalid COOKIE.
        resp.delete_cookie(self.TOKEN_COOKIE_NAME)
        return resp

    def get_next_url(self):
        return self.request.GET.get("next", reverse("kfet.kpsul"))


login_generic = GenericLoginView.as_view()


@permission_required("kfet.manage_perms")
def account_group(request):
    user_pre = Prefetch(
        "user_set", queryset=User.objects.select_related("profile__account_kfet")
    )
    groups = KFetGroup.objects.prefetch_related("permissions", user_pre)
    return render(request, "kfet/account_group.html", {"groups": groups})


class AccountGroupFormMixin(SuccessMessageMixin):
    model = KFetGroup
    template_name = "kfet/account_group_form.html"
    form_class = GroupForm
    success_url = reverse_lazy("kfet.account.group")


class AccountGroupCreate(AccountGroupFormMixin, CreateView):
    success_message = "Nouveau groupe : %(name)s"


class AccountGroupUpdate(AccountGroupFormMixin, UpdateView):
    success_message = "Groupe modifié : %(name)s"
