from django import forms
from django.apps import apps
from django.contrib.auth.models import Permission
from django.contrib import admin
from django.contrib.auth.models import Group as BaseGroup, User
from django.core.exceptions import ValidationError
from django.db.models import Count
from django.db.models.functions import Lower
from django.db.models.signals import pre_save, post_save, pre_delete, \
    post_delete, m2m_changed
from django.dispatch import receiver
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _

from .models import AuthGroup, ReservedGroupName
from .models import GroupRequest

if 'eve_autogroups' in apps.app_configs:
    _has_auto_groups = True
else:
    _has_auto_groups = False


class AuthGroupInlineAdmin(admin.StackedInline):
    model = AuthGroup
    filter_horizontal = ('group_leaders', 'group_leader_groups', 'states',)
    fields = (
        'description',
        'group_leaders',
        'group_leader_groups',
        'states', 'internal',
        'hidden',
        'open',
        'public'
    )
    verbose_name_plural = 'Auth Settings'
    verbose_name = ''

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        """overriding this formfield to have sorted lists in the form"""
        if db_field.name == "group_leaders":
            kwargs["queryset"] = User.objects.order_by(Lower('username'))
        elif db_field.name == "group_leader_groups":
            kwargs["queryset"] = Group.objects.order_by(Lower('name'))
        return super().formfield_for_manytomany(db_field, request, **kwargs)

    def has_delete_permission(self, request, obj=None):
        return False

    def has_change_permission(self, request, obj=None):
        return request.user.has_perm('auth.change_group')


if _has_auto_groups:
    class IsAutoGroupFilter(admin.SimpleListFilter):
        title = 'auto group'
        parameter_name = 'is_auto_group__exact'

        def lookups(self, request, model_admin):
            return (
                ('yes', 'Yes'),
                ('no', 'No'),
            )

        def queryset(self, request, queryset):
            value = self.value()
            if value == 'yes':
                return queryset.exclude(
                    managedalliancegroup__isnull=True,
                    managedcorpgroup__isnull=True
                )
            elif value == 'no':
                return queryset.filter(
                    managedalliancegroup__isnull=True,
                    managedcorpgroup__isnull=True
                )
            return queryset


class HasLeaderFilter(admin.SimpleListFilter):
    title = 'has leader'
    parameter_name = 'has_leader__exact'

    def lookups(self, request, model_admin):
        return (
            ('yes', 'Yes'),
            ('no', 'No'),
        )

    def queryset(self, request, queryset):
        value = self.value()
        if value == 'yes':
            return queryset.filter(authgroup__group_leaders__isnull=False)
        elif value == 'no':
            return queryset.filter(authgroup__group_leaders__isnull=True)
        return queryset


class GroupAdminForm(forms.ModelForm):
    def clean_name(self):
        my_name = self.cleaned_data['name']
        if ReservedGroupName.objects.filter(name__iexact=my_name).exists():
            raise ValidationError(
                _("This name has been reserved and can not be used for groups."),
                code='reserved_name'
            )
        return my_name


