import json

from django import forms
from django.conf import settings
from django.contrib import admin, messages
from django.contrib.admin.options import IS_POPUP_VAR
from django.contrib.admin.utils import unquote
from django.contrib.auth import update_session_auth_hash, password_validation
from django.contrib.auth.admin import sensitive_post_parameters_m
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.contrib.auth.hashers import make_password
from django.contrib.humanize.templatetags.humanize import intcomma
from django.core.exceptions import PermissionDenied, ValidationError
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models import Count
from django.db.models.functions import TruncDay
from django.forms import ModelForm
from django.http import Http404, HttpResponseRedirect
from django.template.response import TemplateResponse
from django.urls import path
from django.urls import reverse
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _

from authenticate.models import VerificationAttempt, User, WrongPasswordAttempt, UserJWT, SMSCall, EmailCall
from collaborators import defined_roles as dr
from collaborators.models import Access
from institute.models import Institute

if settings.DJANGO_ADMIN_IS_ENABLED:

    class VerificationAttemptAdmin(admin.ModelAdmin):

        list_display = ('email', 'phone', 'verification_code',
                        'ip', 'created_at')
        search_fields = ('email', 'phone', 'ip')
        readonly_fields = ('verification_code',)
        ordering = ('-created_at',)
        list_filter = [
            "created_at"]


    admin.site.register(VerificationAttempt, VerificationAttemptAdmin)


    @admin.action(description='Create Sandbox')
    def sandbox_creator(modeladmin, request, queryset):
        for user in queryset:
            if not Access.objects.filter(user=user, institute__sandbox=True).exists():
                institute = Institute.objects.create(name=user.full_name or 'sandbox', user=user, sandbox=True)
                Access.objects.create(user=user, institute=institute, institute_name=institute.name,
                                      institute_created_at=institute.created_at, roles=[dr.__ROLE_OWNER__])


    class UserAdminForm(ModelForm):
        password = ReadOnlyPasswordHashField(
            label=_("Password"),
            help_text=_(
                "Raw passwords are not stored, so there is no way to see this "
                "user’s password, but you can change the password using "
                '<a href="{}">this form</a>.'
            ),
        )

        class Meta:
            model = User
            fields = ['first_name', 'last_name',
                      'email', 'phone', 'username', 'recovery_phone', 'password']

        def __init__(self, *args, **kwargs):
            super().__init__(*args, **kwargs)
            password = self.fields.get("password")
            if password:
                password.help_text = password.help_text.format("../password/")


    class AdminPasswordChangeForm(forms.Form):
        """
        A form used to change the password of a user in the admin interface.
        """

        error_messages = {
            "password_mismatch": _("The two password fields didn’t match."),
        }
        required_css_class = "required"
        password1 = forms.CharField(
            label=_("Password"),
            widget=forms.PasswordInput(
                attrs={"autocomplete": "new-password", "autofocus": True}
            ),
            strip=False,
            help_text=password_validation.password_validators_help_text_html(),
        )
        password2 = forms.CharField(
            label=_("Password (again)"),
            widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
            strip=False,
            help_text=_("Enter the same password as before, for verification."),
        )

        def __init__(self, user, *args, **kwargs):
            self.user = user
            super().__init__(*args, **kwargs)

        def clean_password2(self):
            password1 = self.cleaned_data.get("password1")
            password2 = self.cleaned_data.get("password2")
            if password1 and password2 and password1 != password2:
                raise ValidationError(
                    self.error_messages["password_mismatch"],
                    code="password_mismatch",
                )
            password_validation.validate_password(password2, self.user)
            return password2

        def save(self, commit=True):
            """Save the new password."""
            password = self.cleaned_data["password1"]
            self.user.password = make_password(str(self.user.id) + password)
            if commit:
                self.user.save()
            return self.user

        @property
        def changed_data(self):
            data = super().changed_data
            for name in self.fields:
                if name not in data:
                    return []
            return ["password"]


    class UserAdmin(admin.ModelAdmin):
        form = UserAdminForm
        change_password_form = AdminPasswordChangeForm
        list_display = ('first_name', 'last_name',
                        'email', 'phone', 'human_readable_balance', 'join_date')

        search_fields = ('email', 'phone', 'first_name', 'last_name')
        ordering = ('-join_date',)
        list_filter = ["join_date"]

        def get_urls(self):
            return [
                       path(
                           "<id>/password/",
                           self.admin_site.admin_view(self.user_change_password),
                           name="auth_user_password_change",
                       ),
                   ] + super().get_urls()

        @sensitive_post_parameters_m
        def user_change_password(self, request, id, form_url=""):
            user = self.get_object(request, unquote(id))
            if not self.has_change_permission(request, user):
                raise PermissionDenied
            if user is None:
                raise Http404(
                    _("%(name)s object with primary key %(key)r does not exist.")
                    % {
                        "name": self.model._meta.verbose_name,
                        "key": escape(id),
                    }
                )
            if request.method == "POST":
                form = self.change_password_form(user, request.POST)
                if form.is_valid():
                    form.save()
                    change_message = self.construct_change_message(request, form, None)
                    self.log_change(request, user, change_message)
                    msg = gettext("Password changed successfully.")
                    messages.success(request, msg)
                    update_session_auth_hash(request, form.user)
                    return HttpResponseRedirect(
                        reverse(
                            "%s:%s_%s_change"
                            % (
                                self.admin_site.name,
                                user._meta.app_label,
                                user._meta.model_name,
                            ),
                            args=(user.pk,),
                        )
                    )
            else:
                form = self.change_password_form(user)

            fieldsets = [(None, {"fields": list(form.base_fields)})]
            admin_form = admin.helpers.AdminForm(form, fieldsets, {})

            context = {
                "title": _("Change password: %s") % escape(str(user)),
                "adminForm": admin_form,
                "form_url": form_url,
                "form": form,
                "is_popup": (IS_POPUP_VAR in request.POST or IS_POPUP_VAR in request.GET),
                "is_popup_var": IS_POPUP_VAR,
                "add": True,
                "change": False,
                "has_delete_permission": False,
                "has_change_permission": True,
                "has_absolute_url": False,
                "opts": self.model._meta,
                "original": user,
                "save_as": False,
                "show_save": True,
                **self.admin_site.each_context(request),
            }

            request.current_app = self.admin_site.name

            return TemplateResponse(
                request,
                "admin/auth/user/change_password.html",
                context,
            )

        def changelist_view(self, request, extra_context=None):
            # Aggregate new subscribers per day
            chart_data = (
                User.objects.annotate(date=TruncDay("join_date")).values(
                    "date").annotate(y=Count("id")).order_by("-date")
            )

            # Serialize and attach the chart data to the template context
            as_json = json.dumps(list(chart_data), cls=DjangoJSONEncoder)
            extra_context = extra_context or {"chart_data": as_json}

            # Call the superclass changelist_view to render the page
            return super().changelist_view(request, extra_context=extra_context)

        def human_readable_balance(self, obj):
            raw_balance = f" تومان {intcomma(obj.balance)}"
            return raw_balance

        human_readable_balance.short_description = "Balance"

        def institutes(self, obj):
            return Access.objects.filter(user=obj).count()

        institutes.short_description = "Institutes"


    admin.site.register(User, UserAdmin)


    class WrongPasswordAttemptAdmin(admin.ModelAdmin):
        list_display = ('email', 'phone', 'ip', 'created_at')
        search_fields = ('ip', 'email', 'phone',
                         'created_at')
        ordering = ('-created_at',)
        # readonly_fields = ()
        list_filter = [
            "created_at"]


    admin.site.register(WrongPasswordAttempt, WrongPasswordAttemptAdmin)


    class UserJWTAdmin(admin.ModelAdmin):

        def token_preview(self, obj):
            return obj.token[-20:]

        exclude = ('token',)
        list_display = ('token_preview', 'user', 'created_at', 'expire_at')
        search_fields = ('user', 'token')
        readonly_fields = ('token_preview',)
        list_filter = [
            "created_at",
            "expire_at"]


    admin.site.register(UserJWT, UserJWTAdmin)


    class SMSCallAdmin(admin.ModelAdmin):

        list_display = ('phone', 'success', 'created_at')
        ordering = ('-created_at',)
        search_fields = ('phone',)
        list_filter = [
            "created_at"]


    admin.site.register(SMSCall, SMSCallAdmin)


    class EmailCallAdmin(admin.ModelAdmin):

        list_display = ('email', 'success', 'created_at')
        ordering = ('-created_at',)
        search_fields = ('email',)
        list_filter = [
            "created_at"]


    admin.site.register(EmailCall, EmailCallAdmin)

else:
    class UserAdmin(admin.ModelAdmin):
        def full_name(self, obj):
            if not obj.first_name:
                obj.first_name = '-'
            if not obj.last_name:
                obj.last_name = '-'
            return f'{obj.first_name} {obj.last_name}'

        def image_thumbnail_preview(self, obj):
            return mark_safe(
                f'<a href="{obj.image_thumbnail.url}"><img src="{obj.image_thumbnail.url}"/></a>') if obj.image_thumbnail else '-'

        fieldsets = (
            ('User Details', {
                'fields': ('first_name', 'last_name', 'email', 'phone', 'image_thumbnail_preview', 'join_date')
            }),
        )

        list_display = ('email', 'phone', 'full_name', 'join_date')
        search_fields = ('email', 'phone')
        readonly_fields = ('first_name', 'last_name', 'email', 'phone',
                           'image_thumbnail_preview', 'join_date')


    admin.site.register(User, UserAdmin)
