# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later

import os
import shutil
import subprocess
from datetime import datetime, date
from tempfile import mkdtemp

from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django.db.models import Q, Count
from django.db.models.functions.text import Lower
from django.forms import HiddenInput
from django.http import HttpResponse
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.utils import timezone
from django.views import View
from django.views.generic import DetailView, UpdateView, CreateView, RedirectView, TemplateView
from django.utils.translation import gettext_lazy as _
from django.views.generic.edit import BaseFormView, DeleteView
from django_tables2 import SingleTableView
from member.models import Membership, Club
from note.models import Transaction, NoteClub, Alias, SpecialTransaction, NoteSpecial
from note.tables import HistoryTable
from note_kfet.settings import BASE_DIR
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin

from .forms.registration import WEIChooseBusForm
from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole
from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembershipForm, CurrentSurvey
from .tables import WEITable, WEIRegistrationTable, BusTable, BusTeamTable, WEIMembershipTable


class CurrentWEIDetailView(LoginRequiredMixin, RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        wei = WEIClub.objects.filter(membership_start__lte=date.today()).order_by('date_start')
        if wei.exists():
            wei = wei.last()
            return reverse_lazy('wei:wei_detail', args=(wei.pk,))
        else:
            return reverse_lazy('wei:wei_list')


class WEIListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
    """
    List existing WEI
    """
    model = WEIClub
    table_class = WEITable
    ordering = '-year'
    extra_context = {"title": _("Search WEI")}

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["can_create_wei"] = PermissionBackend.check_perm(self.request.user, "wei.add_weiclub", WEIClub(
            year=0,
            date_start=timezone.now().date(),
            date_end=timezone.now().date(),
        ))
        return context


class WEICreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
    """
    Create WEI
    """
    model = WEIClub
    form_class = WEIForm
    extra_context = {"title": _("Create WEI")}

    def form_valid(self, form):
        form.instance.requires_membership = True
        form.instance.parent_club = Club.objects.get(name="Kfet")
        ret = super().form_valid(form)
        NoteClub.objects.create(club=form.instance)
        return ret

    def get_success_url(self):
        self.object.refresh_from_db()
        return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.pk})


class WEIDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
    """
    View WEI information
    """
    model = WEIClub
    context_object_name = "club"
    extra_context = {"title": _("WEI Detail")}

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        club = context["club"]

        club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note)) \
            .filter(PermissionBackend.filter_queryset(self.request.user, Transaction, "view")) \
            .order_by('-created_at', '-id')
        history_table = HistoryTable(club_transactions, prefix="history-")
        history_table.paginate(per_page=20, page=self.request.GET.get('history-page', 1))
        context['history_list'] = history_table

        club_member = WEIMembership.objects.filter(
            club=club,
            date_end__gte=datetime.today(),
        ).filter(PermissionBackend.filter_queryset(self.request.user, WEIMembership, "view"))
        membership_table = WEIMembershipTable(data=club_member, prefix="membership-")
        membership_table.paginate(per_page=20, page=self.request.GET.get('membership-page', 1))
        context['member_list'] = membership_table

        pre_registrations = WEIRegistration.objects.filter(
            PermissionBackend.filter_queryset(self.request.user, WEIRegistration, "view")).filter(
            membership=None,
            wei=club
        )
        pre_registrations_table = WEIRegistrationTable(data=pre_registrations, prefix="pre-registration-")
        pre_registrations_table.paginate(per_page=20, page=self.request.GET.get('membership-page', 1))
        context['pre_registrations'] = pre_registrations_table

        my_registration = WEIRegistration.objects.filter(wei=club, user=self.request.user)
        if my_registration.exists():
            my_registration = my_registration.get()
        else:
            my_registration = None
        context["my_registration"] = my_registration

        buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request.user, Bus, "view")) \
            .filter(wei=self.object).annotate(count=Count("memberships")).order_by("name")
        bus_table = BusTable(data=buses, prefix="bus-")
        context['buses'] = bus_table

        random_user = User.objects.filter(~Q(wei__wei__in=[club])).first()

        if random_user is None:
            # This case occurs when all users are registered to the WEI.
            # Don't worry, Pikachu never went to the WEI.
            # This bug can arrive only in dev mode.
            context["can_add_first_year_member"] = True
            context["can_add_any_member"] = True
        else:
            # Check if the user has the right to create a registration of a random first year member.
            empty_fy_registration = WEIRegistration(
                wei=club,
                user=random_user,
                first_year=True,
                birth_date="1970-01-01",
                gender="No",
                emergency_contact_name="No",
                emergency_contact_phone="No",
            )
            context["can_add_first_year_member"] = PermissionBackend \
                .check_perm(self.request.user, "wei.add_weiregistration", empty_fy_registration)

            # Check if the user has the right to create a registration of a random old member.
            empty_old_registration = WEIRegistration(
                wei=club,
                user=User.objects.filter(~Q(wei__wei__in=[club])).first(),
                first_year=False,
                birth_date="1970-01-01",
                gender="No",
                emergency_contact_name="No",
                emergency_contact_phone="No",
            )
            context["can_add_any_member"] = PermissionBackend \
                .check_perm(self.request.user, "wei.add_weiregistration", empty_old_registration)

        empty_bus = Bus(
            wei=club,
            name="",
        )
        context["can_add_bus"] = PermissionBackend.check_perm(self.request.user, "wei.add_bus", empty_bus)

        context["not_first_year"] = WEIMembership.objects.filter(user=self.request.user).exists()

        return context


class WEIMembershipsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
    """
    List all WEI memberships
    """
    model = WEIMembership
    table_class = WEIMembershipTable
    extra_context = {"title": _("View members of the WEI")}

    def dispatch(self, request, *args, **kwargs):
        self.club = WEIClub.objects.get(pk=self.kwargs["pk"])
        return super().dispatch(request, *args, **kwargs)

    def get_queryset(self, **kwargs):
        qs = super().get_queryset(**kwargs).filter(club=self.club).distinct()

        pattern = self.request.GET.get("search", "")

        if not pattern:
            return qs.none()

        qs = qs.filter(
            Q(user__first_name__iregex=pattern)
            | Q(user__last_name__iregex=pattern)
            | Q(user__note__alias__name__iregex="^" + pattern)
            | Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
            | Q(bus__name__iregex=pattern)
            | Q(team__name__iregex=pattern)
        )

        return qs[:20]

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = self.club
        context["title"] = _("Find WEI Membership")
        return context


class WEIRegistrationsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
    """
    List all non-validated WEI registrations.
    """
    model = WEIRegistration
    table_class = WEIRegistrationTable
    extra_context = {"title": _("View registrations to the WEI")}

    def dispatch(self, request, *args, **kwargs):
        self.club = WEIClub.objects.get(pk=self.kwargs["pk"])
        return super().dispatch(request, *args, **kwargs)

    def get_queryset(self, **kwargs):
        qs = super().get_queryset(**kwargs).filter(wei=self.club, membership=None).distinct()

        pattern = self.request.GET.get("search", "")

        if not pattern:
            qs = qs.filter(
                Q(user__first_name__iregex=pattern)
                | Q(user__last_name__iregex=pattern)
                | Q(user__note__alias__name__iregex="^" + pattern)
                | Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
            )

        return qs[:20]

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = self.club
        context["title"] = _("Find WEI Registration")
        return context


class WEIUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    """
    Update the information of the WEI.
    """
    model = WEIClub
    context_object_name = "club"
    form_class = WEIForm
    extra_context = {"title": _("Update the WEI")}

    def dispatch(self, request, *args, **kwargs):
        wei = self.get_object()
        today = date.today()
        # We can't update a past WEI
        # But we can update it while it is not officially opened
        if today > wei.membership_end:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_success_url(self):
        return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.pk})


class BusCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
    """
    Create Bus
    """
    model = Bus
    form_class = BusForm
    extra_context = {"title": _("Create new bus")}

    def dispatch(self, request, *args, **kwargs):
        wei = WEIClub.objects.get(pk=self.kwargs["pk"])
        today = date.today()
        # We can't add a bus once the WEI is started
        if today >= wei.date_start:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = WEIClub.objects.get(pk=self.kwargs["pk"])
        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.fields["wei"].initial = WEIClub.objects.get(pk=self.kwargs["pk"])
        return form

    def get_success_url(self):
        self.object.refresh_from_db()
        return reverse_lazy("wei:manage_bus", kwargs={"pk": self.object.pk})


class BusUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    """
    Update Bus
    """
    model = Bus
    form_class = BusForm
    extra_context = {"title": _("Update bus")}

    def dispatch(self, request, *args, **kwargs):
        wei = self.get_object().wei
        today = date.today()
        # We can't update a bus once the WEI is started
        if today >= wei.date_start:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = self.object.wei
        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.fields["wei"].disabled = True
        return form

    def get_success_url(self):
        self.object.refresh_from_db()
        return reverse_lazy("wei:manage_bus", kwargs={"pk": self.object.pk})


class BusManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
    """
    Manage Bus
    """
    model = Bus
    extra_context = {"title": _("Manage bus")}

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = self.object.wei

        bus = self.object
        teams = BusTeam.objects.filter(PermissionBackend.filter_queryset(self.request.user, BusTeam, "view")) \
            .filter(bus=bus).annotate(count=Count("memberships")).order_by("name")
        teams_table = BusTeamTable(data=teams, prefix="team-")
        context["teams"] = teams_table

        memberships = WEIMembership.objects.filter(PermissionBackend.filter_queryset(
            self.request.user, WEIMembership, "view")).filter(bus=bus)
        memberships_table = WEIMembershipTable(data=memberships, prefix="membership-")
        memberships_table.paginate(per_page=20, page=self.request.GET.get("membership-page", 1))
        context["memberships"] = memberships_table

        return context


class BusTeamCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
    """
    Create BusTeam
    """
    model = BusTeam
    form_class = BusTeamForm
    extra_context = {"title": _("Create new team")}

    def dispatch(self, request, *args, **kwargs):
        wei = WEIClub.objects.get(buses__pk=self.kwargs["pk"])
        today = date.today()
        # We can't add a team once the WEI is started
        if today >= wei.date_start:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        bus = Bus.objects.get(pk=self.kwargs["pk"])
        context["club"] = bus.wei
        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.fields["bus"].initial = Bus.objects.get(pk=self.kwargs["pk"])
        return form

    def get_success_url(self):
        self.object.refresh_from_db()
        return reverse_lazy("wei:manage_bus", kwargs={"pk": self.object.bus.pk})


class BusTeamUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    """
    Update Bus team
    """
    model = BusTeam
    form_class = BusTeamForm
    extra_context = {"title": _("Update team")}

    def dispatch(self, request, *args, **kwargs):
        wei = self.get_object().bus.wei
        today = date.today()
        # We can't update a bus once the WEI is started
        if today >= wei.date_start:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = self.object.bus.wei
        context["bus"] = self.object.bus
        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.fields["bus"].disabled = True
        return form

    def get_success_url(self):
        self.object.refresh_from_db()
        return reverse_lazy("wei:manage_bus_team", kwargs={"pk": self.object.pk})


class BusTeamManageView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
    """
    Manage Bus team
    """
    model = BusTeam
    extra_context = {"title": _("Manage WEI team")}

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["bus"] = self.object.bus
        context["club"] = self.object.bus.wei

        memberships = WEIMembership.objects.filter(PermissionBackend.filter_queryset(
            self.request.user, WEIMembership, "view")).filter(team=self.object)
        memberships_table = WEIMembershipTable(data=memberships, prefix="membership-")
        memberships_table.paginate(per_page=20, page=self.request.GET.get("membership-page", 1))
        context["memberships"] = memberships_table

        return context


