from datetime import timedelta

from dal.autocomplete import ModelSelect2
from django import forms
from django.contrib import admin
from django.core.mail import send_mass_mail
from django.db.models import Count, Q, Sum
from django.template import loader
from django.template.defaultfilters import pluralize
from django.utils import timezone

from bda.models import (
    Attribution,
    CategorieSpectacle,
    ChoixSpectacle,
    Participant,
    Quote,
    Salle,
    Spectacle,
    SpectacleRevente,
    Tirage,
)


class ReadOnlyMixin(object):
    readonly_fields_update = ()

    def get_readonly_fields(self, request, obj=None):
        readonly_fields = super().get_readonly_fields(request, obj)
        if obj is None:
            return readonly_fields
        else:
            return readonly_fields + self.readonly_fields_update


class AttributionTabularAdminForm(forms.ModelForm):
    listing = None

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        spectacles = Spectacle.objects.select_related("location")
        if self.listing is not None:
            spectacles = spectacles.filter(listing=self.listing)
        self.fields["spectacle"].queryset = spectacles


class WithoutListingAttributionTabularAdminForm(AttributionTabularAdminForm):
    listing = False


class WithListingAttributionTabularAdminForm(AttributionTabularAdminForm):
    listing = True


class AttributionInline(admin.TabularInline):
    model = Attribution
    extra = 0
    listing = None

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if self.listing is not None:
            qs = qs.filter(spectacle__listing=self.listing)
        return qs


class WithListingAttributionInline(AttributionInline):
    exclude = ("given",)
    form = WithListingAttributionTabularAdminForm
    listing = True
    verbose_name_plural = "Attributions sur listing"


class WithoutListingAttributionInline(AttributionInline):
    form = WithoutListingAttributionTabularAdminForm
    listing = False
    verbose_name_plural = "Attributions hors listing"


class ParticipantAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        queryset = Spectacle.objects.select_related("location")

        if self.instance.pk is not None:
            queryset = queryset.filter(tirage=self.instance.tirage)

        self.fields["choicesrevente"].queryset = queryset


class ParticipantPaidFilter(admin.SimpleListFilter):
    """
    Permet de filtrer les participants sur s'ils ont payé leurs places ou pas
    """

    title = "A payé"
    parameter_name = "paid"

    def lookups(self, request, model_admin):
        return ((True, "Oui"), (False, "Non"))

    def queryset(self, request, queryset):
        return queryset.filter(paid=self.value())


class ParticipantAdmin(ReadOnlyMixin, admin.ModelAdmin):
    inlines = [WithListingAttributionInline, WithoutListingAttributionInline]

    def get_queryset(self, request):
        return self.model.objects.annotate_paid().annotate(
            nb_places=Count("attributions"),
            remain=Sum(
                "attribution__spectacle__price", filter=Q(attribution__paid=False)
            ),
            total=Sum("attributions__price"),
        )

    def nb_places(self, obj):
        return obj.nb_places

    nb_places.admin_order_field = "nb_places"
    nb_places.short_description = "Nombre de places"

    def paid(self, obj):
        return obj.paid

    paid.short_description = "A payé"
    paid.boolean = True
    paid.admin_order_field = "paid"

    def total(self, obj):
        tot = obj.total
        if tot:
            return "%.02f €" % tot
        else:
            return "0 €"

    total.admin_order_field = "total"
    total.short_description = "Total des places"

    def remain(self, obj):
        rem = obj.remain
        if rem:
            return "%.02f €" % rem
        else:
            return "0 €"

    remain.admin_order_field = "remain"
    remain.short_description = "Reste à payer"

    list_display = ("user", "nb_places", "total", "paid", "remain", "tirage")
    list_filter = (ParticipantPaidFilter, "tirage")
    search_fields = ("user__username", "user__first_name", "user__last_name")
    actions = ["send_attribs"]
    actions_on_bottom = True
    list_per_page = 400
    readonly_fields = ("total", "paid")
    readonly_fields_update = ("user", "tirage")
    form = ParticipantAdminForm

    def send_attribs(self, request, queryset):
        emails = []
        for member in queryset.all():
            subject = "Résultats du tirage au sort"
            attribs = member.attributions.all()
            context = {"member": member.user}

            template_name = ""
            if len(attribs) == 0:
                template_name = "bda/mails/attributions-decus.txt"
            else:
                template_name = "bda/mails/attributions.txt"
                context["places"] = attribs

            message = loader.render_to_string(template_name, context)
            emails.append((subject, message, "bda@ens.fr", [member.user.email]))

        send_mass_mail(emails)
        count = len(queryset.all())
        if count == 1:
            message_bit = "1 membre a"
            plural = ""
        else:
            message_bit = "%d membres ont" % count
            plural = "s"
        self.message_user(
            request, "%s été informé%s avec succès." % (message_bit, plural)
        )

    send_attribs.short_description = "Envoyer les résultats par mail"


