import os
import re

from django import forms
from django.db.models import Q
from django.template.defaultfilters import filesizeformat
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from member.models import TFJMUser, Solution, Synthesis
from tfjm.inputs import DatePickerInput, DateTimePickerInput, AmountInput
from tournament.models import Tournament, Team, Pool


class TournamentForm(forms.ModelForm):
    """
    Create and update tournaments.
    """

    # Only organizers can organize tournaments. Well, that's pretty normal...
    organizers = forms.ModelMultipleChoiceField(
        TFJMUser.objects.filter(Q(role="0admin") | Q(role="1volunteer")).order_by('role'),
        label=_("Organizers"),
    )

    def clean(self):
        cleaned_data = super().clean()

        if not self.instance.pk:
            if Tournament.objects.filter(name=cleaned_data["data"], year=os.getenv("TFJM_YEAR")):
                self.add_error("name", _("This tournament already exists."))
            if cleaned_data["final"] and Tournament.objects.filter(final=True, year=os.getenv("TFJM_YEAR")):
                self.add_error("name", _("The final tournament was already defined."))

        return cleaned_data

    class Meta:
        model = Tournament
        exclude = ('year',)
        widgets = {
            "price": AmountInput(),
            "date_start": DatePickerInput(),
            "date_end": DatePickerInput(),
            "date_inscription": DateTimePickerInput(),
            "date_solutions": DateTimePickerInput(),
            "date_syntheses": DateTimePickerInput(),
            "date_solutions_2": DateTimePickerInput(),
            "date_syntheses_2": DateTimePickerInput(),
        }


class OrganizerForm(forms.ModelForm):
    """
    Register an organizer in the website.
    """

    class Meta:
        model = TFJMUser
        fields = ('last_name', 'first_name', 'email', 'is_superuser',)

    def clean(self):
        cleaned_data = super().clean()

        if TFJMUser.objects.filter(email=cleaned_data["email"], year=os.getenv("TFJM_YEAR")).exists():
            self.add_error("email", _("This organizer already exist."))

        return cleaned_data

    def save(self, commit=True):
        user = self.instance
        user.role = '0admin' if user.is_superuser else '1volunteer'
        user.save()
        super().save(commit)


class TeamForm(forms.ModelForm):
    """
    Add and update a team.
    """
    tournament = forms.ModelChoiceField(
        Tournament.objects.filter(date_inscription__gte=timezone.now(), final=False),
    )

    class Meta:
        model = Team
        fields = ('name', 'trigram', 'tournament',)

    def clean(self):
        cleaned_data = super().clean()

        cleaned_data["trigram"] = cleaned_data["trigram"].upper()

        if not re.match("[A-Z]{3}", cleaned_data["trigram"]):
            self.add_error("trigram", _("The trigram must be composed of three upcase letters."))

        if not self.instance.pk:
            if Team.objects.filter(trigram=cleaned_data["trigram"], year=os.getenv("TFJM_YEAR")).exists():
                self.add_error("trigram", _("This trigram is already used."))

            if Team.objects.filter(name=cleaned_data["name"], year=os.getenv("TFJM_YEAR")).exists():
                self.add_error("name", _("This name is already used."))

            if cleaned_data["tournament"].date_inscription < timezone.now:
                self.add_error("tournament", _("This tournament is already closed."))

        return cleaned_data


class JoinTeam(forms.Form):
    """
    Form to join a team with an access code.
    """

    access_code = forms.CharField(
        label=_("Access code"),
        max_length=6,
    )

    def clean(self):
        cleaned_data = super().clean()

        if not re.match("[a-z0-9]{6}", cleaned_data["access_code"]):
            self.add_error('access_code', _("The access code must be composed of 6 alphanumeric characters."))

        team = Team.objects.filter(access_code=cleaned_data["access_code"])
        if not team.exists():
            self.add_error('access_code', _("This access code is invalid."))
        team = team.get()
        if not team.invalid:
            self.add_error('access_code', _("The team is already validated."))
        cleaned_data["team"] = team

        return cleaned_data


