from dal.autocomplete import ModelSelect2
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import Group, Permission, User
from django.db.models import Q
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _

from gestioncof.models import (
    Club,
    CofProfile,
    Event,
    EventCommentField,
    EventOption,
    EventOptionChoice,
    EventRegistration,
    Survey,
    SurveyQuestion,
    SurveyQuestionAnswer,
)
from petitscours.models import (
    PetitCoursAbility,
    PetitCoursAttribution,
    PetitCoursAttributionCounter,
    PetitCoursDemande,
    PetitCoursSubject,
)


def add_link_field(target_model="", field="", link_text=str, desc_text=str):
    def add_link(cls):
        reverse_name = target_model or cls.model.__name__.lower()

        def link(self, instance):
            app_name = instance._meta.app_label
            reverse_path = "admin:%s_%s_change" % (app_name, reverse_name)
            link_obj = getattr(instance, field, None) or instance
            if not link_obj.id:
                return ""
            url = reverse(reverse_path, args=(link_obj.id,))
            return mark_safe("<a href='%s'>%s</a>" % (url, link_text(link_obj)))

        link.allow_tags = True
        link.short_description = desc_text(reverse_name + " link")
        cls.link = link
        cls.readonly_fields = list(getattr(cls, "readonly_fields", [])) + ["link"]
        return cls

    return add_link


class SurveyQuestionAnswerInline(admin.TabularInline):
    model = SurveyQuestionAnswer


@add_link_field(
    desc_text=lambda x: "Réponses", link_text=lambda x: "Éditer les réponses"
)
class SurveyQuestionInline(admin.TabularInline):
    model = SurveyQuestion


class SurveyQuestionAdmin(admin.ModelAdmin):
    search_fields = ("survey__title", "answer")
    inlines = [SurveyQuestionAnswerInline]


class SurveyAdmin(admin.ModelAdmin):
    search_fields = ("title", "details")
    inlines = [SurveyQuestionInline]


class EventOptionChoiceInline(admin.TabularInline):
    model = EventOptionChoice


@add_link_field(desc_text=lambda x: "Choix", link_text=lambda x: "Éditer les choix")
class EventOptionInline(admin.TabularInline):
    model = EventOption


class EventCommentFieldInline(admin.TabularInline):
    model = EventCommentField


class EventOptionAdmin(admin.ModelAdmin):
    search_fields = ("event__title", "name")
    inlines = [EventOptionChoiceInline]


class EventAdmin(admin.ModelAdmin):
    search_fields = ("title", "location", "description")
    inlines = [EventOptionInline, EventCommentFieldInline]


class CofProfileInline(admin.StackedInline):
    model = CofProfile
    inline_classes = ("collapse open",)


def ProfileInfo(field, short_description, boolean=False):
    def getter(self):
        try:
            return getattr(self.profile, field)
        except CofProfile.DoesNotExist:
            return ""

    getter.short_description = short_description
    getter.boolean = boolean
    return getter


User.profile_phone = ProfileInfo("phone", "Téléphone")
User.profile_occupation = ProfileInfo("occupation", "Occupation")
User.profile_departement = ProfileInfo("departement", "Departement")
User.profile_mailing_cof = ProfileInfo("mailing_cof", "ML COF", True)
User.profile_mailing_bda = ProfileInfo("mailing_bda", "ML BdA", True)
User.profile_mailing_bda_revente = ProfileInfo("mailing_bda_revente", "ML BdA-R", True)


