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

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

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django.db import transaction
from django.db.models import Q, Count, Case, When, Value, IntegerField, F
from django.db.models.functions.text import Lower
from django import forms
from django.http import HttpResponse, Http404
from django.shortcuts import redirect
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.views import View
from django.views.generic import DetailView, UpdateView, RedirectView, TemplateView
from django.utils.translation import gettext_lazy as _
from django.views.generic.edit import BaseFormView, DeleteView
from django_tables2 import SingleTableView, MultiTableMixin
from api.viewsets import is_regex
from member.models import Membership, Club
from note.models import Transaction, NoteClub, Alias, SpecialTransaction
from note.tables import HistoryTable
from note_kfet.settings import BASE_DIR
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin, ProtectedCreateView

from .forms.registration import WEIChooseBusForm
from .models import WEIClub, WEIRegistration, WEIMembership, Bus, BusTeam, WEIRole
from .forms import WEIForm, WEIRegistrationForm, BusForm, BusTeamForm, WEIMembership1AForm, \
    WEIMembershipForm, CurrentSurvey
from .tables import BusRepartitionTable, BusTable, BusTeamTable, WEITable, WEIRegistrationTable, \
    WEIRegistration1ATable, 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, "wei.add_weiclub", WEIClub(
            name="",
            email="weiclub@example.com",
            year=0,
            date_start=date.today(),
            date_end=date.today(),
        ))
        return context


