from django import forms
from django.forms import fields
from django.utils import dateformat, formats
from django.utils.translation import gettext_lazy as _

from allauth.account.views import SignupForm
from django_select2.forms import ModelSelect2MultipleWidget, ModelSelect2Widget
from material import Fieldset, Layout, Row
from phonenumber_field.formfields import PhoneNumberField

from aleksis.apps.tezor.models.base import Client
from aleksis.apps.tezor.models.invoice import InvoiceGroup
from aleksis.core.mixins import ExtensibleForm
from aleksis.core.models import Group, Person

from .models import Event, EventRegistration, InfoMailing, RegistrationState, Terms, Voucher

COMMENT_CHOICES = [
    ("first", _("Only first name")),
    ("first_age", _("First name and age")),
    ("first_last_age", _("First name, last name and age")),
]

TEMPLATE_CHOICES = [
    ("list_sign", _("Signature list")),
    ("list_participants", _("Participants list")),
    ("corona", _("Corona attendance list")),
]

LICENCE_CHOICES = [
    ("CC-BY-4.0+", _("Creative Commons with attribution, 4.0 or later")),
    (
        "CC-BY-SA-4.0+",
        _(
            "Creative Commons with attribution and distribution only "
            "under the same conditions, 4.0 or later"
        ),
    ),
]


class EditEventForm(ExtensibleForm):
    """Form to create or edit an event."""

    layout = Layout(
        Fieldset(
            _("Base data"),
            "linked_group",
            Row("display_name", "slug", "description"),
            Row("place", "published"),
            Fieldset(_("Date data"), Row("date_event", "date_registration", "date_retraction")),
            Fieldset(_("Event details"), Row("cost", "max_participants"), "information"),
            Fieldset(_("Terms"), "terms"),
            Fieldset(_("Info mailings"), "info_mailings"),
        ),
    )

    class Meta:
        model = Event
        fields = [
            "linked_group",
            "display_name",
            "description",
            "slug",
            "place",
            "published",
            "date_event",
            "date_registration",
            "date_retraction",
            "cost",
            "max_participants",
            "terms",
            "information",
            "info_mailings",
        ]
        widgets = {
            "linked_group": ModelSelect2Widget(
                search_fields=["name__icontains"],
                attrs={"data-minimum-input-length": 0, "class": "browser-default"},
            ),
            "terms": ModelSelect2MultipleWidget(
                search_fields=["aspect__icontains"],
                attrs={"data-minimum-input-length": 0, "class": "browser-default"},
            ),
            "info_mailings": ModelSelect2MultipleWidget(
                search_fields=["subject__icontains"],
                attrs={"data-minimum-input-length": 0, "class": "browser-default"},
            ),
        }


class EditVoucherForm(forms.ModelForm):
    """Form to edit and create vouchers."""

    class Meta:
        model = Voucher
        exclude = ["code", "used_person_uid", "used", "deleted"]  # noqa
        widgets = {
            "event": ModelSelect2Widget(
                search_fields=["display_name__icontains"],
                attrs={"data-minimum-input-length": 0, "class": "browser-default"},
            ),
            "person": ModelSelect2Widget(
                search_fields=["first_name__icontains", "last_name__icontains"],
                attrs={"data-minimum-input-length": 0, "class": "browser-default"},
            ),
        }
        help_texts = {
            "event": _("Event the voucher is valid for"),
            "person": _("Person the voucher is valid for"),
            "discount": _("Voucher discount"),
        }


class GenerateListForm(forms.Form):
    """Form to create a list of participants of a group."""

    group = forms.ModelChoiceField(
        label=_("Group"),
        queryset=Group.objects.all(),
        help_text=_("Select group to generate list"),
    )

    template = forms.ChoiceField(
        label=_("Template"),
        choices=TEMPLATE_CHOICES,
        help_text=_("Select template to generate list"),
    )

    landscape = forms.BooleanField(
        label=_("Landscape"),
        help_text=_("Select if output should be in landscape"),
        required=False,
    )


class RegisterEventGuardians(ExtensibleForm):
    class Meta:
        model = EventRegistration
        fields = []

    layout = Layout(
        Fieldset(
            _("Guardians personal data"),
            Row("guardian_first_name", "guardian_last_name"),
        ),
        Fieldset(
            _("Guardians contact details"),
            Row("guardian_email", "guardian_mobile_number"),
        ),
    )

    guardian_first_name = forms.CharField(
        label=_("Guardian's first name"),
        help_text=_(
            "Please enter the first name of the legal guardian who will fill in the registration "
            "with you and who can be reached during the event in an emergency."
        ),
    )

    guardian_last_name = forms.CharField(
        label=_("Guardian's last name"),
        help_text=_(
            "Please enter the last name of the legal guardian who will fill in the registration "
            "with you and who can be reached during the event in an emergency."
        ),
    )

    guardian_mobile_number = PhoneNumberField(
        label=_("Guardian's mobile number"),
        help_text=_(
            "We need the mobile phone number for emergencies if we "
            "urgently need to reach your parents during the event."
        ),
    )

    guardian_email = forms.EmailField(
        label=_("Guardian's email address"),
    )


