from django import forms
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.forms import PasswordChangeForm \
    as BasePasswordChangeForm
from django.contrib.auth import authenticate
from django.contrib.auth.password_validation import validate_password
from password_reset.forms import PasswordRecoveryForm \
    as BasePasswordRecoveryForm
from password_reset.forms import PasswordResetForm as BasePasswordResetForm
from .models import User, get_woo_connection
from captcha.fields import CaptchaField


class PasswordRecoveryForm(BasePasswordRecoveryForm):

    def _validate_domain(self, value):
        (user, domain) = value.split('@')
        if domain != settings.SITE_DOMAIN:
            raise ValidationError(
                _('Only @{} addresses are allowed'.format(
                    settings.SITE_DOMAIN)),
                params={'value': value},
                )

    def __init__(self, *args, **kwargs):
        super(PasswordRecoveryForm, self).__init__(*args, **kwargs)
        self.fields['username_or_email'] = forms.EmailField(
            label=_('Enter your {} address'.format(settings.SITE_TITLE)),
            validators=[self._validate_domain],
        )

    # Override clean_username_or_email to search for username even
    # when we are expecting email address in the form filed.
    def clean_username_or_email(self):
        email = self.cleaned_data['username_or_email']
        username = email.split('@')[0]
        user_error = forms.ValidationError(
            _('That account is invalid or was not found.'), code='not_found'
        )
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise user_error
        if not user.is_active:
            raise user_error

        self.cleaned_data['user'] = user
        return username


class CaptchaPasswordRecoveryForm(PasswordRecoveryForm):
    captcha = CaptchaField(
        label=_('Please solve this math problem'),
        help_text=_('Prove you are not a robot. Solve this addition,'
                    ' subtraction or multiplication problem.'),
    )


class RecoveryPasswordResetForm(BasePasswordResetForm):

    def __init__(self, *args, **kwargs):
        super(RecoveryPasswordResetForm, self).__init__(*args, **kwargs)
        self.fields['password1'] = forms.CharField(
            label=_('New password'),
            widget=forms.PasswordInput,
            validators=[validate_password],
        )


class PasswordChangeForm(BasePasswordChangeForm):

    # Override clean_old_password() to use authenticate which uses
    # LDAP authentication
    def clean_old_password(self):
        old_password = self.cleaned_data["old_password"]
        if not authenticate(
                request=None,
                username=self.user.get_username(),
                password=old_password
        ):
            raise forms.ValidationError(
                self.error_messages['password_incorrect'],
                code='password_incorrect',
            )
        return old_password

    # Override save() to change password in LDAP and WooCommerce.  We
    # don't really use commit as the new password is always stored.
    def save(self, commit=True):
        password = self.cleaned_data["new_password1"]
        self.user.set_ldap_password(password)
        self.user.set_woocommerce_password(raw_password=password,
                                           woocommerce_id=None)
        return self.user


class ProfileConfigureForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ['email', 'billing_email']
        labels = {
            'email': _('Recovery email'),
            'billing_email': _('Billing email'),
        }
        widgets = {
            'email': forms.EmailInput(attrs={'size': '100%'}),
            'billing_email': forms.EmailInput(attrs={'size': '100%'}),
        }

    def clean_email(self):
        new_eml = self.cleaned_data['email']
        woo = get_woo_connection()
        query = "customers?role=all&limit=1&email=" + new_eml
        woo_response = woo.get(query)
        total_emails = int(woo_response.headers['X-WP-Total'])
        if total_emails > 1 or \
            (total_emails == 1 and woo_response.json()[0]['username'] != self.user.get_identity()):
            raise ValidationError(_('Sorry, that email is already in use.'), code='already_taken')
        return new_eml

    def save(self, commit=True):
        self.instance.set_woocommerce_billing_email()
        return super(ProfileConfigureForm, self).save(commit)


class ConfirmAddressForm(forms.Form):

    address = forms.EmailField(
        label=_('If you are sure, please type in your {} address and '
                'confirm:'.format(settings.SITE_TITLE)),
    )

    def clean_address(self):
        address = self.cleaned_data['address']
        if self.user.get_identity() != address:
            raise forms.ValidationError(_('The address provided is not the '
                                          'identity of the current user.'))
        return address
