from __future__ import annotations

from django import forms
from django.contrib.auth import get_user_model
from django.contrib.auth.forms import PasswordChangeForm, PasswordResetForm, SetPasswordForm, UserCreationForm
from django.contrib.auth.password_validation import validate_password
from django.db.models import Q

User = get_user_model()


class BaseStyledFormMixin:
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field_name, field in self.fields.items():
            widget = field.widget
            if isinstance(widget, forms.CheckboxInput):
                widget.attrs.setdefault("class", "form-check-input")
            elif isinstance(widget, forms.Select):
                widget.attrs.setdefault("class", "form-select")
            else:
                widget.attrs.setdefault("class", "form-control")
            widget.attrs.setdefault("autocomplete", "off")
            widget.attrs.setdefault("aria-label", field.label)
            if field.required:
                widget.attrs.setdefault("required", "required")


class UserIdentityFieldsMixin:
    def _configure_identity_fields(self):
        if "first_name" in self.fields:
            self.fields["first_name"].widget.attrs["autocomplete"] = "given-name"
        if "last_name" in self.fields:
            self.fields["last_name"].widget.attrs["autocomplete"] = "family-name"
        if "email" in self.fields:
            self.fields["email"].widget.attrs["autocomplete"] = "username"

    def clean_email(self):
        email = self.cleaned_data["email"].strip().lower()
        queryset = User.objects.filter(Q(email__iexact=email) | Q(username__iexact=email))
        if getattr(self, "instance", None) and getattr(self.instance, "pk", None):
            queryset = queryset.exclude(pk=self.instance.pk)
        if queryset.exists():
            raise forms.ValidationError("This email address is already in use.")
        return email


class RegistrationForm(BaseStyledFormMixin, UserIdentityFieldsMixin, UserCreationForm):
    class Meta:
        model = User
        fields = [
            "first_name",
            "last_name",
            "email",
            "role",
            "password1",
            "password2",
        ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._configure_identity_fields()
        self.fields["password1"].widget.attrs["autocomplete"] = "new-password"
        self.fields["password2"].widget.attrs["autocomplete"] = "new-password"

    def save(self, commit=True):
        user = super().save(commit=False)
        user.email = self.cleaned_data["email"].lower()
        user.username = user.email
        user.is_active = True
        user.is_staff = False
        user.is_superuser = False
        if commit:
            user.save()
        return user


class AdminUserUpdateForm(BaseStyledFormMixin, UserIdentityFieldsMixin, forms.ModelForm):
    password1 = forms.CharField(
        required=False,
        label="Password",
        widget=forms.PasswordInput(render_value=False),
        help_text="Leave blank to keep the current password.",
    )
    password2 = forms.CharField(
        required=False,
        label="Confirm Password",
        widget=forms.PasswordInput(render_value=False),
    )

    class Meta:
        model = User
        fields = [
            "first_name",
            "last_name",
            "email",
            "role",
            "is_active",
        ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._configure_identity_fields()
        self.fields["is_active"].help_text = "Inactive users cannot sign in."
        self.fields["password1"].widget.attrs["autocomplete"] = "new-password"
        self.fields["password2"].widget.attrs["autocomplete"] = "new-password"

    def clean(self):
        cleaned_data = super().clean()
        password1 = cleaned_data.get("password1") or ""
        password2 = cleaned_data.get("password2") or ""

        if password1 or password2:
            if not password1:
                self.add_error("password1", "Enter a new password.")
            if not password2:
                self.add_error("password2", "Confirm the new password.")
            if password1 and password2 and password1 != password2:
                self.add_error("password2", "The two password fields didn’t match.")
            if password1 and password2 and password1 == password2:
                validate_password(password1, self.instance)

        return cleaned_data

    def save(self, commit=True):
        user = super().save(commit=False)
        user.email = self.cleaned_data["email"].lower()
        user.username = user.email
        password = self.cleaned_data.get("password1")
        if password:
            user.set_password(password)
        if commit:
            user.save()
        return user


class SignInForm(BaseStyledFormMixin, forms.Form):
    identifier = forms.CharField(max_length=150, label="Email")
    password = forms.CharField(widget=forms.PasswordInput(render_value=False), label="Password")
    remember_me = forms.BooleanField(required=False, label="Remember me on this device")

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["identifier"].widget.attrs["autocomplete"] = "username"
        self.fields["password"].widget.attrs["autocomplete"] = "current-password"


class OTPVerificationForm(BaseStyledFormMixin, forms.Form):
    otp_code = forms.CharField(max_length=6, min_length=6, label="One-time passcode")

    def clean_otp_code(self):
        otp_code = self.cleaned_data["otp_code"].strip()
        if not otp_code.isdigit():
            raise forms.ValidationError("Enter a 6-digit numeric OTP code.")
        return otp_code


class StyledPasswordResetForm(BaseStyledFormMixin, PasswordResetForm):
    pass


class StyledSetPasswordForm(BaseStyledFormMixin, SetPasswordForm):
    pass


class StyledPasswordChangeForm(BaseStyledFormMixin, PasswordChangeForm):
    pass
