from datetime import datetime

from django import forms
from django.core.exceptions import ValidationError
from django.db.models import Count, Q
from django.utils.translation import gettext_lazy as _

from django_global_request.middleware import get_request
from django_select2.forms import Select2Widget
from guardian.shortcuts import get_objects_for_user
from material import Fieldset, Layout, Row

from aleksis.apps.chronos.managers import TimetableType
from aleksis.apps.chronos.models import TimePeriod
from aleksis.core.models import Group, Person
from aleksis.core.util.predicates import check_global_permission

from .models import ExcuseType, ExtraMark, LessonDocumentation, PersonalNote


class LessonDocumentationForm(forms.ModelForm):
    class Meta:
        model = LessonDocumentation
        fields = ["topic", "homework", "group_note"]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.fields["homework"].label = _("Homework for the next lesson")


class PersonalNoteForm(forms.ModelForm):
    class Meta:
        model = PersonalNote
        fields = ["absent", "late", "excused", "excuse_type", "extra_marks", "remarks"]

    person_name = forms.CharField(disabled=True)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["person_name"].widget.attrs.update(
            {"class": "alsijil-lesson-personal-note-name"}
        )
        self.fields["person_name"].widget = forms.HiddenInput()

        if self.instance and getattr(self.instance, "person", None):
            self.fields["person_name"].initial = str(self.instance.person)


class SelectForm(forms.Form):
    layout = Layout(Row("group", "teacher"))

    group = forms.ModelChoiceField(
        queryset=None, label=_("Group"), required=False, widget=Select2Widget,
    )
    teacher = forms.ModelChoiceField(
        queryset=None, label=_("Teacher"), required=False, widget=Select2Widget,
    )

    def clean(self) -> dict:
        data = super().clean()

        if data.get("group") and not data.get("teacher"):
            type_ = TimetableType.GROUP
            instance = data["group"]
        elif data.get("teacher") and not data.get("group"):
            type_ = TimetableType.TEACHER
            instance = data["teacher"]
        elif not data.get("teacher") and not data.get("group"):
            return data
        else:
            raise ValidationError(_("You can't select a group and a teacher both."))

        data["type_"] = type_
        data["instance"] = instance
        return data

    def __init__(self, *args, **kwargs):
        self.request = get_request()
        super().__init__(*args, **kwargs)

        person = self.request.user.person

        group_qs = Group.get_groups_with_lessons()

        # Filter selectable groups by permissions
        if not check_global_permission(self.request.user, "alsijil.view_week"):
            # 1) All groups the user is allowed to see the week view by object permissions
            # 2) All groups the user is a member of an owner of
            group_qs = (
                group_qs.filter(
                    pk__in=get_objects_for_user(
                        self.request.user, "core.view_week_class_register_group", Group
                    ).values_list("pk", flat=True)
                )
            ).union(group_qs.filter(Q(members=person) | Q(owners=person)))

        # Flatten query by filtering groups by pk
        self.fields["group"].queryset = Group.objects.filter(
            pk__in=list(group_qs.values_list("pk", flat=True))
        )

        teacher_qs = Person.objects.annotate(lessons_count=Count("lessons_as_teacher")).filter(
            lessons_count__gt=0
        )

        # Filter selectable teachers by permissions
        if not check_global_permission(self.request.user, "alsijil.view_week"):
            # If the user hasn't the global permission,
            # the user is only allowed to see his own person
            teacher_qs = teacher_qs.filter(pk=person.pk)

        self.fields["teacher"].queryset = teacher_qs


PersonalNoteFormSet = forms.modelformset_factory(
    PersonalNote, form=PersonalNoteForm, max_num=0, extra=0
)


class RegisterAbsenceForm(forms.Form):
    layout = Layout(
        Fieldset("", Row("date_start", "date_end"), Row("from_period", "to_period")),
        Fieldset("", Row("absent", "excused"), Row("excuse_type"), Row("remarks")),
    )
    date_start = forms.DateField(label=_("Start date"), initial=datetime.today)
    date_end = forms.DateField(label=_("End date"), initial=datetime.today)
    from_period = forms.ChoiceField(label=_("Start period"))
    to_period = forms.ChoiceField(label=_("End period"))
    absent = forms.BooleanField(label=_("Absent"), initial=True, required=False)
    excused = forms.BooleanField(label=_("Excused"), initial=True, required=False)
    excuse_type = forms.ModelChoiceField(
        label=_("Excuse type"),
        queryset=ExcuseType.objects.all(),
        widget=Select2Widget,
        required=False,
    )
    remarks = forms.CharField(label=_("Remarks"), max_length=30, required=False)

    def __init__(self, *args, **kwargs):
        self.request = get_request()
        super().__init__(*args, **kwargs)
        period_choices = TimePeriod.period_choices

        self.fields["from_period"].choices = period_choices
        self.fields["to_period"].choices = period_choices
        self.fields["from_period"].initial = TimePeriod.period_min
        self.fields["to_period"].initial = TimePeriod.period_max


class ExtraMarkForm(forms.ModelForm):
    layout = Layout("short_name", "name")

    class Meta:
        model = ExtraMark
        fields = ["short_name", "name"]


class ExcuseTypeForm(forms.ModelForm):
    layout = Layout("short_name", "name")

    class Meta:
        model = ExcuseType
        fields = ["short_name", "name"]