# Copyright (C) 2020 by Animath
# SPDX-License-Identifier: GPL-3.0-or-later

import json
import os
import subprocess
from tempfile import mkdtemp

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
from django.core.exceptions import PermissionDenied, ValidationError
from django.db import transaction
from django.db.models import Q
from django.http import FileResponse, Http404
from django.shortcuts import redirect, resolve_url
from django.template.loader import render_to_string
from django.urls import reverse_lazy
from django.utils import timezone, translation
from django.utils.crypto import get_random_string
from django.utils.http import urlsafe_base64_decode
from django.utils.text import format_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
from django_tables2 import SingleTableView
from magic import Magic
from participation.models import Passage, Solution, Tournament, WrittenReview
from tfjm.tokens import email_validation_token
from tfjm.views import UserMixin, UserRegistrationMixin, VolunteerMixin

from .forms import AddOrganizerForm, CoachRegistrationForm, HealthSheetForm, \
    ParentalAuthorizationFinalForm, ParentalAuthorizationForm, PaymentAdminForm, PaymentForm, \
    PhotoAuthorizationFinalForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm, \
    VaccineSheetForm, VolunteerRegistrationForm
from .models import ParticipantRegistration, Payment, Registration, StudentRegistration
from .tables import RegistrationTable


class SignupView(CreateView):
    """
    Signup, as a participant or a coach.
    """
    model = User
    form_class = SignupForm
    template_name = "registration/signup.html"
    extra_context = dict(title=_("Sign up"))

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

        context["student_registration_form"] = StudentRegistrationForm(self.request.POST or None)
        context["coach_registration_form"] = CoachRegistrationForm(self.request.POST or None)

        del context["student_registration_form"].fields["team"]
        del context["student_registration_form"].fields["email_confirmed"]
        del context["coach_registration_form"].fields["team"]
        del context["coach_registration_form"].fields["email_confirmed"]

        return context

    def get_form(self, form_class=None):
        form = super().get_form(form_class)
        if self.request.method in ("POST", "PUT") \
                and (not self.request.user.is_authenticated or not self.request.user.registration.is_admin):
            # Check that registrations are opened
            now = timezone.now()
            if now < settings.REGISTRATION_DATES['open']:
                form.add_error(None, format_lazy(_("Registrations are not opened yet. "
                                                   "They will open on the {opening_date:%Y-%m-%d %H:%M}."),
                                                 opening_date=settings.REGISTRATION_DATES['open']))
            elif now > settings.REGISTRATION_DATES['close']:
                form.add_error(None, format_lazy(_("Registrations for this year are closed since "
                                                   "{closing_date:%Y-%m-%d %H:%M}."),
                                                 closing_date=settings.REGISTRATION_DATES['close']))
        return form

    @transaction.atomic
    def form_valid(self, form):
        role = form.cleaned_data["role"]
        if role == "participant":
            registration_form = StudentRegistrationForm(self.request.POST)
        else:
            registration_form = CoachRegistrationForm(self.request.POST)
        del registration_form.fields["team"]
        del registration_form.fields["email_confirmed"]

        if not registration_form.is_valid():
            return self.form_invalid(form)

        ret = super().form_valid(form)
        registration = registration_form.instance
        registration.user = form.instance
        registration.save()
        registration.send_email_validation_link()

        return ret

    def get_success_url(self):
        return reverse_lazy("registration:email_validation_sent")


