from django import forms
from django.conf import settings
from django.contrib.auth.password_validation import validate_password
from django.utils.translation import gettext_lazy as _

from oscar.apps.checkout.forms import ShippingAddressForm as BaseShippingAddressForm
from oscar.apps.customer.forms import EmailUserCreationForm
from oscar.core.loading import get_model

from ocyan.core.fender import config

from .fields import MethodsChoiceField, StandardsCompliantRadioWidget

Country = get_model("address", "Country")


class ChooseShippingAddressForm(forms.Form):
    address_chooser = forms.ChoiceField(
        choices=[], required=False, label=_("Address chooser")
    )

    def __init__(self, request=None, default_address=None, *args, **kwargs):  # pylint: disable=W1113
        super().__init__(*args, **kwargs)
        if request is not None:
            user = request.user
            if user.is_authenticated:
                addresses = []

                for address in self.get_addresses(user):
                    addresses.append((address.pk, address))

                self.fields["address_chooser"].choices = addresses
                if default_address is not None:
                    self.fields["address_chooser"].initial = (
                        default_address["id"],
                        str(default_address),
                    )

    def get_addresses(self, user):
        return user.addresses.filter(country__is_shipping_country=True).order_by(
            "-is_default_for_shipping",
            "-num_orders_as_shipping_address",
            "-date_created",
        )


class ChooseBillingAddressForm(ChooseShippingAddressForm):
    def get_addresses(self, user):
        return user.addresses.order_by(
            "-is_default_for_billing", "-num_orders_as_billing_address", "-date_created"
        )


class PasswordCheckForm(forms.Form):
    password = forms.CharField(
        label=_("Password"), widget=forms.PasswordInput, required=False
    )
    password2 = forms.CharField(label=_("Confirm password"), widget=forms.PasswordInput)

    def clean_password2(self):
        password1 = self.cleaned_data.get("password", "")
        password2 = self.cleaned_data.get("password2", "")
        if password1 != password2:
            raise forms.ValidationError(_("The two password fields didn't match."))
        validate_password(password2)
        return password2

    def user_create_form(self, email):
        data = {
            "email": email,
            "password1": self.cleaned_data["password"],
            "password2": self.cleaned_data["password2"],
        }
        form = EmailUserCreationForm(data=data)
        if form.is_valid():
            return form
        for error in form.errors.values():
            self.add_error("__all__", error)
        return None


class SelectBillingAddressForm(forms.Form):
    require_billing_address = forms.BooleanField(
        label=_("Bill To Different Address"), required=False
    )


class ShippingMethodForm(forms.Form):
    method_code = MethodsChoiceField(
        label=_("Shipping method"), widget=StandardsCompliantRadioWidget()
    )

    def __init__(self, *args, **kwargs):
        self.methods = methods = kwargs.pop("methods", ())
        default = kwargs.pop("default", None)
        super(ShippingMethodForm, self).__init__(*args, **kwargs)
        method_code_field = self.fields["method_code"]
        method_code_field.initial = default
        method_code_field.methods = methods
        method_code_field.choices = ((m.code, m.name) for m in methods)
        if (  # if shipping has nothing to do with address, hide if possible
            config.get("shipping", "enable_free_shipping", False)
            and not (
                config.get("shipping", "enable_charged_shipping", False)
                or config.get("shipping", "enable_weightbased_shipping", False)
            )
            and len(methods) == 1
        ):
            method_code_field.widget = forms.HiddenInput()
            method_code_field.initial = methods[0].code

    @property
    def method(self):
        return next(
            (
                method
                for method in self.methods
                if self.cleaned_data["method_code"] == method.code
            )
        )


class ShippingAddressForm(BaseShippingAddressForm):
    field_order = settings.OSCAR_SHIPPING_FORM_FIELD_ORDER

    def adjust_country_field(self):
        countries = Country._default_manager.filter(  # pylint: disable=W0212
            is_shipping_country=True
        )

        # No need to show country dropdown if there is only one option
        if len(countries) == 1:
            self.fields["country"].widget = forms.HiddenInput()
            self.fields["country"].initial = self.instance.country = countries.get()
        else:
            self.fields["country"].queryset = countries
            self.fields["country"].empty_label = None

    class Meta(BaseShippingAddressForm.Meta):
        fields = settings.OSCAR_SHIPPING_FORM_FIELDS


class PaymentMethodForm(forms.Form):
    payment_method = forms.ChoiceField(choices={}, widget=forms.RadioSelect())

    def __init__(self, *args, **kwargs):
        payment_method_choices = kwargs.pop("payment_method_choices", None)
        super(PaymentMethodForm, self).__init__(*args, **kwargs)
        if payment_method_choices is not None:
            payment_method_field = self.fields["payment_method"]
            payment_method_field.choices = payment_method_choices

    @property
    def method(self):
        return self.cleaned_data["payment_method"]