class AttributionAdminForm(forms.ModelForm):
    def clean(self):
        cleaned_data = super().clean()
        participant = cleaned_data.get("participant")
        spectacle = cleaned_data.get("spectacle")
        if participant and spectacle:
            if participant.tirage != spectacle.tirage:
                raise forms.ValidationError(
                    "Erreur : le participant et le spectacle n'appartiennent"
                    "pas au même tirage"
                )
        return cleaned_data

    class Meta:
        widgets = {
            "participant": ModelSelect2(url="bda-participant-autocomplete"),
            "spectacle": ModelSelect2(url="bda-spectacle-autocomplete"),
        }


class AttributionAdmin(ReadOnlyMixin, admin.ModelAdmin):

    list_display = ("id", "spectacle", "participant", "given", "paid")
    search_fields = (
        "spectacle__title",
        "participant__user__username",
        "participant__user__first_name",
        "participant__user__last_name",
    )
    form = AttributionAdminForm
    readonly_fields_update = ("spectacle", "participant")


class ChoixSpectacleAdmin(admin.ModelAdmin):
    autocomplete_fields = ["participant", "spectacle"]

    def tirage(self, obj):
        return obj.participant.tirage

    list_display = ("participant", "tirage", "spectacle", "priority", "double_choice")
    list_filter = ("double_choice", "participant__tirage")
    search_fields = (
        "participant__user__username",
        "participant__user__first_name",
        "participant__user__last_name",
        "spectacle__title",
    )


class QuoteInline(admin.TabularInline):
    model = Quote


class SpectacleAdmin(admin.ModelAdmin):
    inlines = [QuoteInline]
    model = Spectacle
    list_display = ("title", "date", "tirage", "location", "slots", "price", "listing")
    list_filter = ("location", "tirage")
    search_fields = ("title", "location__name")
    readonly_fields = ("rappel_sent",)


class TirageAdmin(admin.ModelAdmin):
    model = Tirage
    list_display = ("title", "ouverture", "fermeture", "active", "enable_do_tirage")
    readonly_fields = ("tokens",)
    list_filter = ("active",)
    search_fields = ("title",)


class SalleAdmin(admin.ModelAdmin):
    model = Salle
    search_fields = ("name", "address")


class SpectacleReventeAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        qset = Participant.objects.select_related("user", "tirage")

        if self.instance.pk is not None:
            qset = qset.filter(tirage=self.instance.seller.tirage)

        self.fields["confirmed_entry"].queryset = qset
        self.fields["seller"].queryset = qset
        self.fields["soldTo"].queryset = qset


class SpectacleReventeAdmin(admin.ModelAdmin):
    """
    Administration des reventes de spectacles
    """

    model = SpectacleRevente

    def spectacle(self, obj):
        """
        Raccourci vers le spectacle associé à la revente.
        """
        return obj.attribution.spectacle

    list_display = ("spectacle", "seller", "date", "soldTo")
    raw_id_fields = ("attribution",)
    readonly_fields = ("date_tirage",)
    search_fields = [
        "attribution__spectacle__title",
        "seller__user__username",
        "seller__user__first_name",
        "seller__user__last_name",
    ]

    actions = ["transfer", "reinit"]
    actions_on_bottom = True
    form = SpectacleReventeAdminForm

    def transfer(self, request, queryset):
        """
        Effectue le transfert des reventes pour lesquels on connaît l'acheteur.
        """
        reventes = queryset.exclude(soldTo__isnull=True).all()
        count = reventes.count()
        for revente in reventes:
            attrib = revente.attribution
            attrib.participant = revente.soldTo
            attrib.save()
        self.message_user(
            request,
            "%d attribution%s %s été transférée%s avec succès."
            % (count, pluralize(count), pluralize(count, "a,ont"), pluralize(count)),
        )

    transfer.short_description = "Transférer les reventes sélectionnées"

    def reinit(self, request, queryset):
        """
        Réinitialise les reventes.
        """
        count = queryset.count()
        for revente in queryset.filter(
            attribution__spectacle__date__gte=timezone.now()
        ):
            revente.reset(new_date=timezone.now() - timedelta(hours=1))
        self.message_user(
            request,
            "%d attribution%s %s été réinitialisée%s avec succès."
            % (count, pluralize(count), pluralize(count, "a,ont"), pluralize(count)),
        )

    reinit.short_description = "Réinitialiser les reventes sélectionnées"


admin.site.register(CategorieSpectacle)
admin.site.register(Spectacle, SpectacleAdmin)
admin.site.register(Salle, SalleAdmin)
admin.site.register(Participant, ParticipantAdmin)
admin.site.register(Attribution, AttributionAdmin)
admin.site.register(ChoixSpectacle, ChoixSpectacleAdmin)
admin.site.register(Tirage, TirageAdmin)
admin.site.register(SpectacleRevente, SpectacleReventeAdmin)