# -*- coding: utf-8 -*-

from django import forms
from django.conf import settings
from django.contrib.auth.forms import (
    AuthenticationForm, PasswordChangeForm as AuthPasswordChangeForm
)
from django.core.exceptions import ObjectDoesNotExist
from django.utils.translation import ugettext_lazy as _

from extended_choices import Choices
from django_select2.forms import ModelSelect2Widget, ModelSelect2MultipleWidget

from .models import Group, GroupRole, MTPUser
from .models import Avatar
from .utils import validate_extra_secure_password


class PasswordChangeForm(AuthPasswordChangeForm):
    """
    We need to override the default `PasswordChangeForm` to implement the
    reinforced security policy required by our clients.
    """

    def clean(self):
        """
        Validate the password according to the security policy.
        """
        old_password = self.cleaned_data.get('old_password')
        new_password1 = self.cleaned_data.get('new_password1')

        if settings.PASSWORD_EXTRA_SECURITY:
            is_valid, reason, __ = validate_extra_secure_password(
                new_password1, previous=old_password
            )
            if not is_valid:
                raise forms.ValidationError(reason)

        return self.cleaned_data


class EmailPasswordForm(forms.Form):
    email = forms.EmailField()


class UploadFileForm(forms.Form):
    file = forms.FileField(required=True)


class UserImportForm(forms.Form):
    filename = forms.CharField(widget=forms.HiddenInput)


USER_GROUPSET_CHOICES = Choices(
    ('EVALUATOR', 'evaluator', _("the participant can assess the other participants")),
    ('AUTO_EVALUATOR', 'auto_evaluator', _("the participant can assess himself")),
    ('BOTH', 'both', _("the participant can both assess himself and the other participants")),
)

class UserGroupSetForm(forms.Form):
    eval_type = forms.ChoiceField(required=True, choices=USER_GROUPSET_CHOICES,
        widget=forms.RadioSelect, label=_("evaluation type"))


class AvatarForm(forms.ModelForm):
    class Meta:
        model = Avatar
        exclude = ('last_update', 'first_update', 'update_locally', 'order', 'is_deleted', 'hash')


class MTPUserSelectWidget(ModelSelect2Widget):
    """Define a Searchable select widget for the ``MTPUser`` instance"""

    search_fields = [
        'username__icontains',
        'email__icontains',
        'first_name__icontains',
        'last_name__icontains'
    ]
    to_field_name = 'username'


class MTPUserSelectMultipleWidget(ModelSelect2MultipleWidget):
    """Define a Searchable multiple select widget for the ``MTPUser`` instance"""

    search_fields = [
        'username__icontains',
        'email__icontains',
        'first_name__icontains',
        'last_name__icontains'
    ]
    to_field_name = 'username'


class GroupForm(forms.ModelForm):
    """Django form for the ``Group`` model"""

    manager = forms.ModelChoiceField(
        widget=MTPUserSelectWidget(),
        queryset=MTPUser.objects.non_deleted(),
        required=False
    )
    members = forms.ModelMultipleChoiceField(
        widget=MTPUserSelectMultipleWidget(),
        queryset=MTPUser.objects.non_deleted(),
        required=False
    )

    class Meta:
        model = Group
        fields = ('name', 'type', 'members')

    def __init__(self, *args, **kwargs):

        # We try to populate the `members` and `manager` form fields if we are editing an
        # existing model instance.
        if 'instance' in kwargs:
            initial = kwargs.setdefault('initial', {})
            try:
                initial['members'] = [
                    m.pk for m in kwargs['instance'].members.all()
                    if not m == kwargs['instance'].manager_member
                ]
            except AttributeError:
                pass

            try:
                initial['manager'] = kwargs['instance'].manager_member
            except AttributeError:
                pass

        super().__init__(*args, **kwargs)

    def save(self, commit=True):

        # First we save our form as usual.
        instance = forms.ModelForm.save(self, False)

        # Then we need to save the group members, which are "on the other
        # side" of the M2M relationship.

        # So we redefine the `save_m2m` method.
        old_save_m2m = self.save_m2m
        def save_m2m():
            old_save_m2m()
            instance.members.clear()
            [
                GroupRole.objects.create(
                    user=member,
                    group=instance
                )
                for member in self.cleaned_data['members']
                if not member == self.cleaned_data['manager']
            ]
            if self.cleaned_data['manager']:
                # If we get a manager, we must remove the old one first
                try:
                    GroupRole.objects.get(
                        group=instance,
                        role=GroupRole.ROLES.MANAGER
                    ).delete()
                except ObjectDoesNotExist:
                    pass

                # And add the new manager
                GroupRole.objects.create(
                    group=instance,
                    role=GroupRole.ROLES.MANAGER,
                    user=self.cleaned_data['manager']
                )
        self.save_m2m = save_m2m

        # Then we use it if necessary.
        if commit:
            instance.save()
            self.save_m2m()

        return instance


