from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import AuthenticationForm
from django.forms.formsets import BaseFormSet, formset_factory
from django.forms.widgets import CheckboxSelectMultiple, RadioSelect
from django.utils.translation import gettext_lazy as _
from djconfig.forms import ConfigForm

from bda.models import Spectacle
from gestioncof.models import CalendarSubscription, Club, CofProfile, EventCommentValue
from gestioncof.widgets import TriStateCheckbox

User = get_user_model()


class ExteAuthenticationForm(AuthenticationForm):
    """
    Formulaire pour l'authentification des extés : renvoie une erreur si la personne
    qui essaie de s'authentifier n'a pas de mot de passe. L'erreur dépend de si la
    personne a un login clipper ou non.
    """

    def clean(self):
        username = self.cleaned_data.get("username")

        if username is not None:
            try:
                user = User.objects.get(username=username)
                if not user.has_usable_password() or user.password in ("", "!"):
                    profile, created = CofProfile.objects.get_or_create(user=user)
                    if profile.login_clipper:
                        raise forms.ValidationError(
                            _("L'utilisateur·ice a un login clipper !"),
                            code="has_clipper",
                        )
                    else:
                        raise forms.ValidationError(
                            _("L'utilisateur·ice n'a pas de mot de passe"),
                            code="no_password",
                        )
            except User.DoesNotExist:
                pass

        return super().clean()


class EventForm(forms.Form):
    def __init__(self, *args, **kwargs):
        event = kwargs.pop("event")
        self.event = event
        current_choices = kwargs.pop("current_choices", None)
        super().__init__(*args, **kwargs)
        choices = {}
        if current_choices:
            for choice in current_choices.all():
                if choice.event_option.id not in choices:
                    choices[choice.event_option.id] = [choice.id]
                else:
                    choices[choice.event_option.id].append(choice.id)
        all_choices = choices
        for option in event.options.all():
            choices = [(choice.id, choice.value) for choice in option.choices.all()]
            if option.multi_choices:
                initial = [] if option.id not in all_choices else all_choices[option.id]
                field = forms.MultipleChoiceField(
                    label=option.name,
                    choices=choices,
                    widget=CheckboxSelectMultiple,
                    required=False,
                    initial=initial,
                )
            else:
                initial = (
                    None if option.id not in all_choices else all_choices[option.id][0]
                )
                field = forms.ChoiceField(
                    label=option.name,
                    choices=choices,
                    widget=RadioSelect,
                    required=False,
                    initial=initial,
                )
            field.option_id = option.id
            self.fields["option_%d" % option.id] = field

    def choices(self):
        for name, value in self.cleaned_data.items():
            if name.startswith("option_"):
                yield (self.fields[name].option_id, value)


class SurveyForm(forms.Form):
    def __init__(self, *args, **kwargs):
        survey = kwargs.pop("survey")
        current_answers = kwargs.pop("current_answers", None)
        super().__init__(*args, **kwargs)
        answers = {}
        if current_answers:
            for answer in current_answers.all():
                if answer.survey_question.id not in answers:
                    answers[answer.survey_question.id] = [answer.id]
                else:
                    answers[answer.survey_question.id].append(answer.id)
        for question in survey.questions.all():
            choices = [(answer.id, answer.answer) for answer in question.answers.all()]
            if question.multi_answers:
                initial = [] if question.id not in answers else answers[question.id]
                field = forms.MultipleChoiceField(
                    label=question.question,
                    choices=choices,
                    widget=CheckboxSelectMultiple,
                    required=False,
                    initial=initial,
                )
            else:
                initial = (
                    None if question.id not in answers else answers[question.id][0]
                )
                field = forms.ChoiceField(
                    label=question.question,
                    choices=choices,
                    widget=RadioSelect,
                    required=False,
                    initial=initial,
                )
            field.question_id = question.id
            self.fields["question_%d" % question.id] = field

    def answers(self):
        for name, value in self.cleaned_data.items():
            if name.startswith("question_"):
                yield (self.fields[name].question_id, value)