class GroupAdmin(admin.ModelAdmin):
    form = GroupAdminForm
    list_select_related = ('authgroup',)
    ordering = ('name',)
    list_display = (
        'name',
        '_description',
        '_properties',
        '_member_count',
        'has_leader'
    )
    list_filter = [
        'authgroup__internal',
        'authgroup__hidden',
        'authgroup__open',
        'authgroup__public',
    ]
    if _has_auto_groups:
        list_filter.append(IsAutoGroupFilter)
    list_filter.append(HasLeaderFilter)

    search_fields = ('name', 'authgroup__description')

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if _has_auto_groups:
            qs = qs.prefetch_related('managedalliancegroup_set', 'managedcorpgroup_set')
        qs = qs.prefetch_related('authgroup__group_leaders').select_related('authgroup')
        qs = qs.annotate(
            member_count=Count('user', distinct=True),
        )
        return qs

    def _description(self, obj):
        return obj.authgroup.description

    def _member_count(self, obj):
        return obj.member_count

    _member_count.short_description = 'Members'
    _member_count.admin_order_field = 'member_count'

    def has_leader(self, obj):
        return obj.authgroup.group_leaders.exists() or obj.authgroup.group_leader_groups.exists()

    has_leader.boolean = True

    def _properties(self, obj):
        properties = list()
        if _has_auto_groups and (
            obj.managedalliancegroup_set.exists()
            or obj.managedcorpgroup_set.exists()
        ):
            properties.append('Auto Group')
        elif obj.authgroup.internal:
            properties.append('Internal')
        else:
            if obj.authgroup.hidden:
                properties.append('Hidden')
            if obj.authgroup.open:
                properties.append('Open')
            if obj.authgroup.public:
                properties.append('Public')
        if not properties:
            properties.append('Default')

        return properties

    _properties.short_description = "properties"

    filter_horizontal = ('permissions',)
    inlines = (AuthGroupInlineAdmin,)

    def formfield_for_manytomany(self, db_field, request, **kwargs):
        if db_field.name == "permissions":
            kwargs["queryset"] = Permission.objects.select_related("content_type").all()
        return super().formfield_for_manytomany(db_field, request, **kwargs)

    def save_formset(self, request, form, formset, change):
        for inline_form in formset:
            ag_instance = inline_form.save(commit=False)
            ag_instance.group = form.instance
            ag_instance.save()
        formset.save()


class Group(BaseGroup):
    class Meta:
        proxy = True
        verbose_name = BaseGroup._meta.verbose_name
        verbose_name_plural = BaseGroup._meta.verbose_name_plural


try:
    admin.site.unregister(BaseGroup)
finally:
    admin.site.register(Group, GroupAdmin)


@admin.register(GroupRequest)
class GroupRequestAdmin(admin.ModelAdmin):
    search_fields = ('user__username', )
    list_display = ('id', 'group', 'user', '_leave_request')
    list_filter = (
        ('group', admin.RelatedOnlyFieldListFilter),
        ('user', admin.RelatedOnlyFieldListFilter),
        'leave_request',
    )

    def _leave_request(self, obj) -> True:
        return obj.leave_request

    _leave_request.short_description = 'is leave request'
    _leave_request.boolean = True


class ReservedGroupNameAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['created_by'].initial = self.current_user.username
        self.fields['created_at'].initial = _("(auto)")

    created_by = forms.CharField(disabled=True)
    created_at = forms.CharField(disabled=True)

    def clean_name(self):
        my_name = self.cleaned_data['name'].lower()
        if Group.objects.filter(name__iexact=my_name).exists():
            raise ValidationError(
                _("There already exists a group with that name."), code='already_exists'
            )
        return my_name

    def clean_created_at(self):
        return now()


@admin.register(ReservedGroupName)
class ReservedGroupNameAdmin(admin.ModelAdmin):
    form = ReservedGroupNameAdminForm
    list_display = ("name", "created_by", "created_at")

    def get_form(self, request, *args, **kwargs):
        form = super().get_form(request, *args, **kwargs)
        form.current_user = request.user
        return form

    def has_change_permission(self, *args, **kwargs) -> bool:
        return False


@receiver(pre_save, sender=Group)
def redirect_pre_save(sender, signal=None, *args, **kwargs):
    pre_save.send(BaseGroup, *args, **kwargs)


@receiver(post_save, sender=Group)
def redirect_post_save(sender, signal=None, *args, **kwargs):
    post_save.send(BaseGroup, *args, **kwargs)


@receiver(pre_delete, sender=Group)
def redirect_pre_delete(sender, signal=None, *args, **kwargs):
    pre_delete.send(BaseGroup, *args, **kwargs)


@receiver(post_delete, sender=Group)
def redirect_post_delete(sender, signal=None, *args, **kwargs):
    post_delete.send(BaseGroup, *args, **kwargs)


@receiver(m2m_changed, sender=Group.permissions.through)
def redirect_m2m_changed_permissions(sender, signal=None, *args, **kwargs):
    m2m_changed.send(BaseGroup, *args, **kwargs)
