import os
import subprocess
from secrets import token_hex

from django import forms
from django.contrib.auth import forms as auth_forms
from django.db import transaction

from .models import Chant, ChantDoc, Membre, User


class BootstrapMixin:
    required_css_class = "required"

    widget_classes = {
        "checkbox": "form-check-input",
        "select": "form-select",
    }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            if getattr(field.widget, "_bs_enabled", False):
                continue
            widgets = getattr(field.widget, "widgets", [field.widget])
            for widget in widgets:
                input_type = getattr(widget, "input_type", "")
                class_name = self.widget_classes.get(input_type, "form-control")
                if "class" in widget.attrs:
                    widget.attrs["class"] += " " + class_name
                else:
                    widget.attrs.update({"class": class_name})


class LoginForm(BootstrapMixin, auth_forms.AuthenticationForm):
    username = forms.EmailField(
        widget=forms.EmailInput(attrs={"autofocus": True}),
    )


class MembreEditForm(BootstrapMixin, forms.ModelForm):
    class Meta:
        model = Membre
        fields = [
            "nom", "prenom", "fonction", "rue", "npa", "localite",
            "tel1", "tel2", "courriel", "date_naissance", "annee_entree",
        ]

    @transaction.atomic()
    def save(self, **kwargs):
        is_new = self.instance.pk is None
        if is_new:
            self.instance.user = User.objects.create_user("foo", self.instance.courriel, password=token_hex(10))
        elif "courriel" in self.changed_data:
            self.instance.user.email = self.cleaned_data["courriel"]
            self.instance.user.save()
        return super().save(**kwargs)


class ChantDocForm(BootstrapMixin, forms.ModelForm):
    class Meta:
        model = ChantDoc
        fields = ["fichier", "lien", "titre"]

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if self.instance.pk and not self.instance.lien:
            del self.fields["lien"]
        if self.instance.pk:# and not self.instance.fichier:
            del self.fields["fichier"]

    def clean(self):
        data = super().clean()
        if not self.instance.pk and not data.get("fichier") and not data.get("lien"):
            raise forms.ValidationError("Vous devez indiquer un fichier ou un lien")
        elif data.get("fichier") and data.get("lien"):
            raise forms.ValidationError("Vous ne pouvez pas indiquer à la fois un fichier et un lien")
        return data

    def save(self, **kwargs):
        is_new = not self.instance.pk
        instance = super().save(**kwargs)
        if is_new and instance.fichier and instance.fichier.name.endswith(".m4a"):
            old_path = instance.fichier.path
            try:
                subprocess.run([
                    "ffmpeg", "-i", old_path, "-q:a", "6", old_path.replace(".m4a", ".mp3"),
                ], check=True)
            except subprocess.CalledProcessError:
                pass
            else:
                instance.fichier.name = instance.fichier.name.replace(".m4a", ".mp3")
                instance.save()
                os.unlink(old_path)
        return instance


class ChantEditForm(BootstrapMixin, forms.ModelForm):
    class Meta:
        model = Chant
        fields = ["titre", "particularite"]

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.formset = None
        if self.instance.pk:
            DocFormSet = forms.inlineformset_factory(Chant, ChantDoc, form=ChantDocForm, extra=1)
            self.formset = DocFormSet(data=kwargs.get("data"), files=kwargs.get("files"), instance=self.instance)

    def is_valid(self):
        return all([super().is_valid()] + [self.formset.is_valid()] if self.formset else [])

    @transaction.atomic()
    def save(self, **kwargs):
        obj = super().save(**kwargs)
        if self.formset:
            self.formset.save()
        return obj