class RegisterEventContactDetails(ExtensibleForm):
    class Meta:
        model = Group
        fields = []

    layout = Layout(
        Fieldset(
            _("Personal data"),
            Row("first_name", "last_name"),
            Row("date_of_birth", "sex"),
        ),
        Fieldset(
            _("Address data"),
            Row("street", "housenumber"),
            Row("postal_code", "place"),
        ),
        Fieldset(
            _("Contact details"),
            Row("mobile_number", "email"),
        ),
        Fieldset(
            _("School details"),
            Row("school", "school_place", "school_class"),
        ),
    )

    first_name = forms.CharField(
        label=_("First name"),
        disabled=True,
    )

    last_name = forms.CharField(
        label=_("Last name"),
        disabled=True,
    )

    street = forms.CharField(
        label=_("Street"),
    )

    housenumber = forms.CharField(
        label=_("Housenumber"),
    )

    postal_code = forms.CharField(
        label=_("Postal code"),
    )

    place = forms.CharField(
        label=_("Place"),
    )

    mobile_number = PhoneNumberField(
        label=_("Mobile number"),
        required=False,
        help_text=_(
            "Your mobile number helps us to reach you in an emergency during the event, e.g. "
            "if you are alone with your group at a conference or similar. If you don't have a "
            "cell phone, you can leave the field blank."
        ),
    )

    date_of_birth = forms.DateField(
        label=_("Date of birth"),
    )

    sex = forms.ChoiceField(
        label=_("Sex"),
        help_text=_(
            "For various reasons, e.g. because we have to keep gender segregation during the night "
            "for legal reasons, we need to know if you are a boy or a girl."
        ),
        choices=Person.SEX_CHOICES,
        initial=None,
    )

    email = forms.EmailField(
        label=_("Email address"),
        help_text=_(
            "Please use your personal e-mail address here, which you will check "
            "personally. Important information will always be sent to your parents "
            "as well. Do not use an e-mail address owned by your parents here."
        ),
    )

    school = forms.CharField(
        label=_("School"),
        help_text=_("Please enter the name of your school."),
    )

    school_place = forms.CharField(
        label=_("School place"),
        help_text=_("Enter the place (city) where your school is located."),
    )

    school_class = forms.CharField(
        label=_("School class"),
        help_text=_("Please enter the class you are in (e.g. 8a)."),
    )


class RegisterEventAdditional(ExtensibleForm):

    layout = Layout(
        Fieldset(
            _("Medical information / intolerances"),
            Row("medical_information"),
        ),
        Fieldset(
            _("Other remarks"),
            Row("comment"),
        ),
    )

    class Meta:
        model = EventRegistration
        fields = ["medical_information", "comment"]
        help_texts = {
            "medical_information": _(
                "If there are any medically important things we need to "
                "consider, e.g. when making food or to make sure you take "
                "prescribed medication, please enter it here."
            ),
            "comment": _("You can write down any remarks you want to tell us here."),
        }

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

        self.__class__.layout_filled = getattr(self.__class__, "layout_filled", False)

        for field in event.additional_fields.all():
            field_instance = getattr(fields, field.field_type)(
                required=field.required,
                help_text=field.help_text,
            )
            self.fields[field.title] = field_instance
            if not self.layout_filled:
                node = Fieldset(f"{field.title}", f"{field.title}")
                self.add_node_to_layout(node)
        self.__class__.layout_filled = True


class RegisterEventFinancial(ExtensibleForm):
    """Form to register for an event."""

    layout = Layout(
        Fieldset(
            _("Financial data"),
            "payment_method",
            Row("voucher_code", "donation"),
        ),
    )

    voucher_code = forms.CharField(
        label=_("Voucher code"),
        help_text=_("If you have a voucher code, type it in here."),
        required=False,
    )

    payment_method = forms.ChoiceField(
        label=_("Payment method"),
        help_text=_(
            "Please choose a payment method. "
            "The actual payment will be made after the registration."
        ),
    )

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

        # FIXME Maybe do not hard-code this
        client, __ = Client.objects.get_or_create(name="Teckids e.V.")
        group, __ = InvoiceGroup.objects.get_or_create(
            name="Hack'n'Fun-Veranstaltungen",
            client=client,
            defaults={
                "template_name": "paweljong/invoice_pdf.html",
            },
        )

        self.fields["payment_method"].choices = group.get_variant_choices()

    class Meta:
        model = EventRegistration
        fields = ["voucher_code", "donation"]
        help_texts = {
            "donation": _(
                "Our association would like to offer all children and young "
                "people the opportunity to participate in our events. Sometimes, "
                "however, families cannot afford the full fee. We therefore have a "
                "budget from which we can promote participation after we have "
                "carefully examined the necessity and eligibility. We rely on "
                "donations for this budget. If you would like to donate a voluntary "
                "additional amount for this budget, please indicate this here."
            ),
        }


