from typing import Any

from django.contrib import admin
from django.contrib.admin import register
from django.contrib.auth.admin import (
    GroupAdmin as BaseGroupAdmin,
)
from django.contrib.auth.admin import (
    UserAdmin as BaseUserAdmin,
)
from django.contrib.auth.models import Group as BaseGroup
from django.contrib.auth.models import Permission
from django.core.exceptions import PermissionDenied
from django.db.models import Prefetch, QuerySet
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _
from rest_framework_simplejwt.token_blacklist.admin import (
    BlacklistedTokenAdmin as BaseBlacklistedTokenAdmin,
)
from rest_framework_simplejwt.token_blacklist.admin import (
    OutstandingTokenAdmin as BaseOutstandingTokenAdmin,
)
from rest_framework_simplejwt.token_blacklist.models import (
    BlacklistedToken as BaseBlacklistedToken,
)
from rest_framework_simplejwt.token_blacklist.models import (
    OutstandingToken as BaseOutstandingToken,
)
from unfold.admin import ModelAdmin, TabularInline
from unfold.forms import AdminPasswordChangeForm, UserCreationForm

from engine.core.admin import ActivationActionsMixin
from engine.core.models import Order
from engine.payments.models import Balance
from engine.vibes_auth.forms import UserForm
from engine.vibes_auth.models import (
    BlacklistedToken,
    ChatMessage,
    ChatThread,
    Group,
    OutstandingToken,
    ThreadStatus,
    User,
)


class BalanceInline(TabularInline):
    model = Balance
    can_delete = False
    extra = 0
    verbose_name = _("balance")
    verbose_name_plural = _("balance")
    is_navtab = True
    icon = "fa-solid fa-wallet"


class OrderInline(TabularInline):
    model = Order
    extra = 0
    verbose_name = _("order")
    verbose_name_plural = _("orders")
    is_navtab = True
    icon = "fa-solid fa-cart-shopping"


class UserAdmin(ActivationActionsMixin, BaseUserAdmin, ModelAdmin):
    inlines = (BalanceInline, OrderInline)
    fieldsets = (
        (None, {"fields": ("email", "password")}),
        (
            _("personal info"),
            {"fields": ("first_name", "last_name", "phone_number", "avatar")},
        ),
        (
            _("permissions"),
            {
                "fields": (
                    "is_active",
                    "is_verified",
                    "is_subscribed",
                    "is_staff",
                    "is_superuser",
                    "groups",
                    "user_permissions",
                )
            },
        ),
        (_("important dates"), {"fields": ("last_login", "date_joined")}),
        (_("additional info"), {"fields": ("language", "attributes")}),
    )
    add_fieldsets = (
        (
            None,
            {
                "classes": ("wide",),
                "fields": ("email", "password1", "password2"),
            },
        ),
    )
    list_display = ("email", "phone_number", "is_verified", "is_active", "is_staff")
    search_fields = ("email", "phone_number")
    list_filter = (
        "is_verified",
        "is_active",
        "is_staff",
        "is_superuser",
        "is_subscribed",
    )
    ordering = ("email",)
    readonly_fields = ("password",)
    form = UserForm
    add_form = UserCreationForm
    change_password_form = AdminPasswordChangeForm

    def get_queryset(self, request: HttpRequest) -> QuerySet[User]:
        qs = super().get_queryset(request)
        return qs.prefetch_related(
            "groups", "payments_balance", "orders"
        ).prefetch_related(
            Prefetch(
                "user_permissions",
                queryset=Permission.objects.select_related("content_type"),
            )
        )

    def save_model(  # ty: ignore[invalid-method-override]
        self, request: HttpRequest, obj: Any, form: UserForm, change: Any
    ) -> None:
        if form.cleaned_data.get("attributes") is None:
            obj.attributes = None
        if (
            form.cleaned_data.get("is_superuser", False)
            and not request.user.is_superuser  # ty: ignore[possibly-missing-attribute]
        ):
            raise PermissionDenied(_("You cannot jump over your head!"))
        super().save_model(request, obj, form, change)  # ty: ignore[invalid-argument-type]


# noinspection PyUnusedLocal
@register(ChatThread)
class ChatThreadAdmin(ModelAdmin):
    list_display = (
        "uuid",
        "user",
        "email",
        "assigned_to",
        "status",
        "last_message_at",
        "is_active",
        "created",
        "modified",
    )
    list_filter = (
        "status",
        "is_active",
        ("assigned_to", admin.EmptyFieldListFilter),
    )
    search_fields = ("uuid", "email", "user__email", "user__username")
    autocomplete_fields = ("user", "assigned_to")
    actions = (
        "close_threads",
        "open_threads",
        "delete_selected",
    )
    readonly_fields = ("created", "modified")

    @admin.action(description=_("Close selected threads"))
    def close_threads(self, request, queryset):
        queryset.update(status=ThreadStatus.CLOSED)

    @admin.action(description=_("Open selected threads"))
    def open_threads(self, request, queryset):
        queryset.update(status=ThreadStatus.OPEN)


@register(ChatMessage)
class ChatMessageAdmin(admin.ModelAdmin):
    list_display = ("uuid", "thread", "sender_type", "sender_user", "sent_at")
    list_filter = ("sender_type",)
    search_fields = ("uuid", "thread__uuid", "sender_user__email")
    autocomplete_fields = ("thread", "sender_user")
    readonly_fields = ("created", "modified")


class GroupAdmin(BaseGroupAdmin, ModelAdmin):
    pass


class BlacklistedTokenAdmin(BaseBlacklistedTokenAdmin, ModelAdmin):
    pass


class OutstandingTokenAdmin(BaseOutstandingTokenAdmin, ModelAdmin):
    pass


admin.site.register(User, UserAdmin)

admin.site.unregister(BaseGroup)
admin.site.register(Group, GroupAdmin)

admin.site.unregister(BaseBlacklistedToken)
admin.site.register(BlacklistedToken, BlacklistedTokenAdmin)

admin.site.unregister(BaseOutstandingToken)
admin.site.register(OutstandingToken, OutstandingTokenAdmin)
