from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponseRedirect
from django.views.generic.base import ContextMixin, TemplateResponseMixin, View


class StaffRequiredMixin(PermissionRequiredMixin):
    permission_required = "bds.is_team"


class MultipleFormMixin(ContextMixin):
    """Mixin pour gérer plusieurs formulaires dans la même vue.
    Le fonctionnement est relativement identique à celui de
    FormMixin, dont la documentation est disponible ici :
    https://docs.djangoproject.com/en/3.0/ref/class-based-views/mixins-editing/

    Les principales différences sont :
    - au lieu de form_class, il faut donner comme attribut un dict de la forme
    {<form_name>: <form_class>}, avec tous les formulaires à instancier. On
    peut aussi redéfinir `get_form_classes`

    - les données initiales se récupèrent pour chaque form via l'attribut
    `<form_name>_initial` ou la fonction `get_<form_name>_initial`. De même,
    si certaines forms sont des `ModelForm`s, on peut définir la fonction
    `get_<form_name>_instance`.

    - chaque form a un préfixe rajouté, par défaut <form_name>, mais qui peut
    être customisé via `prefixes` ou `get_prefixes`.
    """

    form_classes = {}
    prefixes = {}
    initial = {}

    success_url = None

    def get_form_classes(self):
        return self.form_classes

    def get_initial(self, form_name):
        initial_attr = "%s_initial" % form_name

        initial_method = "get_%s_initial" % form_name
        initial_method = getattr(self, initial_method, None)

        if hasattr(self, initial_attr):
            return getattr(self, initial_attr)
        elif callable(initial_method):
            return initial_method()
        else:
            return self.initial.copy()

    def get_prefix(self, form_name):
        return self.prefixes.get(form_name, form_name)

    def get_instance(self, form_name):
        # Au cas où certaines des forms soient des ModelForms
        instance_method = "get_%s_instance" % form_name
        instance_method = getattr(self, instance_method, None)

        if callable(instance_method):
            return instance_method()
        else:
            return None

    def get_form_kwargs(self, form_name):
        kwargs = {
            "initial": self.get_initial(form_name),
            "prefix": self.get_prefix(form_name),
            "instance": self.get_instance(form_name),
        }

        if self.request.method in ("POST", "PUT"):
            kwargs.update({"data": self.request.POST, "files": self.request.FILES})

        return kwargs

    def get_forms(self):
        form_classes = self.get_form_classes()
        return {
            form_name: form_class(**self.get_form_kwargs(form_name))
            for form_name, form_class in form_classes.items()
        }

    def get_success_url(self):
        if not self.success_url:
            raise ImproperlyConfigured("No URL to redirect to. Provide a success_url.")
        return str(self.success_url)

    def form_valid(self, forms):
        # on garde le nom form_valid pour l'interface avec SuccessMessageMixin
        return HttpResponseRedirect(self.get_success_url())

    def form_invalid(self, forms):
        """If the form is invalid, render the invalid form."""
        return self.render_to_response(self.get_context_data(forms=forms))


class ProcessMultipleFormView(View):
    """Équivalent de `ProcessFormView` pour plusieurs forms.
    Note : il faut que *tous* les formulaires soient valides pour
    qu'ils soient sauvegardés !
    """

    def get(self, request, *args, **kwargs):
        forms = self.get_forms()
        return self.render_to_response(self.get_context_data(forms=forms))

    def post(self, request, *args, **kwargs):
        forms = self.get_forms()
        if all(form.is_valid() for form in forms.values()):
            return self.form_valid(forms)
        else:
            return self.form_invalid(forms)


class BaseMultipleFormView(MultipleFormMixin, ProcessMultipleFormView):
    pass


class MultipleFormView(TemplateResponseMixin, BaseMultipleFormView):
    pass