class RegisterEventConsent(ExtensibleForm):
    class Meta:
        model = EventRegistration
        fields = []

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

        for field in event.terms.all():
            field_instance = forms.BooleanField(
                required=True,
                label=field.confirmation_text,
            )
            self.fields[f"consent_{field.pk}"] = field_instance
            node = f"consent_{field.pk}"
            self.add_node_to_layout(node)

        if event.date_retraction:
            field_instance = forms.BooleanField(
                required=True,
                label=_(
                    "I confirm that the retraction of the registration is not possible anymore "
                    "after {}"
                ).format(
                    dateformat.format(event.date_retraction, formats.get_format("DATE_FORMAT"))
                ),
            )
            self.fields["retraction_deadline"] = field_instance
            node = "retraction_deadline"
            self.add_node_to_layout(node)


class EditEventRegistrationForm(forms.ModelForm):

    layout = Layout(
        Row("event", "person"),
        Row("comment", "medical_information"),
        "voucher",
        Row("donation"),
        "accepted_terms",
        Row("school", "school_class", "school_place"),
        "states",
    )

    class Meta:
        model = EventRegistration
        exclude = []  # noqa


class EditTermForm(forms.ModelForm):
    class Meta:
        model = Terms
        exclude = []  # noqa


class RegisterEventAccount(SignupForm, ExtensibleForm):
    """Form to register new user accounts."""

    class Meta:
        model = EventRegistration
        fields = []

    layout = Layout(
        Fieldset(
            _("Base data"),
            Row("first_name", "last_name", "date_of_birth"),
        ),
        Fieldset(
            _("Account data"),
            "username",
            Row("email", "email2"),
            Row("password1", "password2"),
        ),
    )

    first_name = forms.CharField(label=_("First name"))
    last_name = forms.CharField(label=_("Last name"))
    date_of_birth = forms.DateField(label=_("Date of birth"))

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

        self.fields["username"].help_text = _(
            "The username must only contain lower case letters and numbers, "
            "and must begin with a letter."
        )


class EditInfoMailingForm(forms.ModelForm):

    layout = Layout(
        Row("sender", "reply_to", "active"),
        Row("send_to_person", "send_to_guardians", "send_to_retracted", "send_to_not_checked_in"),
        Row("subject"),
        Row("text"),
    )

    class Meta:
        model = InfoMailing
        exclude = ["sent_to"]  # noqa


class RegistrationNotificationForm(forms.ModelForm):

    layout = Layout(
        Row("sender", "reply_to"),
        Row("send_to_person", "send_to_guardians"),
        Row("subject"),
        Row("text"),
    )

    class Meta:
        model = InfoMailing
        exclude = ["sent_to", "active"]  # noqa


class RegistrationStatesForm(forms.ModelForm):
    class Meta:
        model = RegistrationState
        exclude = []  # noqa


class PersonGroupFormPerson(forms.Form):
    class Media:
        js = ("https://unpkg.com/html5-qrcode", "js/paweljong/qrscanner.js")

    layout = Layout("username")

    username = forms.CharField(
        required=True,
        label=_("Person"),
        widget=forms.TextInput(attrs={"autofocus": "", "autocomplete": "off"}),
        help_text=_("Please enter a username."),
    )


class EventCheckpointForm(forms.Form):
    class Media:
        js = (
            "https://unpkg.com/html5-qrcode",
            "js/paweljong/qrscanner.js",
            "js/paweljong/checkpoint.js",
        )

    layout = Layout("comment", "use_latlon", "username")

    comment = forms.CharField(
        required=True,
        label=_("Comment"),
        help_text=_("Please enter a comment describing the checkpoint (e.g. Dinner)."),
    )

    username = forms.CharField(
        required=True,
        label=_("Person"),
        help_text=_("Please enter a username."),
        widget=forms.TextInput(attrs={"autofocus": "", "autocomplete": "off"}),
    )

    use_latlon = forms.BooleanField(
        required=False,
        label=_("Submit geolocation"),
        initial=True,
    )

    lat = forms.DecimalField(
        required=False,
        min_value=-90.0,
        max_value=90.0,
        max_digits=10,
        decimal_places=8,
        widget=forms.HiddenInput(),
    )
    lon = forms.DecimalField(
        required=False,
        min_value=-180.0,
        max_value=180.0,
        max_digits=11,
        decimal_places=8,
        widget=forms.HiddenInput(),
    )