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

from datetime import date

from django.contrib.sites.models import Site
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.template import loader
from django.urls import reverse_lazy
from django.utils import timezone
from django.utils.crypto import get_random_string
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode
from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField
from polymorphic.models import PolymorphicModel
from tfjm.tokens import email_validation_token


class Registration(PolymorphicModel):
    """
    Registrations store extra content that are not asked in the User Model.
    This is specific to the role of the user, see StudentRegistration,
    CoachRegistration or VolunteerRegistration.
    """
    user = models.OneToOneField(
        "auth.User",
        on_delete=models.CASCADE,
        verbose_name=_("user"),
    )

    give_contact_to_animath = models.BooleanField(
        default=False,
        verbose_name=_("Grant Animath to contact me in the future about other actions"),
    )

    email_confirmed = models.BooleanField(
        default=False,
        verbose_name=_("email confirmed"),
    )

    def send_email_validation_link(self):
        """
        The account got created or the email got changed.
        Send an email that contains a link to validate the address.
        """
        subject = "[TFJM²] " + str(_("Activate your TFJM² account"))
        token = email_validation_token.make_token(self.user)
        uid = urlsafe_base64_encode(force_bytes(self.user.pk))
        site = Site.objects.first()
        message = loader.render_to_string('registration/mails/email_validation_email.txt',
                                          {
                                              'user': self.user,
                                              'domain': site.domain,
                                              'token': token,
                                              'uid': uid,
                                          })
        html = loader.render_to_string('registration/mails/email_validation_email.html',
                                       {
                                           'user': self.user,
                                           'domain': site.domain,
                                           'token': token,
                                           'uid': uid,
                                       })
        self.user.email_user(subject, message, html_message=html)

    @property
    def type(self):  # pragma: no cover
        raise NotImplementedError

    @property
    def form_class(self):  # pragma: no cover
        raise NotImplementedError

    @property
    def participates(self):
        return isinstance(self, ParticipantRegistration)

    @property
    def is_admin(self):
        return isinstance(self, VolunteerRegistration) and self.admin or self.user.is_superuser

    @property
    def is_volunteer(self):
        return isinstance(self, VolunteerRegistration)

    @property
    def matrix_username(self):
        return f"tfjm_{self.user.pk}"

    def get_absolute_url(self):
        return reverse_lazy("registration:user_detail", args=(self.user_id,))

    def __str__(self):
        return f"{self.user.first_name} {self.user.last_name}"

    class Meta:
        verbose_name = _("registration")
        verbose_name_plural = _("registrations")


def get_random_photo_filename(instance, filename):
    return "authorization/photo/" + get_random_string(64)


def get_random_health_filename(instance, filename):
    return "authorization/health/" + get_random_string(64)


def get_random_vaccine_filename(instance, filename):
    return "authorization/vaccine/" + get_random_string(64)


def get_random_parental_filename(instance, filename):
    return "authorization/parental/" + get_random_string(64)


class ParticipantRegistration(Registration):
    team = models.ForeignKey(
        "participation.Team",
        related_name="participants",
        on_delete=models.PROTECT,
        blank=True,
        null=True,
        default=None,
        verbose_name=_("team"),
    )

    gender = models.CharField(
        max_length=6,
        verbose_name=_("gender"),
        choices=[
            ("female", _("Female")),
            ("male", _("Male")),
            ("other", _("Other")),
        ],
        default="other",
    )

    address = models.CharField(
        max_length=255,
        verbose_name=_("address"),
    )

    zip_code = models.PositiveIntegerField(
        verbose_name=_("zip code"),
        validators=[MinValueValidator(1000), MaxValueValidator(99999)],
    )

    city = models.CharField(
        max_length=255,
        verbose_name=_("city"),
    )

    phone_number = PhoneNumberField(
        verbose_name=_("phone number"),
        blank=True,
    )

    health_issues = models.TextField(
        verbose_name=_("health issues"),
        blank=True,
        help_text=_("You can indicate here your allergies or anything that is important to know for organizers"),
    )

    photo_authorization = models.FileField(
        verbose_name=_("photo authorization"),
        upload_to=get_random_photo_filename,
        blank=True,
        default="",
    )

    @property
    def under_18(self):
        if isinstance(self, CoachRegistration):
            return False  # In normal case
        important_date = timezone.now().date()
        if self.team and self.team.participation.tournament:
            important_date = self.team.participation.tournament.date_start
            if self.team.participation.final:
                from participation.models import Tournament
                important_date = Tournament.final_tournament().date_start
        return (important_date - self.birth_date).days < 18 * 365.24

    @property
    def type(self):  # pragma: no cover
        raise NotImplementedError

    @property
    def form_class(self):  # pragma: no cover
        raise NotImplementedError

    class Meta:
        verbose_name = _("participant registration")
        verbose_name_plural = _("participant registrations")