class AddOrganizerView(VolunteerMixin, CreateView):
    model = User
    form_class = AddOrganizerForm
    template_name = "registration/add_organizer.html"
    extra_context = dict(title=_("Add organizer"))

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

        context["volunteer_registration_form"] = VolunteerRegistrationForm(self.request.POST or None)
        del context["volunteer_registration_form"].fields["email_confirmed"]
        if not self.request.user.registration.is_admin:
            del context["volunteer_registration_form"].fields["admin"]

        return context

    @transaction.atomic
    def form_valid(self, form):
        registration_form = VolunteerRegistrationForm(self.request.POST)
        del registration_form.fields["email_confirmed"]
        if not self.request.user.registration.is_admin:
            del registration_form.fields["admin"]

        if not registration_form.is_valid():
            return self.form_invalid(form)

        ret = super().form_valid(form)
        registration = registration_form.instance
        registration.user = form.instance
        registration.save()
        registration.send_email_validation_link()

        password = get_random_string(16)
        form.instance.set_password(password)
        form.instance.save()

        with translation.override(settings.PREFERRED_LANGUAGE_CODE):
            subject = f"[{settings.APP_NAME}] " + str(_("New organizer account"))
            site = Site.objects.first()
            message = render_to_string('registration/mails/add_organizer.txt', dict(user=registration.user,
                                                                                    inviter=self.request.user,
                                                                                    password=password,
                                                                                    domain=site.domain))
            html = render_to_string('registration/mails/add_organizer.html', dict(user=registration.user,
                                                                                  inviter=self.request.user,
                                                                                  password=password,
                                                                                  domain=site.domain))
        registration.user.email_user(subject, message, html_message=html)

        if registration.is_admin:
            registration.user.is_superuser = True
            registration.user.save()

        return ret

    def get_success_url(self):
        return reverse_lazy("registration:email_validation_sent")


class UserValidateView(TemplateView):
    """
    A view to validate the email address.
    """
    title = _("Email validation")
    template_name = 'registration/email_validation_complete.html'
    extra_context = dict(title=_("Validate email"))

    def get(self, *args, **kwargs):
        """
        With a given token and user id (in params), validate the email address.
        """
        assert 'uidb64' in kwargs and 'token' in kwargs

        self.validlink = False
        user = self.get_user(kwargs['uidb64'])
        token = kwargs['token']

        # Validate the token
        if user is not None and email_validation_token.check_token(user, token):
            self.validlink = True
            user.registration.email_confirmed = True
            user.registration.save()
        return self.render_to_response(self.get_context_data(), status=200 if self.validlink else 400)

    def get_user(self, uidb64):
        """
        Get user from the base64-encoded string.
        """
        try:
            # urlsafe_base64_decode() decodes to bytestring
            uid = urlsafe_base64_decode(uidb64).decode()
            user = User.objects.get(pk=uid)
        except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
            user = None
        return user

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['user_object'] = self.get_user(self.kwargs["uidb64"])
        context['login_url'] = resolve_url(settings.LOGIN_URL)
        if self.validlink:
            context['validlink'] = True
        else:
            context.update({
                'title': _('Email validation unsuccessful'),
                'validlink': False,
            })
        return context


class UserValidationEmailSentView(TemplateView):
    """
    Display the information that the validation link has been sent.
    """
    template_name = 'registration/email_validation_email_sent.html'
    extra_context = dict(title=_('Email validation email sent'))


class UserResendValidationEmailView(LoginRequiredMixin, DetailView):
    """
    Rensend the email validation link.
    """
    model = User
    extra_context = dict(title=_("Resend email validation link"))

    def get(self, request, *args, **kwargs):
        user = self.get_object()
        user.registration.send_email_validation_link()
        return redirect('registration:email_validation_sent')


class MyAccountDetailView(LoginRequiredMixin, RedirectView):
    """
    Redirect to our own profile detail page.
    """

    def get_redirect_url(self, *args, **kwargs):
        return reverse_lazy("registration:user_detail", args=(self.request.user.pk,))