class SurveyStatusFilterForm(forms.Form):
    def __init__(self, *args, **kwargs):
        survey = kwargs.pop("survey")
        super().__init__(*args, **kwargs)
        for question in survey.questions.all():
            for answer in question.answers.all():
                name = "question_%d_answer_%d" % (question.id, answer.id)
                if self.is_bound and self.data.get(self.add_prefix(name), None):
                    initial = self.data.get(self.add_prefix(name), None)
                else:
                    initial = "none"
                field = forms.ChoiceField(
                    label="%s : %s" % (question.question, answer.answer),
                    choices=[("yes", "yes"), ("no", "no"), ("none", "none")],
                    widget=TriStateCheckbox,
                    required=False,
                    initial=initial,
                )
                field.question_id = question.id
                field.answer_id = answer.id
                self.fields[name] = field

    def filters(self):
        for name, value in self.cleaned_data.items():
            if name.startswith("question_"):
                yield (
                    self.fields[name].question_id,
                    self.fields[name].answer_id,
                    value,
                )


class EventStatusFilterForm(forms.Form):
    def __init__(self, *args, **kwargs):
        event = kwargs.pop("event")
        super().__init__(*args, **kwargs)
        for option in event.options.all():
            for choice in option.choices.all():
                name = "option_%d_choice_%d" % (option.id, choice.id)
                if self.is_bound and self.data.get(self.add_prefix(name), None):
                    initial = self.data.get(self.add_prefix(name), None)
                else:
                    initial = "none"
                field = forms.ChoiceField(
                    label="%s : %s" % (option.name, choice.value),
                    choices=[("yes", "yes"), ("no", "no"), ("none", "none")],
                    widget=TriStateCheckbox,
                    required=False,
                    initial=initial,
                )
                field.option_id = option.id
                field.choice_id = choice.id
                self.fields[name] = field
        # has_paid
        name = "event_has_paid"
        if self.is_bound and self.data.get(self.add_prefix(name), None):
            initial = self.data.get(self.add_prefix(name), None)
        else:
            initial = "none"
        field = forms.ChoiceField(
            label="Événement payé",
            choices=[("yes", "yes"), ("no", "no"), ("none", "none")],
            widget=TriStateCheckbox,
            required=False,
            initial=initial,
        )
        self.fields[name] = field

    def filters(self):
        for name, value in self.cleaned_data.items():
            if name.startswith("option_"):
                yield (self.fields[name].option_id, self.fields[name].choice_id, value)
            elif name == "event_has_paid":
                yield ("has_paid", None, value)


class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ["first_name", "last_name", "email"]


class PhoneForm(forms.ModelForm):
    class Meta:
        model = CofProfile
        fields = ["phone"]


class ProfileForm(forms.ModelForm):
    class Meta:
        model = CofProfile
        fields = [
            "phone",
            "mailing_cof",
            "mailing_bda",
            "mailing_bda_revente",
            "mailing_unernestaparis",
        ]


class RegistrationUserForm(forms.ModelForm):
    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)
        self.fields["username"].help_text = ""

    class Meta:
        model = User
        fields = ("username", "first_name", "last_name", "email")


class RegistrationPassUserForm(RegistrationUserForm):
    """
    Formulaire pour changer le mot de passe d'un utilisateur.
    """

    password1 = forms.CharField(label=_("Mot de passe"), widget=forms.PasswordInput)
    password2 = forms.CharField(
        label=_("Confirmation du mot de passe"), widget=forms.PasswordInput
    )

    def clean_password2(self):
        pass1 = self.cleaned_data["password1"]
        pass2 = self.cleaned_data["password2"]
        if pass1 and pass2:
            if pass1 != pass2:
                raise forms.ValidationError(_("Mots de passe non identiques."))
        return pass2

    def save(self, commit=True, *args, **kwargs):
        user = super().save(commit, *args, **kwargs)
        user.set_password(self.cleaned_data["password2"])
        if commit:
            user.save()
        return user


class RegistrationProfileForm(forms.ModelForm):
    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)
        self.fields["mailing_cof"].initial = True
        self.fields["mailing_bda"].initial = True
        self.fields["mailing_bda_revente"].initial = True
        self.fields["mailing_unernestaparis"].initial = True

    class Meta:
        model = CofProfile
        fields = [
            "login_clipper",
            "phone",
            "occupation",
            "departement",
            "is_cof",
            "type_cotiz",
            "mailing_cof",
            "mailing_bda",
            "mailing_bda_revente",
            "mailing_unernestaparis",
            "comments",
        ]


STATUS_CHOICES = (
    ("no", "Non"),
    ("wait", "Oui mais attente paiement"),
    ("paid", "Oui payé"),
)