class SolutionForm(forms.ModelForm):
    """
    Form to upload a solution.
    """

    problem = forms.ChoiceField(
        label=_("Problem"),
        choices=[(str(i), _("Problem #%(problem)d") % {"problem": i}) for i in range(1, 9)],
    )

    def clean_file(self):
        content = self.cleaned_data['file']
        content_type = content.content_type
        if content_type in ["application/pdf"]:
            if content.size > 5 * 2 ** 20:
                raise forms.ValidationError(
                    _('Please keep filesize under %(max_size)s. Current filesize %(current_size)s') % {
                        "max_size": filesizeformat(2 * 2 ** 20),
                        "current_size": filesizeformat(content.size)
                    })
        else:
            raise forms.ValidationError(_('The file should be a PDF file.'))
        return content

    class Meta:
        model = Solution
        fields = ('file', 'problem',)


class SynthesisForm(forms.ModelForm):
    """
    Form to upload a synthesis.
    """

    def clean_file(self):
        content = self.cleaned_data['file']
        content_type = content.content_type
        if content_type in ["application/pdf"]:
            if content.size > 5 * 2 ** 20:
                raise forms.ValidationError(
                    _('Please keep filesize under %(max_size)s. Current filesize %(current_size)s') % {
                        "max_size": filesizeformat(2 * 2 ** 20),
                        "current_size": filesizeformat(content.size)
                    })
        else:
            raise forms.ValidationError(_('The file should be a PDF file.'))
        return content

    class Meta:
        model = Synthesis
        fields = ('file', 'source', 'round',)


class PoolForm(forms.ModelForm):
    """
    Form to add a pool.
    Should not be used: prefer to pass by API and auto-add pools with the results of the draw.
    """

    team1 = forms.ModelChoiceField(
        Team.objects.filter(validation_status="2valid").all(),
        empty_label=_("Choose a team..."),
        label=_("Team 1"),
    )

    problem1 = forms.IntegerField(
        min_value=1,
        max_value=8,
        initial=1,
        label=_("Problem defended by team 1"),
    )

    team2 = forms.ModelChoiceField(
        Team.objects.filter(validation_status="2valid").all(),
        empty_label=_("Choose a team..."),
        label=_("Team 2"),
    )

    problem2 = forms.IntegerField(
        min_value=1,
        max_value=8,
        initial=2,
        label=_("Problem defended by team 2"),
    )

    team3 = forms.ModelChoiceField(
        Team.objects.filter(validation_status="2valid").all(),
        empty_label=_("Choose a team..."),
        label=_("Team 3"),
    )

    problem3 = forms.IntegerField(
        min_value=1,
        max_value=8,
        initial=3,
        label=_("Problem defended by team 3"),
    )

    def clean(self):
        cleaned_data = super().clean()

        team1, pb1 = cleaned_data["team1"], cleaned_data["problem1"]
        team2, pb2 = cleaned_data["team2"], cleaned_data["problem2"]
        team3, pb3 = cleaned_data["team3"], cleaned_data["problem3"]

        sol1 = Solution.objects.get(team=team1, problem=pb1, final=team1.selected_for_final)
        sol2 = Solution.objects.get(team=team2, problem=pb2, final=team2.selected_for_final)
        sol3 = Solution.objects.get(team=team3, problem=pb3, final=team3.selected_for_final)

        cleaned_data["teams"] = [team1, team2, team3]
        cleaned_data["solutions"] = [sol1, sol2, sol3]

        return cleaned_data

    def save(self, commit=True):
        pool = super().save(commit)

        pool.refresh_from_db()
        pool.teams.set(self.cleaned_data["teams"])
        pool.solutions.set(self.cleaned_data["solutions"])
        pool.save()

        return pool

    class Meta:
        model = Pool
        fields = ('round', 'juries',)