from django.contrib.auth.mixins import PermissionRequiredMixin
from django.db.models import Q
from django.urls import reverse
from django.utils import timezone
from django.views.generic.detail import SingleObjectMixin

from .models import Election, Option, Question


class AdminOnlyMixin(PermissionRequiredMixin):
    """Restreint l'accès aux admins"""

    permission_required = "elections.is_admin"


class SelectElectionMixin:
    """Sélectionne automatiquement les foreignkeys voulues"""

    def get_queryset(self):
        qs = super().get_queryset()
        if self.model is Question:
            return qs.select_related("election")
        elif self.model is Option:
            return qs.select_related("question__election")
        return qs


class RestrictAccessMixin(SelectElectionMixin):
    """Permet de restreindre l'accès à des élections/questions/options"""

    f_prefixes = {Election: "", Question: "election__", Option: "question__election__"}

    def get_f_prefix(self):
        return self.f_prefixes.get(self.model, None)

    def get_filters(self):
        return {}

    def get_queryset(self):
        qs = super().get_queryset()
        if self.model in self.f_prefixes:
            return qs.filter(**self.get_filters())
        # On ne sait pas ce qu'on manipule donc on ne renvoie rien
        return qs.none()


class OpenElectionOnlyMixin(RestrictAccessMixin):
    """N'autorise la vue que lorsque l'élection est ouverte"""

    def get_filters(self):
        f_prefix = self.get_f_prefix()
        # On ne peut modifier que les élections qui n'ont pas commencé, et
        # accessoirement qui ne sont pas dépouillées ou archivées
        # TODO: décider si on choisit pas de juste garder les dates d'ouverture
        filters = super().get_filters()
        filters[f_prefix + "start_date__lt"] = timezone.now()
        filters[f_prefix + "end_date__gt"] = timezone.now()
        filters[f_prefix + "tallied"] = False
        filters[f_prefix + "archived"] = False
        return filters


class CreatorOnlyMixin(AdminOnlyMixin, RestrictAccessMixin):
    """Restreint l'accès au créateurice de l'élection"""

    def get_next_url(self):
        return reverse("kadenios")

    def get_filters(self):
        filters = super().get_filters()
        # TODO: change the way we collect the user according to the model used
        filters[self.get_f_prefix() + "created_by"] = self.request.user
        return filters


class CreatorOnlyEditMixin(CreatorOnlyMixin, SingleObjectMixin):
    """Permet au créateurice de modifier l'élection implicitement"""

    def get_filters(self):
        # On ne peut modifier que les élections qui n'ont pas commencé
        filters = super().get_filters()
        filters[self.get_f_prefix() + "start_date__gt"] = timezone.now()
        return filters


class ClosedElectionMixin(CreatorOnlyMixin, SingleObjectMixin):
    """Permet d'agir sur une élection terminée"""

    def get_filters(self):
        f_prefix = self.get_f_prefix()
        # L'élection doit être terminée et non archivée
        filters = super().get_filters()
        filters[f_prefix + "end_date__lt"] = timezone.now()
        filters[f_prefix + "archived"] = False
        return filters


class NotArchivedMixin:
    """Permet de ne garder que les élections non archivées ou dont on est l'admin"""

    def get_queryset(self):
        user = self.request.user
        qs = super().get_queryset()
        if user.is_authenticated:
            return qs.filter(Q(archived=False) | Q(created_by=user))

        return qs.filter(archived=False)