# Copyright 2010-2017, The University of Melbourne
# Copyright 2010-2017, Brian May
#
# This file is part of Karaage.
#
# Karaage is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Karaage is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Karaage  If not, see <http://www.gnu.org/licenses/>.

import ajax_select.fields
import six
from django import forms
from django.conf import settings
from django.contrib.admin.widgets import AdminDateWidget
from django.contrib.auth.forms import SetPasswordForm as BaseSetPasswordForm

from karaage.common import get_current_person
from karaage.common.constants import COUNTRIES
from karaage.common.forms import (
    clean_email,
    validate_password,
    validate_phone_number,
)
from karaage.institutes.models import Institute
from karaage.people.models import Group, Person
from karaage.people.utils import (
    UsernameException,
    validate_username_for_new_person,
)
from karaage.projects.models import Project
from karaage.projects.utils import add_user_to_project


class PersonForm(forms.ModelForm):
    #    title = forms.ChoiceField(choices=TITLES, required=False)
    #    position = forms.CharField(required=False)
    #    email = forms.EmailField()
    #    department = forms.CharField(required=False)
    #    supervisor = forms.CharField(required=False)
    telephone = forms.CharField(
        required=True,
        label=six.u("Office Telephone"),
        help_text=six.u("Used for emergency contact and password reset service."),
        validators=[validate_phone_number],
    )
    mobile = forms.CharField(
        required=False,
        validators=[validate_phone_number],
    )
    fax = forms.CharField(required=False, validators=[validate_phone_number])
    address = forms.CharField(label=six.u("Mailing Address"), required=False, widget=forms.Textarea())
    country = forms.ChoiceField(choices=COUNTRIES, initial="AU", required=False)

    def __init__(self, *args, **kwargs):
        super(PersonForm, self).__init__(*args, **kwargs)
        self.fields[
            "short_name"
        ].help_text = "This is typically the person's given name. For example enter 'Fred' here."
        self.fields[
            "full_name"
        ].help_text = "This is typically the person's full name. For example enter 'Fred Smith' here."

    def clean(self):
        data = super(PersonForm, self).clean()

        for key in [
            "short_name",
            "full_name",
            "email",
            "position",
            "supervisor",
            "department",
            "telephone",
            "mobile",
            "fax",
            "address",
        ]:
            if key in data and data[key]:
                data[key] = data[key].strip()

        return data

    class Meta:
        model = Person
        fields = [
            "short_name",
            "full_name",
            "email",
            "title",
            "position",
            "supervisor",
            "department",
            "telephone",
            "mobile",
            "fax",
            "address",
            "country",
        ]

    def clean_email(self):
        email = self.cleaned_data["email"]
        users = Person.objects.filter(email__exact=email)
        if self.instance:
            users = users.exclude(pk=self.instance.pk)
        if users.count() > 0:
            raise forms.ValidationError(
                six.u("An account with this email already exists. Please email %s") % settings.ACCOUNTS_EMAIL
            )
        clean_email(email)
        return email


class AdminPersonForm(PersonForm):
    institute = forms.ModelChoiceField(queryset=None)
    comment = forms.CharField(widget=forms.Textarea(), required=False)
    expires = forms.DateField(widget=AdminDateWidget, required=False)
    is_admin = forms.BooleanField(help_text="Designates whether the user can log into this admin site.", required=False)
    is_systemuser = forms.BooleanField(
        help_text="Designates that this user is a system process, not a person.", required=False
    )

    def __init__(self, *args, **kwargs):
        super(AdminPersonForm, self).__init__(*args, **kwargs)
        self.fields["institute"].queryset = Institute.active.all()

    class Meta:
        model = Person
        fields = [
            "short_name",
            "full_name",
            "email",
            "title",
            "position",
            "supervisor",
            "department",
            "institute",
            "telephone",
            "mobile",
            "fax",
            "address",
            "country",
            "expires",
            "comment",
            "is_systemuser",
            "is_admin",
        ]