class WEICreateView(ProtectQuerysetMixin, ProtectedCreateView):
    """
    Create WEI
    """

    model = WEIClub
    form_class = WEIForm
    extra_context = {"title": _("Create WEI")}

    def get_sample_object(self):
        return WEIClub(
            name="",
            email="weiclub@example.com",
            year=0,
            date_start=date.today(),
            date_end=date.today(),
        )

    @transaction.atomic
    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, MultiTableMixin, DetailView):
    """
    View WEI information
    """
    model = WEIClub
    context_object_name = "club"
    extra_context = {"title": _("WEI Detail")}

    tables = [
        lambda data: HistoryTable(data, prefix="history-"),
        lambda data: WEIMembershipTable(data, prefix="membership-"),
        lambda data: WEIRegistrationTable(data, prefix="pre-registration-"),
        lambda data: BusTable(data, prefix="bus-"),
    ]
    paginate_by = 20   # number of rows in tables

    def get_tables_data(self):
        club = self.object
        club_transactions = Transaction.objects.all().filter(Q(source=club.note) | Q(destination=club.note)) \
            .filter(PermissionBackend.filter_queryset(self.request, Transaction, "view")) \
            .order_by('-created_at', '-id')
        club_member = WEIMembership.objects.filter(
            club=club,
            date_end__gte=date.today(),
        ).filter(PermissionBackend.filter_queryset(self.request, WEIMembership, "view"))
        pre_registrations = WEIRegistration.objects.filter(
            PermissionBackend.filter_queryset(self.request, WEIRegistration, "view")).filter(
            membership=None,
            wei=club
        )
        # Annotate the query to be able to sort registrations on validate status
        pre_registrations = pre_registrations.annotate(
            deposit=Case(
                When(deposit_type='note', then=F('wei__deposit_amount')),
                default=Value(0),
                output_field=IntegerField()
            )
        ).annotate(
            total_fee=F('fee') + F('deposit')
        ).annotate(
            validate_status=Case(
                When(total_fee__gt=F('user__note__balance'), then=Value(2)),
                When(first_year=True, then=Value(1)),
                default=Value(0),
                output_field=IntegerField(),
            )
        )
        buses = Bus.objects.filter(PermissionBackend.filter_queryset(self.request, Bus, "view")) \
            .filter(wei=self.object).annotate(count=Count("memberships")).order_by("name")
        return [club_transactions, club_member, pre_registrations, buses, ]

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

        club = context["club"]

        tables = context["tables"]
        for name, table in zip(["history_list", "member_list", "pre_registrations", "buses"], tables):
            context[name] = table

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

        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, "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, "wei.add_weiregistration", empty_old_registration)

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

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

        context["registration_validated"] = WEIMembership.objects.filter(registration=my_registration).exists() if my_registration else False

        qs = WEIMembership.objects.filter(club=club, registration__first_year=True, bus__isnull=True)
        context["can_validate_1a"] = PermissionBackend.check_perm(
            self.request, "wei.change_weimembership_bus", qs.first()) if qs.exists() else False

        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()

        # Check if this is a valid regex. If not, we won't check regex
        valid_regex = is_regex(pattern)
        suffix_alias = "__iregex" if valid_regex else "__istartswith"
        suffix = "__iregex" if valid_regex else "__icontains"
        prefix = "^" if valid_regex else ""
        qs = qs.filter(
            Q(**{f"user__first_name{suffix}": pattern})
            | Q(**{f"user__last_name{suffix}": pattern})
            | Q(**{f"user__note__alias__name{suffix_alias}": prefix + pattern})
            | Q(**{f"user__note__alias__normalized_name{suffix_alias}": prefix + Alias.normalize(pattern)})
            | Q(**{f"bus__name{suffix}": pattern})
            | Q(**{f"team__name{suffix}": pattern})
        )

        return qs

    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()
        # Annotate the query to be able to sort registrations on validate status
        qs = qs.annotate(
            deposit=Case(
                When(deposit_type='note', then=F('wei__deposit_amount')),
                default=Value(0),
                output_field=IntegerField()
            )
        ).annotate(
            total_fee=F('fee') + F('deposit')
        ).annotate(
            validate_status=Case(
                When(total_fee__gt=F('user__note__balance'), then=Value(2)),
                When(first_year=True, then=Value(1)),
                default=Value(0),
                output_field=IntegerField(),
            )
        )

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

        if pattern:
            # Check if this is a valid regex. If not, we won't check regex
            valid_regex = is_regex(pattern)
            suffix_alias = "__iregex" if valid_regex else "__istartswith"
            suffix = "__iregex" if valid_regex else "__icontains"
            prefix = "^" if valid_regex else ""
            qs = qs.filter(
                Q(**{f"user__first_name{suffix}": pattern})
                | Q(**{f"user__last_name{suffix}": pattern})
                | Q(**{f"user__note__alias__name{suffix_alias}": prefix + pattern})
                | Q(**{f"user__note__alias__normalized_name{suffix_alias}": prefix + Alias.normalize(pattern)})
            )

        return qs

    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.date_start:
            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, ProtectedCreateView):
    """
    Create Bus
    """
    model = Bus
    form_class = BusForm
    extra_context = {"title": _("Create new bus")}

    def get_sample_object(self):
        wei = WEIClub.objects.get(pk=self.kwargs["pk"])
        return Bus(
            wei=wei,
            name="",
        )

    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
        context["information"] = CurrentSurvey.get_algorithm_class().get_bus_information(self.object)
        self.object.save()
        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, 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, 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, ProtectedCreateView):
    """
    Create BusTeam
    """
    model = BusTeam
    form_class = BusTeamForm
    extra_context = {"title": _("Create new team")}

    def get_sample_object(self):
        bus = Bus.objects.get(pk=self.kwargs["pk"])
        return BusTeam(
            name="",
            bus=bus,
            color=0,
        )

    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_team", kwargs={"pk": self.object.pk})

    def get_template_names(self):
        names = super().get_template_names()
        return names


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})

    def get_template_names(self):
        names = super().get_template_names()
        return names


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, 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, ProtectedCreateView):
    """
    Register a new user to the WEI
    """
    model = WEIRegistration
    form_class = WEIRegistrationForm
    extra_context = {"title": _("Register first year student to the WEI")}

    def get_sample_object(self):
        wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
        if "myself" in self.request.path:
            user = self.request.user
        else:
            # To avoid unique validation issues, we use an account that can't join the WEI.
            # In development mode, the note account may not exist, we use a random user (may fail)
            user = User.objects.get(username="note") \
                if User.objects.filter(username="note").exists() else User.objects.first()
        return WEIRegistration(
            wei=wei,
            user=user,
            first_year=True,
            birth_date="1970-01-01",
            gender="No",
            emergency_contact_name="No",
            emergency_contact_phone="No",
        )

    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,)))
        # Don't register twice
        if 'myself' in self.request.path and not self.request.user.is_anonymous \
                and WEIRegistration.objects.filter(wei=wei, user=self.request.user).exists():
            obj = WEIRegistration.objects.get(wei=wei, user=self.request.user)
            return redirect(reverse_lazy('wei:wei_update_registration', args=(obj.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

        # Cacher les champs pendant l'inscription initiale
        if "first_year" in form.fields:
            del form.fields["first_year"]
        if "deposit_given" in form.fields:
            del form.fields["deposit_given"]
        if "information_json" in form.fields:
            del form.fields["information_json"]
        if "deposit_type" in form.fields:
            del form.fields["deposit_type"]

        if "soge_credit" in form.fields:
            form.fields["soge_credit"].help_text = _('Check if you will open a Société Générale account')
        return form

    @transaction.atomic
    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"
                                         " participated to a WEI."))
                return self.form_invalid(form)

        if 'treasury' in settings.INSTALLED_APPS:
            from treasury.models import SogeCredit
            form.instance.soge_credit = \
                form.instance.soge_credit \
                or SogeCredit.objects.filter(user=form.instance.user, credit_transaction__valid=False).exists()

        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, ProtectedCreateView):
    """
    Register an old user to the WEI
    """
    model = WEIRegistration
    form_class = WEIRegistrationForm
    extra_context = {"title": _("Register old student to the WEI")}

    def get_sample_object(self):
        wei = WEIClub.objects.get(pk=self.kwargs["wei_pk"])
        if "myself" in self.request.path:
            user = self.request.user
        else:
            # To avoid unique validation issues, we use an account that can't join the WEI.
            # In development mode, the note account may not exist, we use a random user (may fail)
            user = User.objects.get(username="note") \
                if User.objects.filter(username="note").exists() else User.objects.first()
        return WEIRegistration(
            wei=wei,
            user=user,
            first_year=True,
            birth_date="1970-01-01",
            gender="No",
            emergency_contact_name="No",
            emergency_contact_phone="No",
        )

    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,)))
        # Don't register twice
        if 'myself' in self.request.path and not self.request.user.is_anonymous \
                and WEIRegistration.objects.filter(wei=wei, user=self.request.user).exists():
            obj = WEIRegistration.objects.get(wei=wei, user=self.request.user)
            return redirect(reverse_lazy('wei:wei_update_registration', args=(obj.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(self.request.POST if self.request.POST else None)
        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 "soge_credit" in form.fields:
            form.fields["soge_credit"].help_text = _('Check if you will open a Société Générale account')

        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.")

        # Cacher les champs pendant l'inscription initiale
        if "first_year" in form.fields:
            del form.fields["first_year"]
        if "deposit_given" in form.fields:
            del form.fields["deposit_given"]
        if "information_json" in form.fields:
            del form.fields["information_json"]

        # S'assurer que le champ deposit_type est obligatoire
        if "deposit_type" in form.fields:
            form.fields["deposit_type"].required = True
            form.fields["deposit_type"].help_text = _("Choose how you want to pay the deposit")
            form.fields["deposit_type"].widget = forms.RadioSelect(choices=form.fields["deposit_type"].choices)

        return form

    @transaction.atomic
    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

        # Sauvegarder le type de caution
        form.instance.deposit_type = form.cleaned_data["deposit_type"]
        form.instance.save()

        if 'treasury' in settings.INSTALLED_APPS:
            from treasury.models import SogeCredit
            form.instance.soge_credit = \
                form.instance.soge_credit \
                or SogeCredit.objects.filter(user=form.instance.user, credit_transaction__valid=False).exists()

        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,)))
        # Store the validate parameter in the view's state
        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 = self.get_membership_form(instance=self.object.membership,
                                                       data=self.request.POST)
            context["membership_form"] = membership_form
        elif not self.object.first_year and PermissionBackend.check_perm(
                self.request, "wei.change_weiregistration_information_json", self.object):
            information = self.object.information
            d = dict(
                bus=Bus.objects.filter(pk__in=information["preferred_bus_pk"]).all(),
                team=BusTeam.objects.filter(pk__in=information["preferred_team_pk"]).all(),
                roles=WEIRole.objects.filter(pk__in=information["preferred_roles_pk"]).all(),
            ) if 'preferred_bus_pk' in information else dict()
            choose_bus_form = WEIChooseBusForm(
                self.request.POST if self.request.POST else d
            )
            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

        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        form.fields["user"].disabled = True
        # The auto-json-format may cause issues with the default field remove
        if "information_json" in form.fields:
            del form.fields["information_json"]
        # Masquer le champ deposit_given pour tout le monde dans le formulaire de modification
        if "deposit_given" in form.fields:
            form.fields["deposit_given"].help_text = _("Tick if the deposit check has been given")
            if self.object.first_year or self.object.deposit_type == 'note':
                del form.fields["deposit_given"]

        # S'assurer que le champ deposit_type est obligatoire pour les 2A+
        if "deposit_type" in form.fields:
            if self.object.first_year:
                del form.fields["deposit_type"]
            else:
                form.fields["deposit_type"].required = True
                form.fields["deposit_type"].help_text = _("Choose how you want to pay the deposit")

        if self.object.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.")

        return form

    def get_membership_form(self, data=None, instance=None):
        registration = self.get_object()
        membership_form = WEIMembershipForm(data if data else None, instance=instance, wei=registration.wei)
        del membership_form.fields["credit_type"]
        del membership_form.fields["credit_amount"]
        del membership_form.fields["first_name"]
        del membership_form.fields["last_name"]
        del membership_form.fields["bank"]
        for field_name, _field in list(membership_form.fields.items()):
            if not PermissionBackend.check_perm(
                    self.request, "wei.change_weimembership_" + field_name, self.object.membership):
                del membership_form.fields[field_name]
        return membership_form

    @transaction.atomic
    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:
            try:
                membership = form.instance.membership
                if membership is None:
                    raise ValueError(_("No membership found for this registration"))

                membership_form = self.get_membership_form(self.request.POST, instance=membership)
                if not membership_form.is_valid():
                    return self.form_invalid(form)

                # Vérifier que l'utilisateur a la permission de modifier le membership
                # On vérifie d'abord si l'utilisateur a la permission générale de modification
                if not self.request.user.has_perm("wei.change_weimembership"):
                    raise PermissionDenied(_("You don't have the permission to update memberships"))

                # On vérifie ensuite les permissions spécifiques pour chaque champ modifié
                for field_name in membership_form.changed_data:
                    perm = f"wei.change_weimembership_{field_name}"
                    if not self.request.user.has_perm(perm):
                        raise PermissionDenied(_("You don't have the permission to update the field %(field)s") % {'field': field_name})

                membership_form.save()
            except (WEIMembership.DoesNotExist, ValueError, PermissionDenied) as e:
                form.add_error(None, str(e))
                return self.form_invalid(form)
        # 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, "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

            if "deposit_type" in form.cleaned_data:
                form.instance.deposit_type = form.cleaned_data["deposit_type"]
            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})
        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, "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, ProtectedCreateView):
    """
    Validate WEI Registration
    """
    model = WEIMembership
    extra_context = {"title": _("Validate WEI registration")}

    def get_sample_object(self):
        """
        Return a sample object for permission checking
        """
        registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
        return WEIMembership(
            user=registration.user,
            club=registration.wei,
            date_start=registration.wei.date_start,
            fee=registration.wei.membership_fee_paid if registration.user.profile.paid else registration.wei.membership_fee_unpaid,
            # Add any fields needed for proper permission checking
            registration=registration,
        )

    def dispatch(self, request, *args, **kwargs):
        registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])

        wei = registration.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.fee
        context["fee"] = fee

        # Calculer le montant total nécessaire (frais + caution si transaction)
        total_needed = fee
        if registration.deposit_type == 'note':
            total_needed += registration.wei.deposit_amount
        context["total_needed"] = total_needed

        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_class(self):
        registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
        if registration.first_year and 'selected_bus_pk' not in registration.information:
            return WEIMembership1AForm
        return WEIMembershipForm

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        registration = WEIRegistration.objects.get(pk=self.kwargs["pk"])
        wei = registration.wei
        kwargs['wei'] = wei
        return kwargs

    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

        # Ajouter le champ deposit_given uniquement pour les non-première année et le rendre obligatoire
        if not registration.first_year:
            if registration.deposit_type == 'check':
                form.fields["deposit_given"] = forms.BooleanField(
                    required=True,
                    disabled=True,
                    initial=registration.deposit_given,
                    label=_("Deposit check given"),
                    help_text=_("Only treasurers can validate this field")
                )
            else:
                form.fields["deposit_given"] = forms.BooleanField(
                    required=True,
                    initial=False,
                    label=_("Create deposit transaction"),
                    help_text=_("A transaction of %(amount).2f€ will be created from the user's Note account") % {
                        'amount': registration.wei.deposit_amount / 100
                    }
                )

        if 'bus' in form.fields:
            # For 2A+ and hardcoded 1A
            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⋅e WEI")
                    ).all()
        return form

    @transaction.atomic
    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

        if "deposit_given" in form.data:
            registration.deposit_given = form.data["deposit_given"] == "on"
            registration.save()
        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

        fee = club.membership_fee_paid if user.profile.paid else club.membership_fee_unpaid
        if registration.soge_credit:
            fee = registration.wei.fee_soge_credit

        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:
            credit_amount = 0

        # Calculer le montant total nécessaire (frais + caution si transaction)
        total_needed = fee
        if registration.deposit_type == 'note':
            total_needed += club.deposit_amount

        # Vérifier que l'utilisateur a assez d'argent pour tout payer
        if user.note.balance + credit_amount < total_needed:
            form.add_error('credit_type',
                           _("This user doesn't have enough money to join this club and pay the deposit. "
                               "Current balance: %(balance)d€, credit: %(credit)d€, needed: %(needed)d€") % {
                               'balance': user.note.balance,
                               'credit': credit_amount,
                               'needed': total_needed}
                           )
            return self.form_invalid(form)

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

            if not first_name:
                form.add_error('first_name', _("This field is required."))
                return self.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()

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

        # Créer la transaction de caution si nécessaire
        if registration.deposit_type == 'note':
            from note.models import Transaction
            Transaction.objects.create(
                source=user.note,
                destination=club.note,
                quantity=1,
                amount=club.deposit_amount,
                reason=_("Deposit %(name)s") % {'name': club.name},
                valid=True,
            )

        return super().form_valid(form)

    def form_invalid(self, form):
        registration = getattr(form.instance, "registration", None)
        if registration is not None:
            registration.deposit_given = False
            registration.save()
        return super().form_invalid(form)

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