class UserDetailView(LoginRequiredMixin, DetailView):
    """
    Display the detail about a user.
    """

    model = User
    context_object_name = "user_object"
    template_name = "registration/user_detail.html"

    def dispatch(self, request, *args, **kwargs):
        me = request.user
        if not me.is_authenticated:
            return self.handle_no_permission()
        user = self.get_object()
        if user == me or me.registration.is_admin or me.registration.is_volunteer \
                and user.registration.participates and user.registration.team \
                and (user.registration.team.participation.tournament in me.registration.organized_tournaments.all()
                     or user.registration.team.participation.final
                     and Tournament.final_tournament() in me.registration.organized_tournaments.all()) \
                or user.registration.is_volunteer and me.registration.is_volunteer \
                and me.registration.interesting_tournaments.intersection(user.registration.interesting_tournaments):
            return super().dispatch(request, *args, **kwargs)
        raise PermissionDenied

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["title"] = _("Detail of user {user}").format(user=str(self.object.registration))
        return context


class UserListView(VolunteerMixin, SingleTableView):
    """
    Display the list of all registered users.
    """
    model = Registration
    table_class = RegistrationTable
    template_name = "registration/user_list.html"


class UserUpdateView(UserMixin, UpdateView):
    """
    Update the detail about a user and its registration.
    """
    model = User
    context_object_name = "user_object"
    form_class = UserForm
    template_name = "registration/update_user.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        user = self.get_object()
        context["title"] = _("Update user {user}").format(user=str(self.object.registration))
        context["registration_form"] = user.registration.form_class(data=self.request.POST or None,
                                                                    instance=self.object.registration)
        if not self.request.user.registration.is_admin:
            if "team" in context["registration_form"].fields:
                del context["registration_form"].fields["team"]
            if "admin" in context["registration_form"].fields:
                del context["registration_form"].fields["admin"]
            del context["registration_form"].fields["email_confirmed"]
        return context

    @transaction.atomic
    def form_valid(self, form):
        user = form.instance
        registration_form = user.registration.form_class(data=self.request.POST or None,
                                                         instance=self.object.registration)
        if not self.request.user.registration.is_admin:
            if "team" in registration_form.fields:
                del registration_form.fields["team"]
            if "admin" in registration_form.fields:
                del registration_form.fields["admin"]
            del registration_form.fields["email_confirmed"]

        if not registration_form.is_valid():
            return self.form_invalid(form)

        registration_form.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("registration:user_detail", args=(self.object.pk,))


class UserUploadPhotoAuthorizationView(UserRegistrationMixin, UpdateView):
    """
    A participant can send its photo authorization.
    """
    model = ParticipantRegistration
    template_name = "registration/upload_photo_authorization.html"
    extra_context = dict(title=_("Upload photo authorization"))

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.object.team:
            tournament = self.object.team.participation.tournament \
                if 'final' not in self.request.path else Tournament.final_tournament()
            context["tournament"] = tournament
        return context

    def get_form_class(self):
        return PhotoAuthorizationForm if 'final' not in self.request.path else PhotoAuthorizationFinalForm

    @transaction.atomic
    def form_valid(self, form):
        old_instance: ParticipantRegistration = ParticipantRegistration.objects.get(pk=self.object.pk)
        old_field = old_instance.photo_authorization \
            if 'final' not in self.request.path else old_instance.photo_authorization_final
        if old_field:
            old_field.delete()
            old_instance.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))


class UserUploadHealthSheetView(UserRegistrationMixin, UpdateView):
    """
    A participant can send its health sheet.
    """
    model = StudentRegistration
    form_class = HealthSheetForm
    template_name = "registration/upload_health_sheet.html"
    extra_context = dict(title=_("Upload health sheet"))

    @transaction.atomic
    def form_valid(self, form):
        old_instance = StudentRegistration.objects.get(pk=self.object.pk)
        if old_instance.health_sheet:
            old_instance.health_sheet.delete()
            old_instance.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))


class UserUploadVaccineSheetView(UserRegistrationMixin, UpdateView):
    """
    A participant can send its vaccine sheet.
    """
    model = StudentRegistration
    form_class = VaccineSheetForm
    template_name = "registration/upload_vaccine_sheet.html"
    extra_context = dict(title=_("Upload vaccine sheet"))

    @transaction.atomic
    def form_valid(self, form):
        old_instance = StudentRegistration.objects.get(pk=self.object.pk)
        if old_instance.vaccine_sheet:
            old_instance.vaccine_sheet.delete()
            old_instance.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))