class StudentRegistration(ParticipantRegistration):
    """
    Specific registration for students.
    They have a team, a student class and a school.
    """
    birth_date = models.DateField(
        verbose_name=_("birth date"),
        default=date.today,
    )

    student_class = models.IntegerField(
        choices=[
            (12, _("12th grade")),
            (11, _("11th grade")),
            (10, _("10th grade or lower")),
        ],
        verbose_name=_("student class"),
    )

    school = models.CharField(
        max_length=255,
        verbose_name=_("school"),
    )

    responsible_name = models.CharField(
        max_length=255,
        verbose_name=_("responsible name"),
        default="",
    )

    responsible_phone = PhoneNumberField(
        verbose_name=_("responsible phone number"),
        default="",
    )

    responsible_email = models.EmailField(
        verbose_name=_("responsible email address"),
        default="",
    )

    parental_authorization = models.FileField(
        verbose_name=_("parental authorization"),
        upload_to=get_random_parental_filename,
        blank=True,
        default="",
    )

    health_sheet = models.FileField(
        verbose_name=_("health sheet"),
        upload_to=get_random_health_filename,
        blank=True,
        default="",
    )

    vaccine_sheet = models.FileField(
        verbose_name=_("vaccine sheet"),
        upload_to=get_random_vaccine_filename,
        blank=True,
        default="",
    )

    @property
    def type(self):
        return _("student")

    @property
    def form_class(self):
        from registration.forms import StudentRegistrationForm
        return StudentRegistrationForm

    class Meta:
        verbose_name = _("student registration")
        verbose_name_plural = _("student registrations")


class CoachRegistration(ParticipantRegistration):
    """
    Specific registration for coaches.
    They have a team and a professional activity.
    """
    professional_activity = models.TextField(
        verbose_name=_("professional activity"),
    )

    @property
    def type(self):
        return _("coach")

    @property
    def form_class(self):
        from registration.forms import CoachRegistrationForm
        return CoachRegistrationForm

    class Meta:
        verbose_name = _("coach registration")
        verbose_name_plural = _("coach registrations")


class VolunteerRegistration(Registration):
    """
    Specific registration for organizers and juries.
    """
    professional_activity = models.TextField(
        verbose_name=_("professional activity"),
    )

    admin = models.BooleanField(
        verbose_name=_("administrator"),
        help_text=_("An administrator has all rights. Please don't give this right to all juries and volunteers."),
        default=False,
    )

    @property
    def interesting_tournaments(self) -> set:
        return set(self.organized_tournaments.all()).union(map(lambda pool: pool.tournament, self.jury_in.all()))

    @property
    def type(self):
        return _('admin') if self.is_admin else _('volunteer')

    @property
    def form_class(self):
        from registration.forms import VolunteerRegistrationForm
        return VolunteerRegistrationForm

    class Meta:
        verbose_name = _("volunteer registration")
        verbose_name_plural = _("volunteer registrations")


def get_scholarship_filename(instance, filename):
    return f"authorization/scholarship/scholarship_{instance.registration.pk}"


class Payment(models.Model):
    registration = models.OneToOneField(
        ParticipantRegistration,
        on_delete=models.CASCADE,
        related_name="payment",
        verbose_name=_("registration"),
    )

    type = models.CharField(
        verbose_name=_("type"),
        max_length=16,
        choices=[
            ('', _("No payment")),
            ('helloasso', "Hello Asso"),
            ('scholarship', _("Scholarship")),
            ('bank_transfer', _("Bank transfer")),
            ('other', _("Other (please indicate)")),
            ('free', _("The tournament is free")),
        ],
        blank=True,
        default="",
    )

    scholarship_file = models.FileField(
        verbose_name=_("scholarship file"),
        help_text=_("only if you have a scholarship."),
        upload_to=get_scholarship_filename,
        blank=True,
        default="",
    )

    additional_information = models.TextField(
        verbose_name=_("additional information"),
        help_text=_("To help us to find your payment."),
        blank=True,
        default="",
    )

    valid = models.BooleanField(
        verbose_name=_("payment valid"),
        null=True,
        default=False,
    )

    def get_absolute_url(self):
        return reverse_lazy("registration:user_detail", args=(self.registration.user.id,))

    def __str__(self):
        return _("Payment of {registration}").format(registration=self.registration)

    class Meta:
        verbose_name = _("payment")
        verbose_name_plural = _("payments")