class WEIRegister1AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
    """
    Register a new user to the WEI
    """
    model = WEIRegistration
    form_class = WEIRegistrationForm
    extra_context = {"title": _("Register first year student to the WEI")}

    def dispatch(self, request, *args, **kwargs):
        wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
        today = date.today()
        # We can't register someone once the WEI is started and before the membership start date
        if today >= wei.date_start or today < wei.membership_start:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = _("Register 1A")
        context['club'] = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
        if "myself" in self.request.path:
            context["form"].fields["user"].disabled = True
        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.fields["user"].initial = self.request.user
        del form.fields["first_year"]
        del form.fields["caution_check"]
        del form.fields["information_json"]
        return form

    def form_valid(self, form):
        form.instance.wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
        form.instance.first_year = True

        if not form.instance.pk:
            # Check if the user is not already registered to the WEI
            if WEIRegistration.objects.filter(wei=form.instance.wei, user=form.instance.user).exists():
                form.add_error('user', _("This user is already registered to this WEI."))
                return self.form_invalid(form)

            # Check if the user can be in her/his first year (yeah, no cheat)
            if WEIRegistration.objects.filter(user=form.instance.user).exists():
                form.add_error('user', _("This user can't be in her/his first year since he/she has already"
                                         " participed to a WEI."))
                return self.form_invalid(form)

        return super().form_valid(form)

    def get_success_url(self):
        self.object.refresh_from_db()
        return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})


class WEIRegister2AView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
    """
    Register an old user to the WEI
    """
    model = WEIRegistration
    form_class = WEIRegistrationForm
    extra_context = {"title": _("Register old student to the WEI")}

    def dispatch(self, request, *args, **kwargs):
        wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
        today = date.today()
        # We can't register someone once the WEI is started and before the membership start date
        if today >= wei.date_start or today < wei.membership_start:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = _("Register 2A+")
        context['club'] = WEIClub.objects.get(pk=self.kwargs["wei_pk"])

        if "myself" in self.request.path:
            context["form"].fields["user"].disabled = True

        choose_bus_form = WEIChooseBusForm()
        choose_bus_form.fields["bus"].queryset = Bus.objects.filter(wei=context["club"]).order_by('name')
        choose_bus_form.fields["team"].queryset = BusTeam.objects.filter(bus__wei=context["club"])\
            .order_by('bus__name', 'name')
        context['membership_form'] = choose_bus_form

        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.fields["user"].initial = self.request.user
        if "myself" in self.request.path and self.request.user.profile.soge:
            form.fields["soge_credit"].disabled = True
            form.fields["soge_credit"].help_text = _("You already opened an account in the Société générale.")

        del form.fields["caution_check"]
        del form.fields["first_year"]
        del form.fields["ml_events_registration"]
        del form.fields["ml_art_registration"]
        del form.fields["ml_sport_registration"]
        del form.fields["information_json"]

        return form

    def form_valid(self, form):
        form.instance.wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
        form.instance.first_year = False

        if not form.instance.pk:
            # Check if the user is not already registered to the WEI
            if WEIRegistration.objects.filter(wei=form.instance.wei, user=form.instance.user).exists():
                form.add_error('user', _("This user is already registered to this WEI."))
                return self.form_invalid(form)

        choose_bus_form = WEIChooseBusForm(self.request.POST)
        if not choose_bus_form.is_valid():
            return self.form_invalid(form)

        information = form.instance.information
        information["preferred_bus_pk"] = [bus.pk for bus in choose_bus_form.cleaned_data["bus"]]
        information["preferred_bus_name"] = [bus.name for bus in choose_bus_form.cleaned_data["bus"]]
        information["preferred_team_pk"] = [team.pk for team in choose_bus_form.cleaned_data["team"]]
        information["preferred_team_name"] = [team.name for team in choose_bus_form.cleaned_data["team"]]
        information["preferred_roles_pk"] = [role.pk for role in choose_bus_form.cleaned_data["roles"]]
        information["preferred_roles_name"] = [role.name for role in choose_bus_form.cleaned_data["roles"]]
        form.instance.information = information
        form.instance.save()

        return super().form_valid(form)

    def get_success_url(self):
        self.object.refresh_from_db()
        return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})


class WEIUpdateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    """
    Update a registration for the WEI
    """
    model = WEIRegistration
    form_class = WEIRegistrationForm
    extra_context = {"title": _("Update WEI Registration")}

    def dispatch(self, request, *args, **kwargs):
        wei = self.get_object().wei
        today = date.today()
        # We can't update a registration once the WEI is started and before the membership start date
        if today >= wei.date_start or today < wei.membership_start:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = self.object.wei

        if self.object.is_validated:
            membership_form = WEIMembershipForm(instance=self.object.membership)
            for field_name, field in membership_form.fields.items():
                if not PermissionBackend.check_perm(
                        self.request.user, "wei.change_membership_" + field_name, self.object.membership):
                    field.widget = HiddenInput()
            context["membership_form"] = membership_form
        elif not self.object.first_year and PermissionBackend.check_perm(
                self.request.user, "wei.change_weiregistration_information_json", self.object):
            choose_bus_form = WEIChooseBusForm(
                dict(
                    bus=Bus.objects.filter(pk__in=self.object.information["preferred_bus_pk"]).all(),
                    team=BusTeam.objects.filter(pk__in=self.object.information["preferred_team_pk"]).all(),
                    roles=WEIRole.objects.filter(pk__in=self.object.information["preferred_roles_pk"]).all(),
                )
            )
            choose_bus_form.fields["bus"].queryset = Bus.objects.filter(wei=context["club"])
            choose_bus_form.fields["team"].queryset = BusTeam.objects.filter(bus__wei=context["club"])
            context["membership_form"] = choose_bus_form

        if not self.object.soge_credit and self.object.user.profile.soge:
            form = context["form"]
            form.fields["soge_credit"].disabled = True
            form.fields["soge_credit"].help_text = _("You already opened an account in the Société générale.")

        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.fields["user"].disabled = True
        if not self.object.first_year:
            del form.fields["information_json"]
        return form

    def form_valid(self, form):
        # If the membership is already validated, then we update the bus and the team (and the roles)
        if form.instance.is_validated:
            membership_form = WEIMembershipForm(self.request.POST, instance=form.instance.membership)
            if not membership_form.is_valid():
                return self.form_invalid(form)
            membership_form.save()
        # If it is not validated and if this is an old member, then we update the choices
        elif not form.instance.first_year and PermissionBackend.check_perm(
                self.request.user, "wei.change_weiregistration_information_json", self.object):
            choose_bus_form = WEIChooseBusForm(self.request.POST)
            if not choose_bus_form.is_valid():
                return self.form_invalid(form)
            information = form.instance.information
            information["preferred_bus_pk"] = [bus.pk for bus in choose_bus_form.cleaned_data["bus"]]
            information["preferred_bus_name"] = [bus.name for bus in choose_bus_form.cleaned_data["bus"]]
            information["preferred_team_pk"] = [team.pk for team in choose_bus_form.cleaned_data["team"]]
            information["preferred_team_name"] = [team.name for team in choose_bus_form.cleaned_data["team"]]
            information["preferred_roles_pk"] = [role.pk for role in choose_bus_form.cleaned_data["roles"]]
            information["preferred_roles_name"] = [role.name for role in choose_bus_form.cleaned_data["roles"]]
            form.instance.information = information
            form.instance.save()

        return super().form_valid(form)

    def get_success_url(self):
        self.object.refresh_from_db()
        if self.object.first_year:
            survey = CurrentSurvey(self.object)
            if not survey.is_complete():
                return reverse_lazy("wei:wei_survey", kwargs={"pk": self.object.pk})
        if PermissionBackend.check_perm(self.request.user, "wei.add_weimembership", WEIMembership(
            club=self.object.wei,
            user=self.object.user,
            date_start=timezone.now().date(),
            date_end=timezone.now().date(),
            fee=0,
            registration=self.object,
        )):
            return reverse_lazy("wei:validate_registration", kwargs={"pk": self.object.pk})
        return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.wei.pk})


class WEIDeleteRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, DeleteView):
    """
    Delete a non-validated WEI registration
    """
    model = WEIRegistration
    extra_context = {"title": _("Delete WEI registration")}

    def dispatch(self, request, *args, **kwargs):
        object = self.get_object()
        wei = object.wei
        today = date.today()
        # We can't delete a registration of a past WEI
        if today > wei.membership_end:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))

        if not PermissionBackend.check_perm(self.request.user, "wei.delete_weiregistration", object):
            raise PermissionDenied(_("You don't have the right to delete this WEI registration."))

        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = self.object.wei
        return context

    def get_success_url(self):
        return reverse_lazy('wei:wei_detail', args=(self.object.wei.pk,))


class WEIValidateRegistrationView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
    """
    Validate WEI Registration
    """
    model = WEIMembership
    form_class = WEIMembershipForm
    extra_context = {"title": _("Validate WEI registration")}

    def dispatch(self, request, *args, **kwargs):
        wei = WEIRegistration.objects.get(pk=self.kwargs["pk"]).wei
        today = date.today()
        # We can't validate anyone once the WEI is started and before the membership start date
        if today >= wei.date_start or today < wei.membership_start:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)

        registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
        context["registration"] = registration
        survey = CurrentSurvey(registration)
        if survey.information.valid:
            context["suggested_bus"] = survey.information.get_selected_bus()
        context["club"] = registration.wei

        kfet = registration.wei.parent_club
        bde = kfet.parent_club

        context["kfet_member"] = Membership.objects.filter(
            club__name=kfet.name,
            user=registration.user,
            date_start__gte=kfet.membership_start,
        ).exists()
        context["bde_member"] = Membership.objects.filter(
            club__name=bde.name,
            user=registration.user,
            date_start__gte=bde.membership_start,
        ).exists()

        fee = registration.wei.membership_fee_paid if registration.user.profile.paid \
            else registration.wei.membership_fee_unpaid
        if not context["kfet_member"]:
            fee += kfet.membership_fee_paid if registration.user.profile.paid \
                else kfet.membership_fee_unpaid
        if not context["bde_member"]:
            fee += bde.membership_fee_paid if registration.user.profile.paid \
                else bde.membership_fee_unpaid

        context["fee"] = fee

        form = context["form"]
        if registration.soge_credit:
            form.fields["credit_amount"].initial = fee
        else:
            form.fields["credit_amount"].initial = max(0, fee - registration.user.note.balance)

        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
        form.fields["last_name"].initial = registration.user.last_name
        form.fields["first_name"].initial = registration.user.first_name

        if registration.soge_credit:
            form.fields["credit_type"].disabled = True
            form.fields["credit_type"].initial = NoteSpecial.objects.get(special_type="Virement bancaire")
            form.fields["credit_amount"].disabled = True
            form.fields["last_name"].disabled = True
            form.fields["first_name"].disabled = True
            form.fields["bank"].disabled = True
            form.fields["bank"].initial = "Société générale"

        form.fields["bus"].widget.attrs["api_url"] = "/api/wei/bus/?wei=" + str(registration.wei.pk)
        if registration.first_year:
            # Use the results of the survey to fill initial data
            # A first year has no other role than "1A"
            del form.fields["roles"]
            survey = CurrentSurvey(registration)
            if survey.information.valid:
                form.fields["bus"].initial = survey.information.get_selected_bus()
        else:
            # Use the choice of the member to fill initial data
            information = registration.information
            if "preferred_bus_pk" in information and len(information["preferred_bus_pk"]) == 1:
                form["bus"].initial = Bus.objects.get(pk=information["preferred_bus_pk"][0])
            if "preferred_team_pk" in information and len(information["preferred_team_pk"]) == 1:
                form["team"].initial = BusTeam.objects.get(pk=information["preferred_team_pk"][0])
            if "preferred_roles_pk" in information:
                form["roles"].initial = WEIRole.objects.filter(
                    Q(pk__in=information["preferred_roles_pk"]) | Q(name="Adhérent WEI")
                ).all()
        return form

    def form_valid(self, form):
        """
        Create membership, check that all is good, make transactions
        """
        registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
        club = registration.wei
        user = registration.user

        membership = form.instance
        membership.user = user
        membership.club = club
        membership.date_start = min(date.today(), club.date_start)
        membership.registration = registration
        # Force the membership of the clubs BDE and Kfet
        membership._force_renew_parent = True

        if user.profile.paid:
            fee = club.membership_fee_paid
        else:
            fee = club.membership_fee_unpaid

        kfet = club.parent_club
        bde = kfet.parent_club

        kfet_member = Membership.objects.filter(
            club__name=kfet.name,
            user=registration.user,
            date_start__gte=kfet.membership_start,
        ).exists()
        bde_member = Membership.objects.filter(
            club__name=bde.name,
            user=registration.user,
            date_start__gte=bde.membership_start,
        ).exists()

        if not kfet_member:
            fee += kfet.membership_fee_paid if registration.user.profile.paid else kfet.membership_fee_unpaid
        if not bde_member:
            fee += bde.membership_fee_paid if registration.user.profile.paid else bde.membership_fee_unpaid

        credit_type = form.cleaned_data["credit_type"]
        credit_amount = form.cleaned_data["credit_amount"]
        last_name = form.cleaned_data["last_name"]
        first_name = form.cleaned_data["first_name"]
        bank = form.cleaned_data["bank"]

        if credit_type is None or registration.soge_credit:
            credit_amount = 0

        if not registration.caution_check and not registration.first_year:
            form.add_error('bus', _("This user didn't give her/his caution check."))
            return super().form_invalid(form)

        if not registration.soge_credit and user.note.balance < fee + credit_amount:
            # Users must have money before registering to the WEI.
            form.add_error('bus',
                           _("This user don't have enough money to join this club, and can't have a negative balance."))
            return super().form_invalid(form)

        if credit_amount:
            if not last_name:
                form.add_error('last_name', _("This field is required."))
                return super().form_invalid(form)

            if not first_name:
                form.add_error('first_name', _("This field is required."))
                return super().form_invalid(form)

            # Credit note before adding the membership
            SpecialTransaction.objects.create(
                source=credit_type,
                destination=registration.user.note,
                amount=credit_amount,
                reason="Crédit " + str(credit_type) + " (WEI)",
                last_name=last_name,
                first_name=first_name,
                bank=bank,
            )

        # Now, all is fine, the membership can be created.

        if registration.soge_credit:
            form.instance._soge = True

        if registration.first_year:
            membership = form.instance
            # If the user is not a member of the club Kfet, then the membership is created.
            membership.save()
            membership.refresh_from_db()
            membership.roles.set(WEIRole.objects.filter(name="1A").all())
            membership.save()

        ret = super().form_valid(form)

        membership.refresh_from_db()
        membership.roles.add(WEIRole.objects.get(name="Adhérent WEI"))

        return ret

    def get_success_url(self):
        self.object.refresh_from_db()
        return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.club.pk})