class UserUploadParentalAuthorizationView(UserRegistrationMixin, UpdateView):
    """
    A participant can send its parental authorization.
    """
    model = StudentRegistration
    template_name = "registration/upload_parental_authorization.html"
    extra_context = dict(title=_("Upload parental authorization"))

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.object.team:
            tournament = self.object.team.participation.tournament \
                if 'final' not in self.request.path else Tournament.final_tournament()
            context["tournament"] = tournament
        return context

    def get_form_class(self):
        return ParentalAuthorizationForm if 'final' not in self.request.path else ParentalAuthorizationFinalForm

    @transaction.atomic
    def form_valid(self, form):
        old_instance: StudentRegistration = StudentRegistration.objects.get(pk=self.object.pk)
        old_field = old_instance.parental_authorization \
            if 'final' not in self.request.path else old_instance.parental_authorization_final
        if old_field:
            old_field.delete()
            old_instance.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))


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

        translation.activate("fr")

        if "registration_id" in self.request.GET:
            registration = Registration.objects.get(pk=self.request.GET.get("registration_id"))
            # Don't get unwanted information
            if registration.user == self.request.user \
                    or self.request.user.is_authenticated and self.request.user.registration.is_admin:
                context["registration"] = registration
        if "tournament_id" in self.request.GET and self.request.GET.get("tournament_id").isnumeric():
            if not Tournament.objects.filter(pk=self.request.GET.get("tournament_id")).exists():
                raise PermissionDenied("Ce tournoi n'existe pas.")
            context["tournament"] = Tournament.objects.get(pk=self.request.GET.get("tournament_id"))
        elif "tournament_name" in self.request.GET:
            if not Tournament.objects.filter(name__iexact=self.request.GET.get("tournament_name")).exists():
                raise PermissionDenied("Ce tournoi n'existe pas.")
            context["tournament"] = Tournament.objects.get(name__iexact=self.request.GET.get("tournament_name"))
        elif settings.SINGLE_TOURNAMENT:
            # One single tournament (for ETEAM)
            context["tournament"] = Tournament.objects.first()
        else:
            raise PermissionDenied("Merci d'indiquer un tournoi.")

        return context

    def render_to_response(self, context, **response_kwargs):
        template_name = self.get_template_names()[0]
        with translation.override(settings.PREFERRED_LANGUAGE_CODE):
            tex = render_to_string(template_name, context=context, request=self.request)
        temp_dir = mkdtemp()
        with open(os.path.join(temp_dir, "texput.tex"), "w") as f:
            f.write(tex)
        process = subprocess.Popen(["pdflatex", "-interaction=nonstopmode", f"-output-directory={temp_dir}",
                                    os.path.join(temp_dir, "texput.tex"), ])
        process.wait()
        return FileResponse(open(os.path.join(temp_dir, "texput.pdf"), "rb"),
                            content_type="application/pdf",
                            filename=template_name.split("/")[-1][:-3] + "pdf")


class AdultPhotoAuthorizationTemplateView(AuthorizationTemplateView):
    def get_template_names(self):
        if settings.TFJM_APP == "TFJM":
            return ["registration/tex/Autorisation_droit_image_majeur.tex"]
        elif settings.TFJM_APP == "ETEAM":
            return ["registration/tex/photo_authorization_eteam_adult.tex"]


class ChildPhotoAuthorizationTemplateView(AuthorizationTemplateView):
    def get_template_names(self):
        if settings.TFJM_APP == "TFJM":
            return ["registration/tex/Autorisation_droit_image_mineur.tex"]
        elif settings.TFJM_APP == "ETEAM":
            return ["registration/tex/photo_authorization_eteam_child.tex"]