class UserProfileAdmin(UserAdmin):
    def is_buro(self, obj):
        try:
            return obj.profile.is_buro
        except CofProfile.DoesNotExist:
            return False

    is_buro.short_description = "Membre du Buro"
    is_buro.boolean = True

    def is_cof(self, obj):
        try:
            return obj.profile.is_cof
        except CofProfile.DoesNotExist:
            return False

    is_cof.short_description = "Membre du COF"
    is_cof.boolean = True

    list_display = UserAdmin.list_display + (
        "profile_phone",
        "profile_occupation",
        "profile_mailing_cof",
        "profile_mailing_bda",
        "profile_mailing_bda_revente",
        "is_cof",
        "is_buro",
    )
    list_display_links = ("username", "email", "first_name", "last_name")
    list_filter = UserAdmin.list_filter + (
        "profile__is_cof",
        "profile__is_buro",
        "profile__mailing_cof",
        "profile__mailing_bda",
    )
    search_fields = UserAdmin.search_fields + ("profile__phone",)
    inlines = [CofProfileInline]

    staff_fieldsets = [
        (None, {"fields": ["username", "password"]}),
        (_("Personal info"), {"fields": ["first_name", "last_name", "email"]}),
    ]

    def get_fieldsets(self, request, user=None):
        if not request.user.is_superuser:
            return self.staff_fieldsets
        return super().get_fieldsets(request, user)

    def save_model(self, request, user, form, change):
        cof_group, created = Group.objects.get_or_create(name="COF")
        if created:
            # Si le groupe COF n'était pas déjà dans la bdd
            # On lui assigne les bonnes permissions
            perms = Permission.objects.filter(
                Q(content_type__app_label="gestioncof")
                | Q(content_type__app_label="bda")
                | (Q(content_type__app_label="auth") & Q(content_type__model="user"))
            )
            cof_group.permissions.set(perms)
            # On y associe les membres du Burô
            cof_group.user_set.set(User.objects.filter(profile__is_buro=True))
            # Sauvegarde
            cof_group.save()
        # le Burô est staff et appartient au groupe COF
        if user.profile.is_buro:
            user.is_staff = True
            user.groups.add(cof_group)
        else:
            user.is_staff = False
            user.groups.remove(cof_group)
        user.save()


# FIXME: This is absolutely horrible.
def user_str(self):
    if self.first_name and self.last_name:
        return "{} ({})".format(self.get_full_name(), self.username)
    else:
        return self.username


User.__str__ = user_str


class EventRegistrationAdminForm(forms.ModelForm):
    class Meta:
        widgets = {"user": ModelSelect2(url="cof-user-autocomplete")}


class EventRegistrationAdmin(admin.ModelAdmin):
    form = EventRegistrationAdminForm

    list_display = ("__str__", "event", "user", "paid")
    list_filter = ("paid",)
    search_fields = (
        "user__username",
        "user__first_name",
        "user__last_name",
        "user__email",
        "event__title",
    )


class PetitCoursAbilityAdmin(admin.ModelAdmin):
    list_display = ("user", "matiere", "niveau", "agrege")
    search_fields = (
        "user__username",
        "user__first_name",
        "user__last_name",
        "user__email",
        "matiere__name",
        "niveau",
    )
    list_filter = ("matiere", "niveau", "agrege")


class PetitCoursAttributionAdmin(admin.ModelAdmin):
    list_display = ("user", "demande", "matiere", "rank")
    search_fields = ("user__username", "matiere__name")


class PetitCoursAttributionCounterAdmin(admin.ModelAdmin):
    list_display = ("user", "matiere", "count")
    list_filter = ("matiere",)
    search_fields = (
        "user__username",
        "user__first_name",
        "user__last_name",
        "user__email",
        "matiere__name",
    )
    actions = ["reset"]
    actions_on_bottom = True

    def reset(self, request, queryset):
        queryset.update(count=0)

    reset.short_description = "Remise à zéro du compteur"


class PetitCoursDemandeAdmin(admin.ModelAdmin):
    list_display = (
        "name",
        "email",
        "agrege_requis",
        "niveau",
        "created",
        "traitee",
        "processed",
    )
    list_filter = ("traitee", "niveau")
    search_fields = ("name", "email", "phone", "lieu", "remarques")
    readonly_fields = ("created",)


class ClubAdminForm(forms.ModelForm):
    def clean(self):
        cleaned_data = super().clean()
        respos = cleaned_data.get("respos")
        members = cleaned_data.get("membres")
        for respo in respos.all():
            if respo not in members:
                raise forms.ValidationError(
                    "Erreur : le respo %s n'est pas membre du club."
                    % respo.get_full_name()
                )
        return cleaned_data


class ClubAdmin(admin.ModelAdmin):
    list_display = ["name"]
    form = ClubAdminForm


admin.site.register(Survey, SurveyAdmin)
admin.site.register(SurveyQuestion, SurveyQuestionAdmin)
admin.site.register(Event, EventAdmin)
admin.site.register(EventOption, EventOptionAdmin)
admin.site.unregister(User)
admin.site.register(User, UserProfileAdmin)
admin.site.register(CofProfile)
admin.site.register(Club, ClubAdmin)
admin.site.register(PetitCoursSubject)
admin.site.register(PetitCoursAbility, PetitCoursAbilityAdmin)
admin.site.register(PetitCoursAttribution, PetitCoursAttributionAdmin)
admin.site.register(PetitCoursAttributionCounter, PetitCoursAttributionCounterAdmin)
admin.site.register(PetitCoursDemande, PetitCoursDemandeAdmin)
admin.site.register(EventRegistration, EventRegistrationAdmin)