class WEISurveyView(LoginRequiredMixin, BaseFormView, DetailView):
    """
    Display the survey for the WEI for first year members.
    Warning: this page is accessible for anyone that is connected, the view doesn't extend ProtectQuerySetMixin.
    """
    model = WEIRegistration
    template_name = "wei/survey.html"
    survey = None
    extra_context = {"title": _("Survey WEI")}

    def dispatch(self, request, *args, **kwargs):
        obj = self.get_object()
        self.object = obj

        wei = obj.wei
        today = date.today()
        # We can't access to the WEI survey once the WEI is started and before the membership start date
        if today >= wei.date_start or today < wei.membership_start:
            return redirect(reverse_lazy('wei:wei_closed', args=(wei.pk,)))

        if not self.survey:
            self.survey = CurrentSurvey(obj)
        # If the survey is complete, then display the end page.
        if self.survey.is_complete():
            return redirect(reverse_lazy('wei:wei_survey_end', args=(self.survey.registration.pk,)))
        # Non first year members don't have a survey
        if not obj.first_year:
            return redirect(reverse_lazy('wei:wei_survey_end', args=(self.survey.registration.pk,)))
        return super().dispatch(request, *args, **kwargs)

    def get_form_class(self):
        """
        Get the survey form. It may depend on the current state of the survey.
        """
        return self.survey.get_form_class()

    def get_form(self, form_class=None):
        """
        Update the form with the data of the survey.
        """
        form = super().get_form(form_class)
        self.survey.update_form(form)
        return form

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = self.object.wei
        return context

    def form_valid(self, form):
        """
        Update the survey with the data of the form.
        """
        self.survey.form_valid(form)
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy('wei:wei_survey', args=(self.get_object().pk,))