class ParentalAuthorizationTemplateView(AuthorizationTemplateView):
    template_name = "registration/tex/Autorisation_parentale.tex"

    def get_template_names(self):
        if settings.TFJM_APP == "TFJM":
            return ["registration/tex/Autorisation_parentale.tex"]
        elif settings.TFJM_APP == "ETEAM":
            return ["registration/tex/parental_authorization_eteam.tex"]


class InstructionsTemplateView(AuthorizationTemplateView):
    template_name = "registration/tex/Instructions.tex"


class PaymentUpdateView(LoginRequiredMixin, UpdateView):
    model = Payment
    form_class = PaymentAdminForm

    def dispatch(self, request, *args, **kwargs):
        user = self.request.user
        object = self.get_object()
        if not user.is_authenticated or \
                not user.registration.is_admin \
                and (user.registration.is_volunteer and user.registration not in object.tournament.organizers.all()
                     or user.registration.is_student and user.registration not in object.registrations.all()
                     or user.registration.is_coach and user.registration.team != object.team):
            return self.handle_no_permission()
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data()
        context['title'] = _("Update payment")
        # Grouping is only possible if there isn't any validated payment in the team
        context['can_group'] = all(p.valid is False for reg in self.object.team.students.all()
                                   for p in reg.payments.filter(final=self.object.final).all())
        context['bank_transfer_form'] = PaymentForm(payment_type='bank_transfer',
                                                    data=self.request.POST or None,
                                                    instance=self.object)

        if not self.object.grouped:
            context['scholarship_form'] = PaymentForm(payment_type='scholarship',
                                                      data=self.request.POST or None,
                                                      instance=self.object)

        context['other_form'] = PaymentForm(payment_type='other',
                                            data=self.request.POST or None,
                                            instance=self.object)
        return context

    def form_valid(self, form):
        old_instance = Payment.objects.get(pk=self.object.pk)
        if self.request.user.registration.participates:
            if old_instance.valid is not False:
                raise PermissionDenied(_("This payment is already valid or pending validation."))
        if old_instance.valid is False:
            form.instance.valid = None
        if old_instance.receipt:
            old_instance.receipt.delete()
            old_instance.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("participation:team_detail", args=(self.object.registrations.first().team.pk,))


class PaymentUpdateGroupView(LoginRequiredMixin, DetailView):
    model = Payment

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

        if not self.request.user.is_authenticated or \
                not self.request.user.registration.is_admin \
                and (self.request.user.registration not in self.get_object().registrations.all()
                     or payment.valid is not False):
            return self.handle_no_permission()

        if any(p.valid is not False for reg in payment.team.students.all()
               for p in reg.payments.filter(final=payment.final).all()):
            raise PermissionDenied(_("Since one payment is already validated, or pending validation, "
                                     "grouping is not possible."))
        return super().dispatch(request, *args, **kwargs)

    def get(self, request, *args, **kwargs):
        payment = self.get_object()

        if payment.valid is not False:
            raise PermissionDenied(_("This payment is already valid or pending validation."))

        if payment.grouped:
            registrations = list(payment.registrations.all())
            first_reg = registrations[0]
            payment.registrations.set([first_reg])
            payment.grouped = False
            tournament = first_reg.team.participation.tournament if not payment.final else Tournament.final_tournament()
            payment.amount = tournament.price
            payment.checkout_intent_id = None
            payment.save()
            for registration in registrations[1:]:
                p = Payment.objects.create(type=payment.type,
                                           grouped=False,
                                           final=payment.final,
                                           amount=tournament.price,
                                           receipt=payment.receipt,
                                           additional_information=payment.additional_information)
                p.registrations.set([registration])
                p.save()
        else:
            reg = payment.registrations.get()
            tournament = reg.team.participation.tournament if not payment.final else Tournament.final_tournament()
            for student in reg.team.students.all():
                if student != reg:
                    Payment.objects.filter(registrations=student, final=payment.final).delete()
                payment.registrations.add(student)
            payment.amount = tournament.price * reg.team.students.count()
            payment.grouped = True
            payment.checkout_intent_id = None
            payment.save()

        return redirect(reverse_lazy("registration:update_payment", args=(payment.pk,)))


