from django.contrib import messages
from django.contrib.messages.views import SuccessMessageMixin
from django.db.models import Count, Prefetch
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.utils import timezone
from django.utils.text import slugify
from django.utils.translation import gettext_lazy as _
from django.views.generic import (
    CreateView,
    DetailView,
    ListView,
    RedirectView,
    UpdateView,
)
from django.views.generic.detail import SingleObjectMixin

from .forms import ElectionCreateForm, OptionFormSet
from .mixins import CreatorOnlyMixin
from .models import Election, Option, Question

# TODO: access control *everywhere*

# #############################################################################
#  Administration Views
# #############################################################################


class ElectionCreateView(SuccessMessageMixin, CreateView):
    model = Election
    form_class = ElectionCreateForm
    template_name = "elections/election_create.html"
    success_message = _("Élection créée avec succès !")

    def get_success_url(self):
        return reverse("election.admin", args=[self.object.pk])

    def form_valid(self, form):
        # We need to add the short name and the creator od the election
        form.instance.short_name = slugify(
            form.instance.start_date.strftime("%Y-%m-%d") + "_" + form.instance.name
        )[:50]
        # TODO: Change this if we modify the user model
        form.instance.created_by = self.request.user
        return super().form_valid(form)


# TODO : only the creator can edit the election and view the admin panel
class ElectionAdminView(CreatorOnlyMixin, DetailView):
    model = Election
    template_name = "elections/election_admin.html"

    def get_context_data(self, **kwargs):
        kwargs.update({"current_time": timezone.now()})
        return super().get_context_data(**kwargs)

    def get_queryset(self):
        return super().get_queryset().prefetch_related("questions__options")


class ElectionListView(CreatorOnlyMixin, ListView):
    model = Election
    template_name = "elections/election_list.html"


class ElectionUpdateView(SuccessMessageMixin, UpdateView):
    model = Election
    fields = ["name", "description", "start_date", "end_date"]
    success_message = _("Élection modifiée avec succès !")
    template_name = "elections/election_update.html"

    def get_success_url(self):
        return reverse("election.admin", args=[self.object.pk])

    def get_queryset(self):
        # On ne peut plus modifier une élection qui a déjà commencé
        return super().get_queryset().filter(start_date__gt=timezone.now())


class ElectionTallyView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
    model = Election
    pattern_name = "election.admin"

    def get_queryset(self):
        return (
            super()
            .get_queryset()
            .filter(end_date__lt=timezone.now())
            .prefetch_related("questions__options")
        )

    def get(self, request, *args, **kwargs):
        election = self.get_object()
        options, questions = [], []
        for q in election.questions.all():
            max_votes = 0
            for o in q.options.all():
                o.nb_votes = o.voters.count()
                max_votes = max(max_votes, o.nb_votes)
                options.append(o)

            q.max_votes = max_votes
            questions.append(q)

        Option.objects.bulk_update(options, ["nb_votes"])
        Question.objects.bulk_update(questions, ["max_votes"])
        election.tallied = True
        election.save()
        messages.success(request, _("Élection dépouillée avec succès !"))
        return super().get(request, *args, **kwargs)


class ElectionChangePublicationView(
    SuccessMessageMixin, SingleObjectMixin, RedirectView
):
    model = Election
    pattern_name = "election.admin"

    def get_queryset(self):
        return super().get_queryset().filter(end_date__lt=timezone.now())

    def get(self, request, *args, **kwargs):
        election = self.get_object()
        election.results_public = not election.results_public
        election.save()
        messages.success(
            request,
            _("Élection publiée avec succès !")
            if election.results_public
            else _("Élection dépubliée avec succès !"),
        )
        return super().get(request, *args, **kwargs)


class ElectionArchiveView(SuccessMessageMixin, SingleObjectMixin, RedirectView):
    model = Election
    pattern_name = "election.admin"

    def get_queryset(self):
        return super().get_queryset().filter(end_date__lt=timezone.now())

    def get(self, request, *args, **kwargs):
        election = self.get_object()
        election.archived = True
        election.save()
        messages.success(request, _("Élection archivée avec succès !"))
        return super().get(request, *args, **kwargs)


class ElectionView(DetailView):
    model = Election
    template_name = "elections/election.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.object.tallied:
            options_qs = Option.objects.annotate(nb_votes=Count("voters"))
            questions = self.election.question.prefetch_related(
                Prefetch("options", queryset=options_qs)
            )
            context["questions"] = questions

        return context

    def get_queryset(self):
        return super().get_queryset().filter(archived=False)


class VoteView(SuccessMessageMixin, DetailView):
    model = Question
    template_name = "elections/vote.html"

    def get_queryset(self):
        # On ne peut voter que si l'élection n'a pas été comptée
        return (
            super()
            .get_queryset()
            .filter(
                election__tallied=False,
                election__archived=False,
                election__start_date__lt=timezone.now(),
                election__end_date__gt=timezone.now(),
            )
            .select_related("election")
        )

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        vote_form = OptionFormSet(instance=self.object)

        return self.render_to_response(self.get_context_data(formset=vote_form))

    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        vote_form = OptionFormSet(self.request.POST, instance=self.object)

        # We record the cast votes
        for v in vote_form:
            v.record_vote(self.request.user)

        messages.success(self.request, _("Votre vote a bien été enregistré !"))

        return HttpResponseRedirect(
            reverse("election.view", args=[self.object.election.pk])
            + f"#q_{self.object.pk}"
        )