class WEISurveyEndView(LoginRequiredMixin, TemplateView):
    template_name = "wei/survey_end.html"
    extra_context = {"title": _("Survey WEI")}

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = WEIRegistration.objects.get(pk=self.kwargs["pk"]).wei
        return context


class WEIClosedView(LoginRequiredMixin, TemplateView):
    template_name = "wei/survey_closed.html"
    extra_context = {"title": _("Survey WEI")}

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["club"] = WEIClub.objects.get(pk=self.kwargs["pk"])
        return context


class MemberListRenderView(LoginRequiredMixin, View):
    """
    Render Invoice as a generated PDF with the given information and a LaTeX template
    """

    def get_queryset(self, **kwargs):
        qs = WEIMembership.objects.filter(PermissionBackend.filter_queryset(self.request.user, WEIMembership, "view"))
        qs = qs.filter(club__pk=self.kwargs["wei_pk"]).order_by(
            Lower('bus__name'),
            Lower('team__name'),
            'user__profile__promotion',
            Lower('user__last_name'),
            Lower('user__first_name'),
            'id',
        )

        if "bus_pk" in self.kwargs:
            qs = qs.filter(bus__pk=self.kwargs["bus_pk"])

        if "team_pk" in self.kwargs:
            qs = qs.filter(team__pk=self.kwargs["team_pk"] if self.kwargs["team_pk"] else None)

        return qs.distinct()

    def get(self, request, **kwargs):
        qs = self.get_queryset()

        wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
        bus = team = None
        if "bus_pk" in self.kwargs:
            bus = Bus.objects.get(pk=self.kwargs["bus_pk"])
        if "team_pk" in self.kwargs:
            team = BusTeam.objects.filter(pk=self.kwargs["team_pk"] if self.kwargs["team_pk"] else None)
            if team.exists():
                team = team.get()
                bus = team.bus
            else:
                team = dict(name="Staff")

        # Fill the template with the information
        tex = render_to_string("wei/weilist_sample.tex", dict(memberships=qs.all(), wei=wei, bus=bus, team=team))

        try:
            os.mkdir(BASE_DIR + "/tmp")
        except FileExistsError:
            pass
        # We render the file in a temporary directory
        tmp_dir = mkdtemp(prefix=BASE_DIR + "/tmp/")

        try:
            with open("{}/wei-list.tex".format(tmp_dir), "wb") as f:
                f.write(tex.encode("UTF-8"))
            del tex

            error = subprocess.Popen(
                ["pdflatex", "{}/wei-list.tex".format(tmp_dir)],
                cwd=tmp_dir,
                stdin=open(os.devnull, "r"),
                stderr=open(os.devnull, "wb"),
                stdout=open(os.devnull, "wb"),
            ).wait()

            if error:
                raise IOError("An error attempted while generating a WEI list (code=" + str(error) + ")")

            # Display the generated pdf as a HTTP Response
            pdf = open("{}/wei-list.pdf".format(tmp_dir), 'rb').read()
            response = HttpResponse(pdf, content_type="application/pdf")
            response['Content-Disposition'] = "inline;filename=Liste%20des%20participants%20au%20WEI.pdf"
        except IOError as e:
            raise e
        finally:
            # Delete all temporary files
            shutil.rmtree(tmp_dir)

        return response