class AdminEventForm(forms.Form):
    status = forms.ChoiceField(
        label="Inscription", initial="no", choices=STATUS_CHOICES, widget=RadioSelect
    )

    def __init__(self, *args, **kwargs):
        self.event = kwargs.pop("event")
        registration = kwargs.pop("current_registration", None)
        current_choices, paid = (
            (registration.options.all(), registration.paid)
            if registration is not None
            else ([], None)
        )
        if paid is True:
            kwargs["initial"] = {"status": "paid"}
        elif paid is False:
            kwargs["initial"] = {"status": "wait"}
        else:
            kwargs["initial"] = {"status": "no"}
        super().__init__(*args, **kwargs)
        choices = {}
        for choice in current_choices:
            if choice.event_option.id not in choices:
                choices[choice.event_option.id] = [choice.id]
            else:
                choices[choice.event_option.id].append(choice.id)
        all_choices = choices
        for option in self.event.options.all():
            choices = [(choice.id, choice.value) for choice in option.choices.all()]
            if option.multi_choices:
                initial = [] if option.id not in all_choices else all_choices[option.id]
                field = forms.MultipleChoiceField(
                    label=option.name,
                    choices=choices,
                    widget=CheckboxSelectMultiple,
                    required=False,
                    initial=initial,
                )
            else:
                initial = (
                    None if option.id not in all_choices else all_choices[option.id][0]
                )
                field = forms.ChoiceField(
                    label=option.name,
                    choices=choices,
                    widget=RadioSelect,
                    required=False,
                    initial=initial,
                )
            field.option_id = option.id
            self.fields["option_%d" % option.id] = field
        for commentfield in self.event.commentfields.all():
            initial = commentfield.default
            if registration is not None:
                try:
                    initial = registration.comments.get(
                        commentfield=commentfield
                    ).content
                except EventCommentValue.DoesNotExist:
                    pass
            widget = (
                forms.Textarea if commentfield.fieldtype == "text" else forms.TextInput
            )
            field = forms.CharField(
                label=commentfield.name, widget=widget, required=False, initial=initial
            )
            field.comment_id = commentfield.id
            self.fields["comment_%d" % commentfield.id] = field

    def choices(self):
        for name, value in self.cleaned_data.items():
            if name.startswith("option_"):
                yield (self.fields[name].option_id, value)

    def comments(self):
        for name, value in self.cleaned_data.items():
            if name.startswith("comment_"):
                yield (self.fields[name].comment_id, value)


class BaseEventRegistrationFormset(BaseFormSet):
    def __init__(self, *args, **kwargs):
        self.events = kwargs.pop("events")
        self.current_registrations = kwargs.pop("current_registrations", None)
        self.extra = len(self.events)
        super().__init__(*args, **kwargs)

    def _construct_form(self, index, **kwargs):
        kwargs["event"] = self.events[index]
        if self.current_registrations is not None:
            kwargs["current_registration"] = self.current_registrations[index]
        return super()._construct_form(index, **kwargs)


EventFormset = formset_factory(AdminEventForm, BaseEventRegistrationFormset)


class CalendarForm(forms.ModelForm):
    subscribe_to_events = forms.BooleanField(
        initial=True, label="Événements du COF", required=False
    )
    subscribe_to_my_shows = forms.BooleanField(
        initial=True,
        label="Les spectacles pour lesquels j'ai obtenu une place",
        required=False,
    )
    other_shows = forms.ModelMultipleChoiceField(
        label="Spectacles supplémentaires",
        queryset=Spectacle.objects.filter(tirage__active=True),
        widget=forms.CheckboxSelectMultiple,
        required=False,
    )

    class Meta:
        model = CalendarSubscription
        fields = ["subscribe_to_events", "subscribe_to_my_shows", "other_shows"]


class ClubsForm(forms.Form):
    """
    Formulaire d'inscription d'un membre à plusieurs clubs du COF.
    """

    clubs = forms.ModelMultipleChoiceField(
        label="Inscriptions aux clubs du COF",
        queryset=Club.objects.all(),
        widget=forms.CheckboxSelectMultiple,
        required=False,
    )


# ---
# Announcements banner
# TODO: move this to the `gestion` app once the supportBDS branch is merged
# ---


class GestioncofConfigForm(ConfigForm):
    gestion_banner = forms.CharField(
        label=_("Announcements banner"),
        help_text=_("An empty banner disables annoucements"),
        max_length=2048,
        required=False,
    )