import random
import string
import xlrd

from django.contrib.auth import get_user_model
from django.utils.translation import ugettext as _

from mtp.apps.common.notifications.tasks import send_async

from .constants import (
    INTERNAL_GROUP_TRAINEE,
    INTERNAL_GROUP_TRAINER,
    INTERNAL_GROUP_WHOSWHO,
)
from .models import Group, GroupRole, GroupSet


def generate_password():
    characters = string.ascii_letters + string.digits + string.punctuation
    password = ''.join(random.choice(characters) for x in range(8))
    return password


def validate_extra_secure_password(password, previous=None):
    """
    Make sure the password is valid according to the extra security policy.

    Return:
    - is_valid: boolean
    - msg: string, the error message, or None
    - code: string, the error code, or None
    """

    # Password must be at least 8 characters long.
    if len(password) < 8:
        msg = _(u"Your password must contain at least 8 characters.")
        code = 'invalid-length'
        return False, msg, code

    # Password must contain at least:
    # - an uppercase letter,
    # - a lowercase letter,
    # - a digit,
    # - a special character.

    REQUIRED_CHARACTER_GROUPS = (
        string.ascii_uppercase,
        string.ascii_lowercase,
        string.digits,
        string.punctuation,
    )
    for character_group in REQUIRED_CHARACTER_GROUPS:
        if not len(set(character_group).intersection(password)):
            msg = _(
                u"Your new password must contain at least one uppercase letter, "
                u"one lowercase letter, one digit and one special character."
            )
            code = 'missing-characters'
            return False, msg, code

    # Password must be different from the previous password.
    if password == previous:
        msg = _(u"Your password must be different from your previous one.")
        code = 'identical-previous'
        return False, msg, code

    # Password is valid.
    return True, None, None


def init_user(user, password, groupset, is_staff):
    if groupset:
        # We cannot use ``bulk_create`` because it does not call the ``save`` method and the
        # ``Syncable`` models need to call the ``save`` method for each update.
        for group in groupset.mgroups.all():
            GroupRole.objects.create(user=user, group=group)

    whoswho_group = Group.objects.get(name=INTERNAL_GROUP_WHOSWHO)
    GroupRole.objects.create(user=user, group=whoswho_group)

    if is_staff:
        trainer_group = Group.objects.get(name=INTERNAL_GROUP_TRAINER)
        GroupRole.objects.create(user=user, group=trainer_group)
        user.is_staff = True
    else:
        trainee_group = Group.objects.get(name=INTERNAL_GROUP_TRAINEE)
        GroupRole.objects.create(user=user, group=trainee_group)

    if not password or password == "":
        password = generate_password()

    user.set_password(password)
    user.save()

    return user, password


def xls_import_trainee(file, commit=False):
    trainee_reader = xlrd.open_workbook(filename=file)
    first_sheet_name = trainee_reader.sheet_names()[0]
    first_sheet = trainee_reader.sheet_by_name(first_sheet_name)
    nrows = first_sheet.nrows
    if nrows < 2:
        return

    users = []

    for row in range(1, nrows):
        row_values = first_sheet.row_values(row)
        row_types = first_sheet.row_types(row)

        username = row_values[0].strip()
        first_name = row_values[1].title()
        last_name = row_values[2].upper().strip()
        email = row_values[3].strip().lower()

        try:
            if row_types[4] == 2:
                if row_values[4].is_integer():
                    password = str(int(row_values[4]))
                else:
                    password = str(row_values[4])

            elif row_types[4] == 1:
                password = row_values[4]

            else:
                password = '123456'

        except:
            password = '123456'

        try:
            groupset_name = row_values[5].strip()
            groupset = GroupSet.objects.get(name=row_values[5].strip())
        except IndexError:
            groupset = None
        # TODO: Tell the user the groupset dont exist with a nice message :)
        except GroupSet.DoesNotExist:
            groupset = None

        new_trainee = False

        try:
            user = get_user_model().objects.get(username=username)
            user.first_name = first_name
            user.last_name = last_name
            user.email = email
        except get_user_model().DoesNotExist:
            user = get_user_model()(
                username=username,
                first_name=first_name,
                last_name=last_name,
                email=email
            )
            new_trainee = True

        users.append(user)

        if commit:
            user.is_deleted = False
            user.save()
            init_user(user, password, groupset, False)
            if new_trainee:
                send_async.delay([user], 'user_welcome', {
                    'password': password
                })

    return users