class BaseGroupField:
    """Base class to use for the field related to ``Group`` instances"""

    search_fields = [
        'name__icontains',
    ]
    to_field_name = 'name'


class GroupSelectMultipleWidget(BaseGroupField, ModelSelect2MultipleWidget):
    """Field to select multiple ``Group`` instances"""


class GroupSelectWidget(BaseGroupField, ModelSelect2Widget):
    """Field to select ``Group`` instances"""


class UserForm(forms.ModelForm):
    """Form for the ``mtp.apps.accounts.models.MTPUser`` model"""

    pass1 = forms.CharField(widget=forms.PasswordInput, label=_("Password"), required=False)
    pass2 = forms.CharField(widget=forms.PasswordInput, label=_("Confirm password"),
                            required=False)
    mgroups = forms.ModelMultipleChoiceField(queryset=Group.objects.non_deleted(),
                                             required=False, label=_('Business units'))

    class Meta:
        model = MTPUser

        fields = (
            'username',
            'first_name',
            'last_name',
            'email',
            'natural_language',
            'mgroups'
        )

    def __init__(self, *args, **kwargs):
        """Get the current user and set the queryset for the editable groups"""

        current_user = kwargs.pop('current_user')

        super().__init__(*args, **kwargs)

        # The current user can only see / add / remove the groups he manage
        self.editable_groups_qs = Group.objects.non_deleted().managed_by(current_user)
        self.fields['mgroups'].queryset = self.editable_groups_qs

    def save(self, commit=True):
        """Save the user and his associated group roles"""

        # First we save our form as usual.
        instance = super().save(commit=False)

        # Then we need to save the groups of the user, so we redefine the save_m2m
        def save_m2m():
            # So, we first remove all the managed groups from the user...
            GroupRole.objects.filter(role=GroupRole.ROLES.MEMBER, user=instance,
                                     group__pk__in=self.editable_groups_qs.values_list(
                                         'pk', flat=True)
                                     ).delete()

            # ...and create the new ones
            [
                GroupRole.objects.create(
                    group=group,
                    user=instance
                )
                for group in self.cleaned_data['mgroups']
                if not group.manager_member == instance
            ]
        self.save_m2m = save_m2m

        if commit:
            instance.save()
            self.save_m2m()

        return instance

    def clean_username(self):
      username = self.cleaned_data.get("username")
      username = username.lower().strip()
      return username

    def clean_pass1(self):
        password1 = self.cleaned_data.get('pass1')
        if len(password1) < 6 and len(password1) != 0:
            raise forms.ValidationError("The password must contain at least 6 characters.")

        return password1

    def clean_pass2(self):
        password1 = self.cleaned_data.get('pass1')
        password2 = self.cleaned_data.get('pass2')
        if password1 and password1 != password2:
            raise forms.ValidationError("The passwords don't match.")

        return password2


class MTPAuthenticationForm(AuthenticationForm):
    """Authenticated form subclass for the MTP platform

    This subclass deactivate the authentication of the users who belongs
    to the ``trainee`` group.

    """

    trainee_error_message = _('Log in not allowed, please log in on the trainee interface.')

    def confirm_login_allowed(self, user):
        """Disallow the trainee to log in"""

        super().confirm_login_allowed(user)

        if user.is_trainee():
            raise forms.ValidationError(
                self.trainee_error_message,
                code='is_trainee'
            )
