from __future__ import annotations

from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
from django.contrib.auth.forms import ReadOnlyPasswordHashField
from django.utils.translation import gettext_lazy as _

try:
    from unfold.admin import ModelAdmin as UnfoldModelAdmin, StackedInline as UnfoldStackedInline
except ImportError:
    UnfoldModelAdmin = admin.ModelAdmin  # type: ignore[assignment]
    UnfoldStackedInline = admin.StackedInline  # type: ignore[assignment]

from simorgh.apps.localization.models import Country

from .models import (
    ActiveDirectoryConfig,
    AuthMethodConfig,
    MobileOtpToken,
    OtpSendLog,
    SocialAuthConfig,
    User,
    UserProfile,
)

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

_COUNTRY_QUERYSET = Country.objects.filter(is_enabled=True).order_by("sort_order", "name_english")


def _normalize_mobile(dial_code: str, local_number: str) -> str:
    """Combine a country dial_code (e.g. '+98') with a local number → E.164."""
    local = local_number.strip().lstrip("0").lstrip("+")
    # dial_code already contains the '+', e.g. '+98'
    return f"{dial_code}{local}"


# ---------------------------------------------------------------------------
# Change form (editing an existing user)
# ---------------------------------------------------------------------------

class UserChangeAdminForm(forms.ModelForm):
    """Change form with ReadOnlyPasswordHashField + mobile_country / mobile_number.

    Django admin's get_form() only includes fields listed in fieldsets, so
    'mobile' is intentionally absent from the form.  The normalised E.164 value
    is stored on self._computed_mobile and applied in UserAdmin.save_model().
    """

    # Always keep the password field as a read-only hash display.
    password = ReadOnlyPasswordHashField(
        label=_("password"),
        help_text=_(
            "Raw passwords are not stored. Change the password using "
            '<a href="../password/">this form</a>.'
        ),
    )
    mobile_number = forms.CharField(
        label=_("local number"),
        max_length=20,
        required=False,
        help_text=_("Without country code, e.g. 9123456789"),
    )

    class Meta:
        model = User
        fields = "__all__"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Pre-fill mobile_number by stripping the country dial-code prefix.
        instance = self.instance
        if instance and instance.pk and instance.mobile:
            country = instance.mobile_country
            if country and instance.mobile.startswith(country.dial_code):
                self.fields["mobile_number"].initial = instance.mobile[len(country.dial_code):]
            else:
                self.fields["mobile_number"].initial = instance.mobile.lstrip("+")

    def clean(self):
        cleaned_data = super().clean()
        country = cleaned_data.get("mobile_country")
        number = cleaned_data.get("mobile_number", "").strip()

        if not number:
            self.add_error("mobile_number", _("Mobile number is required."))
            return cleaned_data
        if not country:
            self.add_error("mobile_country", _("Please select a country code."))
            return cleaned_data

        mobile = _normalize_mobile(country.dial_code, number)
        # Uniqueness check (mobile is not a form field, so Django won't do it).
        qs = User.objects.filter(mobile=mobile)
        if self.instance.pk:
            qs = qs.exclude(pk=self.instance.pk)
        if qs.exists():
            self.add_error("mobile_number", _("A user with this mobile number already exists."))
        else:
            self._computed_mobile = mobile

        return cleaned_data

    def clean_email(self):
        email = self.cleaned_data.get("email")
        return email.strip() if email and email.strip() else None


# ---------------------------------------------------------------------------
# Add form (creating a new user)
# ---------------------------------------------------------------------------

class UserCreationAdminForm(forms.ModelForm):
    """Custom creation form with country-code selector + local number + passwords."""

    mobile_country = forms.ModelChoiceField(
        queryset=_COUNTRY_QUERYSET,
        label=_("country code"),
        required=True,
        empty_label="—",
    )
    mobile_number = forms.CharField(
        label=_("local number"),
        max_length=20,
        required=True,
        help_text=_("Without country code, e.g. 9123456789"),
    )
    password1 = forms.CharField(
        label=_("password"),
        strip=False,
        widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
    )
    password2 = forms.CharField(
        label=_("password confirmation"),
        strip=False,
        widget=forms.PasswordInput(attrs={"autocomplete": "new-password"}),
        help_text=_("Enter the same password as before, for verification."),
    )

    class Meta:
        model = User
        fields = ("mobile_country", "email", "is_staff", "is_active")

    def clean_password2(self):
        pw1 = self.cleaned_data.get("password1", "")
        pw2 = self.cleaned_data.get("password2", "")
        if pw1 and pw2 and pw1 != pw2:
            raise forms.ValidationError(_("The two password fields didn't match."))
        return pw2

    def clean(self):
        cleaned_data = super().clean()
        country = cleaned_data.get("mobile_country")
        number = cleaned_data.get("mobile_number", "").strip()

        if not number:
            self.add_error("mobile_number", _("Mobile number is required."))
            return cleaned_data
        if not country:
            self.add_error("mobile_country", _("Please select a country code."))
            return cleaned_data

        cleaned_data["mobile"] = _normalize_mobile(country.dial_code, number)
        return cleaned_data

    def clean_email(self):
        email = self.cleaned_data.get("email")
        return email.strip() if email and email.strip() else None

    def save(self, commit=True):
        user = super().save(commit=False)
        user.mobile = self.cleaned_data["mobile"]
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
            self.save_m2m()
        return user