class AddPersonForm(AdminPersonForm):
    project = forms.ModelChoiceField(queryset=None, label=six.u("Default Project"), required=False)
    needs_account = forms.BooleanField(
        required=False,
        label=six.u("Do you require a cluster account"),
        help_text=six.u("eg. Will you be working on the project yourself"),
    )
    username = forms.CharField(
        label=six.u("Requested username"),
        max_length=settings.USERNAME_MAX_LENGTH,
        help_text=(
            settings.USERNAME_VALIDATION_ERROR_MSG + " and has a max length of %s." % settings.USERNAME_MAX_LENGTH
        ),
    )
    password1 = forms.CharField(widget=forms.PasswordInput(render_value=False), label=six.u("Password"))
    password2 = forms.CharField(widget=forms.PasswordInput(render_value=False), label=six.u("Password (again)"))

    def __init__(self, *args, **kwargs):
        super(AddPersonForm, self).__init__(*args, **kwargs)
        self.fields["project"].queryset = Project.objects.all()

    def clean_username(self):
        username = self.cleaned_data["username"]
        try:
            validate_username_for_new_person(username)
        except UsernameException as e:
            raise forms.ValidationError(e.args[0])
        return username

    def clean_password2(self):
        username = self.cleaned_data.get("username")
        password1 = self.cleaned_data.get("password1")
        password2 = self.cleaned_data.get("password2")
        return validate_password(username, password1, password2)

    def save(self, commit=True):
        assert commit is True

        data = self.cleaned_data

        person = super(AddPersonForm, self).save(commit=False)
        person.username = data["username"]
        person.is_admin = data["is_admin"]
        person.is_active = True
        person.approved_by = get_current_person()
        person.set_password(data["password2"])
        person.save()

        if data["needs_account"] and data["project"]:
            add_user_to_project(person, data["project"])

        return person


class AdminPasswordChangeForm(forms.Form):
    new1 = forms.CharField(widget=forms.PasswordInput(), label=six.u("New Password"))
    new2 = forms.CharField(widget=forms.PasswordInput(), label=six.u("New Password (again)"))

    def __init__(self, person, *args, **kwargs):
        self.person = person
        super(AdminPasswordChangeForm, self).__init__(*args, **kwargs)

    def clean_new2(self):
        username = self.person.username
        password1 = self.cleaned_data.get("new1")
        password2 = self.cleaned_data.get("new2")
        return validate_password(username, password1, password2)

    def save(self):
        data = self.cleaned_data
        person = self.person
        person.set_password(data["new1"])
        person.save()


class PasswordChangeForm(AdminPasswordChangeForm):
    old = forms.CharField(widget=forms.PasswordInput(), label="Old password")

    def clean_new2(self):
        username = self.person.username
        password1 = self.cleaned_data.get("new1")
        password2 = self.cleaned_data.get("new2")
        old_password = self.cleaned_data.get("old", None)
        return validate_password(username, password1, password2, old_password)

    def clean_old(self):
        person = Person.objects.authenticate(username=self.person.username, password=self.cleaned_data["old"])
        if person is None:
            raise forms.ValidationError(six.u("Your old password was incorrect"))

        return self.cleaned_data["old"]


class SetPasswordForm(BaseSetPasswordForm):
    def clean_new_password1(self):
        password1 = self.cleaned_data.get("new_password1")
        return validate_password(self.user.username, password1)


class AdminGroupForm(forms.Form):
    name = forms.RegexField(
        "^%s$" % settings.GROUP_VALIDATION_RE,
        required=True,
        error_messages={"invalid": settings.GROUP_VALIDATION_ERROR_MSG},
    )
    description = forms.CharField()

    def __init__(self, *args, **kwargs):
        self.instance = kwargs.pop("instance", None)
        super(AdminGroupForm, self).__init__(*args, **kwargs)
        if self.instance is not None:
            self.initial = self.instance.__dict__

    def clean_name(self):
        name = self.cleaned_data["name"]
        groups = Group.objects.filter(name=name)
        if self.instance is not None:
            groups = groups.exclude(pk=self.instance.pk)
        if groups.count() > 0:
            raise forms.ValidationError("That group name already exists.")
        return name

    def save(self, group=None):
        data = self.cleaned_data

        if self.instance is None:
            group = Group()
        else:
            group = self.instance

        group.name = data["name"]
        group.description = data["description"]
        group.save()

        return group


class AddGroupMemberForm(forms.Form):

    """Add a user to a group form"""

    person = ajax_select.fields.AutoCompleteSelectField("person", required=True, label="Add person")

    def __init__(self, *args, **kwargs):
        self.instance = kwargs.pop("instance", None)
        super(AddGroupMemberForm, self).__init__(*args, **kwargs)

    def save(self, commit=True):
        person = self.cleaned_data["person"]
        self.instance.add_person(person)
        return self.instance