class PaymentRedirectHelloAssoView(AccessMixin, DetailView):
    model = Payment

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

        # An external user has the link for the payment
        token = request.GET.get('token', "")
        if token and token == payment.token:
            return super().dispatch(request, *args, **kwargs)

        if not request.user.is_authenticated:
            return self.handle_no_permission()

        if not request.user.registration.is_admin:
            if request.user.registration.is_volunteer \
                    and payment.tournament not in request.user.registration.organized_tournaments.all():
                return self.handle_no_permission()

            if request.user.registration.is_student \
                    and request.user.registration not in payment.registrations.all():
                return self.handle_no_permission()

            if request.user.registration.is_coach \
                    and request.user.registration.team != payment.team:
                return self.handle_no_permission()

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

    def get(self, request, *args, **kwargs):
        payment = self.get_object()
        if payment.valid is not False:
            raise PermissionDenied(_("The payment is already valid or pending validation."))

        checkout_intent = payment.create_checkout_intent()
        return redirect(checkout_intent["redirectUrl"])


class PaymentHelloAssoReturnView(DetailView):
    model = Payment

    def get(self, request, *args, **kwargs):
        checkout_id = request.GET.get("checkoutIntentId")
        payment = self.get_object()
        payment_qs = Payment.objects.exclude(valid=True).filter(checkout_intent_id=checkout_id).filter(pk=payment.pk)
        if not payment_qs.exists():
            messages.error(request, _("The payment is not found or is already validated."), "danger")
            return redirect("index")

        team = payment.team
        tournament = payment.tournament
        right_to_see = not request.user.is_anonymous \
            and (request.user.registration.is_admin
                 or request.user.registration in payment.registrations.all()
                 or (request.user.registration.is_volunteer
                     and tournament in request.user.registration.organized_tournaments.all())
                 or (request.user.registration.is_coach and request.user.registration.team == team))

        if right_to_see:
            error_response = redirect("registration:update_payment", pk=payment.pk)
        else:
            error_response = redirect("index")

        return_type = request.GET.get("type")
        if return_type == "error":
            messages.error(request, format_lazy(_("An error occurred during the payment: {error}"),
                                                error=request.GET.get("error")), "danger")
            return error_response
        elif return_type == "return":
            code = request.GET.get("code")
            if code == "refused":
                messages.error(request, _("The payment has been refused."), "danger")
                return error_response
            elif code != "succeeded":
                messages.error(request, format_lazy(_("The return code is unknown: {code}"), code=code), "danger")
                return error_response
        else:
            messages.error(request, format_lazy(_("The return type is unknown: {type}"), type=return_type), "danger")
            return error_response

        checkout_intent = payment.get_checkout_intent()
        if 'order' in checkout_intent:
            payment.type = "helloasso"
            payment.valid = True
            payment.additional_information = json.dumps(checkout_intent['order'])
            payment.save()
            messages.success(request, _("The payment has been successfully validated! "
                                        "Your registration is now complete."))
            payment.send_helloasso_payment_confirmation_mail()
        else:
            payment.type = "helloasso"
            payment.valid = None
            payment.save()
            messages.success(request, _("Your payment is done! "
                                        "The validation of your payment may takes a few minutes, "
                                        "and will be automatically done. "
                                        "If it is not the case, please contact us."))

        if right_to_see:
            return redirect("participation:team_detail", pk=team.pk)
        else:
            return redirect("index")