class WEIUpdateMembershipView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    """
    Update a membership for the WEI
    """
    model = WEIMembership
    context_object_name = "membership"
    template_name = "wei/weimembership_update.html"
    extra_context = {"title": _("Update WEI Membership")}

    def dispatch(self, request, *args, **kwargs):
        wei = self.get_object().registration.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,)))
        # Store the validate parameter in the view's state
        return super().dispatch(request, *args, **kwargs)

    def get_form(self):
        form = WEIMembershipForm(
            self.request.POST or None,
            self.request.FILES or None,
            instance=self.object,
            wei=self.object.registration.wei,
        )

        form.fields["roles"].initial = self.object.roles.all()
        form.fields["bus"].initial = self.object.bus
        form.fields["team"].initial = self.object.team

        del form.fields["credit_type"]
        del form.fields["credit_amount"]
        del form.fields["first_name"]
        del form.fields["last_name"]
        del form.fields["bank"]

        return form

    def get_success_url(self):
        return reverse_lazy("wei:wei_detail", kwargs={"pk": self.object.registration.wei.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 request.GET.get("reset") == "true":
            info = self.survey.information
            info.reset(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

    @transaction.atomic
    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)
        club = WEIRegistration.objects.get(pk=self.kwargs["pk"]).wei
        context["club"] = club

        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, "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, "wei.add_weiregistration", empty_old_registration)

        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, 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

            with open(os.devnull, "wb") as devnull:
                error = subprocess.Popen(
                    ["/usr/bin/xelatex", "-interaction=nonstopmode", "{}/wei-list.tex".format(tmp_dir)],
                    cwd=tmp_dir,
                    stderr=devnull,
                    stdout=devnull,
                ).wait()

            if error:
                with open("{}/wei-list.log".format(tmp_dir), "r") as f:
                    log = f.read()
                raise IOError("An error attempted while generating a WEI list (code=" + str(error) + ")\n\n" + log)

            # Display the generated pdf as a HTTP Response
            with open("{}/wei-list.pdf".format(tmp_dir), 'rb') as f:
                pdf = f.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


class WEI1AListView(LoginRequiredMixin, ProtectQuerysetMixin, SingleTableView):
    model = WEIRegistration
    template_name = "wei/1A_list.html"
    table_class = WEIRegistration1ATable
    extra_context = {"title": _("Attribute buses to first year members")}

    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, filter_permissions=True, **kwargs):
        qs = super().get_queryset(filter_permissions, **kwargs)
        qs = qs.filter(first_year=True, membership__isnull=False)
        qs = qs.filter(wei=self.club)
        qs = qs.order_by('-membership__bus')
        return qs

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['club'] = self.club
        context['bus_repartition_table'] = BusRepartitionTable(
            Bus.objects.filter(wei=self.club, size__gt=0)
                       .filter(PermissionBackend.filter_queryset(self.request, Bus, "view"))
                       .all())
        return context


class WEIAttributeBus1AView(ProtectQuerysetMixin, DetailView):
    model = WEIRegistration
    template_name = "wei/attribute_bus_1A.html"
    extra_context = {"title": _("Attribute bus")}

    def get_queryset(self, filter_permissions=True, **kwargs):
        qs = super().get_queryset(filter_permissions, **kwargs)
        qs = qs.filter(first_year=True)
        return qs

    def dispatch(self, request, *args, **kwargs):
        obj = self.get_object()
        if 'selected_bus_pk' not in obj.information:
            return redirect(reverse_lazy('wei:wei_survey', args=(obj.pk,)))
        return super().dispatch(request, *args, **kwargs)

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


class WEIAttributeBus1ANextView(LoginRequiredMixin, RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        wei = WEIClub.objects.filter(pk=self.kwargs['pk'])
        if not wei.exists():
            raise Http404
        wei = wei.get()

        # On cherche d'abord les 1A qui ont une inscription validée (membership) mais pas de bus
        qs = WEIRegistration.objects.filter(
            wei=wei,
            first_year=True,
            membership__isnull=False,
            membership__bus__isnull=True
        )

        # Parmi eux, on prend ceux qui ont répondu au questionnaire (ont un bus préféré)
        qs = qs.filter(information_json__contains='selected_bus_pk')

        if not qs.exists():
            # Si on ne trouve personne, on affiche un message et on retourne à la liste
            messages.info(self.request, _("No first year student without a bus found. Either all of them have a bus, or none has filled the survey yet."))
            return reverse_lazy('wei:wei_1A_list', args=(wei.pk,))

        # On redirige vers la page d'attribution pour le premier étudiant trouvé
        return reverse_lazy('wei:wei_bus_1A', args=(qs.first().pk,))


class BusInformationUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
    model = Bus

    def get_form_class(self):
        return CurrentSurvey.get_algorithm_class().get_bus_information_form()

    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
        context["information"] = CurrentSurvey.get_algorithm_class().get_bus_information(self.object)
        self.object.save()
        return context

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