class UserProfileInline(UnfoldStackedInline):
    model = UserProfile
    can_delete = False
    extra = 0
    fields = ("avatar_image", "job_title", "department", "phone", "bio", "avatar", "linkedin_url", "website_url")


@admin.register(User)
class UserAdmin(DjangoUserAdmin, UnfoldModelAdmin):
    form = UserChangeAdminForm
    add_form = UserCreationAdminForm

    list_display = ("mobile", "email", "username", "is_staff", "is_active")
    search_fields = ("mobile", "email", "username")
    ordering = ("mobile",)
    inlines = [UserProfileInline]
    fieldsets = (
        (None, {"fields": ("mobile_country", "mobile_number", "email", "password")}),
        (_("Personal info"), {"fields": ("username", "language", "timezone")}),
        (
            _("Permissions"),
            {"fields": ("is_active", "is_staff", "is_superuser", "groups", "user_permissions")},
        ),
        (_("Important dates"), {"fields": ("last_login", "date_joined")}),
    )
    add_fieldsets = (
        (None, {"classes": ("wide",), "fields": ("mobile_country", "mobile_number", "email", "password1", "password2")}),
    )

    def save_model(self, request, obj, form, change):
        # Apply the E.164 mobile computed in UserChangeAdminForm.clean().
        if hasattr(form, "_computed_mobile"):
            obj.mobile = form._computed_mobile
        super().save_model(request, obj, form, change)


@admin.register(AuthMethodConfig)
class AuthMethodConfigAdmin(UnfoldModelAdmin):
    list_display = (
        "tenant",
        "is_password_enabled",
        "is_mobile_otp_enabled",
        "is_ad_enabled",
        "is_google_enabled",
        "is_github_enabled",
        "is_microsoft_enabled",
    )
    list_filter = (
        "is_password_enabled",
        "is_mobile_otp_enabled",
        "is_ad_enabled",
        "is_google_enabled",
    )
    search_fields = ("tenant__slug", "tenant__name")


@admin.register(ActiveDirectoryConfig)
class ActiveDirectoryConfigAdmin(UnfoldModelAdmin):
    list_display = ("tenant", "server_url", "domain", "is_active")
    list_filter = ("is_active",)
    search_fields = ("tenant__slug", "domain", "server_url")
    fieldsets = (
        (None, {"fields": ("tenant", "is_active")}),
        (_("Server"), {"fields": ("server_url", "domain", "base_dn")}),
        (_("Search"), {"fields": ("user_search_filter",)}),
        (_("Service account"), {"fields": ("service_account_dn", "service_account_password")}),
    )


@admin.register(SocialAuthConfig)
class SocialAuthConfigAdmin(UnfoldModelAdmin):
    list_display = (
        "tenant",
        "google_enabled",
        "github_enabled",
        "microsoft_enabled",
    )
    search_fields = ("tenant__slug",)
    fieldsets = (
        (None, {"fields": ("tenant",)}),
        (_("Google"), {"fields": ("google_enabled", "google_client_id", "google_client_secret")}),
        (_("GitHub"), {"fields": ("github_enabled", "github_client_id", "github_client_secret")}),
        (
            _("Microsoft"),
            {"fields": ("microsoft_enabled", "microsoft_client_id", "microsoft_client_secret")},
        ),
    )


@admin.register(MobileOtpToken)
class MobileOtpTokenAdmin(UnfoldModelAdmin):
    list_display = ("mobile", "tenant", "expires_at", "is_used", "attempts", "created_at")
    list_filter = ("is_used",)
    search_fields = ("mobile",)
    readonly_fields = ("mobile", "code", "tenant", "expires_at", "is_used", "attempts", "created_at")
    ordering = ("-created_at",)


@admin.register(OtpSendLog)
class OtpSendLogAdmin(UnfoldModelAdmin):
    list_display = ("mobile", "status", "provider", "tenant", "ip_address", "created_at")
    list_filter = ("status", "provider")
    search_fields = ("mobile", "ip_address")
    readonly_fields = (
        "mobile", "otp_token", "tenant", "provider",
        "status", "error_message", "ip_address", "created_at",
    )
    ordering = ("-created_at",)


@admin.register(UserProfile)
class UserProfileAdmin(UnfoldModelAdmin):
    list_display = ("user", "phone", "job_title", "department", "created_at")
    search_fields = ("user__username", "user__email", "phone", "job_title")
    readonly_fields = ("created_at", "updated_at")
    raw_id_fields = ("user",)