class PhotoAuthorizationView(LoginRequiredMixin, View):
    """
    Display the sent photo authorization.
    """

    def get(self, request, *args, **kwargs):
        filename = kwargs["filename"]
        path = f"media/authorization/photo/{filename}"
        student_qs = ParticipantRegistration.objects.filter(Q(photo_authorization__endswith=filename)
                                                            | Q(photo_authorization_final__endswith=filename))
        if not os.path.exists(path) or not student_qs.exists():
            raise Http404
        student = student_qs.get()
        user = request.user
        if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
                and student.team.participation.tournament in user.registration.organized_tournaments.all()):
            raise PermissionDenied
        # Guess mime type of the file
        mime = Magic(mime=True)
        mime_type = mime.from_file(path)
        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
        # Replace file name
        true_file_name = _("Photo authorization of {student}.{ext}").format(student=str(student), ext=ext)
        return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)


class HealthSheetView(LoginRequiredMixin, View):
    """
    Display the sent health sheet.
    """

    def get(self, request, *args, **kwargs):
        filename = kwargs["filename"]
        path = f"media/authorization/health/{filename}"
        if not os.path.exists(path):
            raise Http404
        student = StudentRegistration.objects.get(health_sheet__endswith=filename)
        user = request.user
        if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
                and student.team.participation.tournament in user.registration.organized_tournaments.all()):
            raise PermissionDenied
        # Guess mime type of the file
        mime = Magic(mime=True)
        mime_type = mime.from_file(path)
        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
        # Replace file name
        true_file_name = _("Health sheet of {student}.{ext}").format(student=str(student), ext=ext)
        return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)


class VaccineSheetView(LoginRequiredMixin, View):
    """
    Display the sent health sheet.
    """

    def get(self, request, *args, **kwargs):
        filename = kwargs["filename"]
        path = f"media/authorization/vaccine/{filename}"
        if not os.path.exists(path):
            raise Http404
        student = StudentRegistration.objects.get(vaccine_sheet__endswith=filename)
        user = request.user
        if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
                and student.team.participation.tournament in user.registration.organized_tournaments.all()):
            raise PermissionDenied
        # Guess mime type of the file
        mime = Magic(mime=True)
        mime_type = mime.from_file(path)
        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
        # Replace file name
        true_file_name = _("Vaccine sheet of {student}.{ext}").format(student=str(student), ext=ext)
        return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)


class ParentalAuthorizationView(LoginRequiredMixin, View):
    """
    Display the sent parental authorization.
    """

    def get(self, request, *args, **kwargs):
        filename = kwargs["filename"]
        path = f"media/authorization/parental/{filename}"
        if not os.path.exists(path):
            raise Http404
        student = StudentRegistration.objects.get(Q(parental_authorization__endswith=filename)
                                                  | Q(parental_authorization_final__endswith=filename))
        user = request.user
        if not (student.user == user or user.registration.is_admin or user.registration.is_volunteer and student.team
                and student.team.participation.tournament in user.registration.organized_tournaments.all()):
            raise PermissionDenied
        # Guess mime type of the file
        mime = Magic(mime=True)
        mime_type = mime.from_file(path)
        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
        # Replace file name
        true_file_name = _("Parental authorization of {student}.{ext}").format(student=str(student), ext=ext)
        return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)


class ReceiptView(LoginRequiredMixin, View):
    """
    Display the sent payment receipt or scholarship notification.
    """

    def get(self, request, *args, **kwargs):
        filename = kwargs["filename"]
        path = f"media/authorization/receipt/{filename}"
        if not os.path.exists(path):
            raise Http404
        payment = Payment.objects.get(receipt__endswith=filename)
        user = request.user
        if not (user.registration in payment.registrations.all() or user.registration.is_admin):
            raise PermissionDenied
        # Guess mime type of the file
        mime = Magic(mime=True)
        mime_type = mime.from_file(path)
        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
        # Replace file name
        registrations = ", ".join(str(registration) for registration in payment.registrations.all())
        true_file_name = _("Payment receipt of {registrations}.{ext}").format(registrations=registrations, ext=ext)
        return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)


class SolutionView(LoginRequiredMixin, View):
    """
    Display the sent solution.
    """

    def get(self, request, *args, **kwargs):
        filename = kwargs["filename"]
        path = f"media/solutions/{filename}"
        if not os.path.exists(path):
            raise Http404
        solution = Solution.objects.get(file__endswith=filename)
        user = request.user
        if user.registration.participates and user.registration.team.participation:
            passage_participant_qs = Passage.objects.filter(Q(reporter=user.registration.team.participation)
                                                            | Q(opponent=user.registration.team.participation)
                                                            | Q(reviewer=user.registration.team.participation)
                                                            | Q(observer=user.registration.team.participation),
                                                            reporter=solution.participation,
                                                            solution_number=solution.problem)
        else:
            passage_participant_qs = Passage.objects.none()
        if not (user.registration.is_admin
                or (user.registration.is_volunteer
                    and user.registration in solution.tournament.organizers.all())
                or (user.registration.is_volunteer
                    and user.registration.pools_presided.filter(tournament=solution.tournament).exists())
                or user.registration.is_volunteer
                and Passage.objects.filter(Q(pool__juries=user.registration)
                                           | Q(pool__tournament__in=user.registration.organized_tournaments.all()),
                                           reporter=solution.participation,
                                           solution_number=solution.problem).exists()
                or user.registration.participates and user.registration.team
                and (solution.participation.team == user.registration.team or
                     any(passage.pool.round == 1
                         or (passage.pool.round == 2 and passage.pool.tournament.solutions_available_second_phase)
                         or (passage.pool.round == 3 and passage.pool.tournament.solutions_available_third_phase)
                         for passage in passage_participant_qs.all()))):
            raise PermissionDenied
        # Guess mime type of the file
        mime = Magic(mime=True)
        mime_type = mime.from_file(path)
        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
        # Replace file name
        true_file_name = str(solution) + f".{ext}"
        return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)


class WrittenReviewView(LoginRequiredMixin, View):
    """
    Display the sent written reviews.
    """

    def get(self, request, *args, **kwargs):
        filename = kwargs["filename"]
        path = f"media/reviews/{filename}"
        if not os.path.exists(path):
            raise Http404
        review = WrittenReview.objects.get(file__endswith=filename)
        user = request.user
        if not (user.registration.is_admin or user.registration.is_volunteer
                and (user.registration in review.passage.pool.juries.all()
                     or user.registration in review.passage.pool.tournament.organizers.all()
                     or user.registration.pools_presided.filter(tournament=review.passage.pool.tournament).exists())
                or user.registration.participates and user.registration.team == review.participation.team):
            raise PermissionDenied
        # Guess mime type of the file
        mime = Magic(mime=True)
        mime_type = mime.from_file(path)
        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
        # Replace file name
        true_file_name = str(review) + f".{ext}"
        return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)


class UserImpersonateView(LoginRequiredMixin, RedirectView):
    """
    An administrator can log in through this page as someone else, and act as this other person.
    """

    def dispatch(self, request, *args, **kwargs):
        if self.request.user.registration.is_admin:
            if not User.objects.filter(pk=kwargs["pk"]).exists():
                raise Http404
            session = request.session
            session["admin"] = request.user.pk
            session["_fake_user_id"] = kwargs["pk"]
        return super().dispatch(request, *args, **kwargs)

    def get_redirect_url(self, *args, **kwargs):
        return reverse_lazy("registration:user_detail", args=(kwargs["pk"],))


class ResetAdminView(LoginRequiredMixin, View):
    """
    Return to admin view, clear the session field that let an administrator to log in as someone else.
    """

    def dispatch(self, request, *args, **kwargs):
        user = request.user
        if not user.is_authenticated:
            return self.handle_no_permission()
        if "_fake_user_id" in request.session:
            del request.session["_fake_user_id"]
        return redirect(request.GET.get("path", reverse_lazy("index")))
