from __future__ import unicode_literals

from datetime import timedelta

from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
from django.core.exceptions import ValidationError
from django.contrib.auth.models import AbstractUser as _AbstractUser, UserManager as _UserManager

from doorstep.exceptions import DoorstepError


class UserManager(_UserManager):

    def register(self, first_name, last_name, email, gender, username, password, is_verified=False, **extra_fields):
        """
        Creates a new user in database, and also marked first user as staff and superuser
        """
        # Is verified can be later use to verify user email address
        verify_code = None
        if not is_verified:
            verify_code = self.make_random_password(length=20)

        if self.filter(email__iexact=email).count() > 0:
            raise ValidationError("User with this Email address already exists.")

        # First user will automatically become super user and staff member
        if self.count() == 0:
            user = self.create_superuser(username=username,
                                         first_name=first_name,
                                         last_name=last_name,
                                         email=email,
                                         password=password,
                                         gender=gender,
                                         is_verified=is_verified,
                                         verify_code=verify_code,
                                         updated_by=username,
                                         created_by=username,
                                         **extra_fields)
        else:
            user = self.create_user(username=username,
                                    first_name=first_name,
                                    last_name=last_name,
                                    email=email,
                                    password=password,
                                    gender=gender,
                                    is_verified=is_verified,
                                    verify_code=verify_code,
                                    updated_by=username,
                                    created_by=username,
                                    **extra_fields)

        return user

    def get_reset_code(self, email):
        """
        Generates a new password reset code returns user
        """

        try:
            user = self.get(email__iexact=email)
            user.reset_code = self.make_random_password(length=20)
            user.reset_code_expire = timezone.now() + timedelta(days=2)
            user.save()

            return user
        except get_user_model().DoesNotExist:
            raise DoorstepError('We can\'t find that email address, sorry!')

    def reset_password(self, user_id, reset_code, password):
        """
        Set new password for the user
        """

        if not password:
            raise DoorstepError('New password can\'t be blank.')

        try:
            user = self.get(id=user_id)
            if not user.reset_code or user.reset_code != reset_code or user.reset_code_expire < timezone.now():
                raise DoorstepError('Password reset code is invalid or expired.')

            # Password reset code shouldn't be used again
            user.reset_code = None
            user.set_password(password)
            user.save()

        except get_user_model().DoesNotExist:
            raise DoorstepError('Password reset code is invalid or expired.')

    def change_password(self, user, current_password, password):
        """
        Updates user's current password
        """

        if not password:
            raise DoorstepError('New password can\'t be blank.')

        # Changing user's password if old password verifies
        user = self.get(id=user.id)

        if not user.check_password(current_password):
            raise DoorstepError('Your current password is wrong.')

        user.set_password(password)
        user.save()


class AbstractUser(_AbstractUser):
    """
    An abstract class extending Django authentication user model for Doorstep.
    """
    MALE = 'M'
    FEMALE = 'F'
    GENDERS = ((MALE, 'Male'),
               (FEMALE, 'Female'))

    birth_date = models.DateField(null=True, blank=True)
    gender = models.CharField(max_length=1, choices=GENDERS, default=None)
    is_verified = models.BooleanField(default=True)
    verify_code = models.CharField(max_length=512, blank=True, null=True,
                                   help_text='User account verification code.', editable=False)
    reset_code = models.CharField(max_length=512, blank=True, null=True,
                                  help_text='Password reset code.', editable=False)
    reset_code_expire = models.DateTimeField(max_length=512, blank=True, null=True,
                                             help_text='Password reset code expire date.', editable=False)
    updated_on = models.DateTimeField(auto_now=True)
    updated_by = models.CharField(max_length=100)
    created_on = models.DateTimeField(auto_now_add=True)
    created_by = models.CharField(max_length=100)

    objects = UserManager()

    REQUIRED_FIELDS = ['email', 'gender', 'updated_by', 'created_by']

    class Meta:
        abstract = True

    @classmethod
    def get_by_username(cls, username):
        """
        Returns user for specified username or raised DoesNotExist exception
        """
        return cls.objects.get(username__iexact=username)


class User(AbstractUser):
    """
    Extends Django authentication user model for Doorstep.
    """

from __future__ import unicode_literals

from datetime import timedelta

from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
from django.core.exceptions import ValidationError
from django.contrib.auth.models import AbstractUser as _AbstractUser, UserManager as _UserManager

from doorstep.exceptions import DoorstepError


class UserManager(_UserManager):

    def register(self, first_name, last_name, email, gender, username, password, is_verified=False, **extra_fields):
        """
        Creates a new user in database, and also marked first user as staff and superuser
        """
        # Is verified can be later use to verify user email address
        verify_code = None
        if not is_verified:
            verify_code = self.make_random_password(length=20)

        if self.filter(email__iexact=email).count() > 0:
            raise ValidationError("User with this Email address already exists.")

        # First user will automatically become super user and staff member
        if self.count() == 0:
            user = self.create_superuser(username=username,
                                         first_name=first_name,
                                         last_name=last_name,
                                         email=email,
                                         password=password,
                                         gender=gender,
                                         is_verified=is_verified,
                                         verify_code=verify_code,
                                         updated_by=username,
                                         created_by=username,
                                         **extra_fields)
        else:
            user = self.create_user(username=username,
                                    first_name=first_name,
                                    last_name=last_name,
                                    email=email,
                                    password=password,
                                    gender=gender,
                                    is_verified=is_verified,
                                    verify_code=verify_code,
                                    updated_by=username,
                                    created_by=username,
                                    **extra_fields)

        return user

    def get_reset_code(self, email):
        """
        Generates a new password reset code returns user
        """

        try:
            user = self.get(email__iexact=email)
            user.reset_code = self.make_random_password(length=20)
            user.reset_code_expire = timezone.now() + timedelta(days=2)
            user.save()

            return user
        except get_user_model().DoesNotExist:
            raise DoorstepError('We can\'t find that email address, sorry!')

    def reset_password(self, user_id, reset_code, password):
        """
        Set new password for the user
        """

        if not password:
            raise DoorstepError('New password can\'t be blank.')

        try:
            user = self.get(id=user_id)
            if not user.reset_code or user.reset_code != reset_code or user.reset_code_expire < timezone.now():
                raise DoorstepError('Password reset code is invalid or expired.')

            # Password reset code shouldn't be used again
            user.reset_code = None
            user.set_password(password)
            user.save()

        except get_user_model().DoesNotExist:
            raise DoorstepError('Password reset code is invalid or expired.')

    def change_password(self, user, current_password, password):
        """
        Updates user's current password
        """

        if not password:
            raise DoorstepError('New password can\'t be blank.')

        # Changing user's password if old password verifies
        user = self.get(id=user.id)

        if not user.check_password(current_password):
            raise DoorstepError('Your current password is wrong.')

        user.set_password(password)
        user.save()


class AbstractUser(_AbstractUser):
    """
    An abstract class extending Django authentication user model for Doorstep.
    """
    MALE = 'M'
    FEMALE = 'F'
    GENDERS = ((MALE, 'Male'),
               (FEMALE, 'Female'))

    birth_date = models.DateField(null=True, blank=True)
    gender = models.CharField(max_length=1, choices=GENDERS, default=None)
    is_verified = models.BooleanField(default=True)
    verify_code = models.CharField(max_length=512, blank=True, null=True,
                                   help_text='User account verification code.', editable=False)
    reset_code = models.CharField(max_length=512, blank=True, null=True,
                                  help_text='Password reset code.', editable=False)
    reset_code_expire = models.DateTimeField(max_length=512, blank=True, null=True,
                                             help_text='Password reset code expire date.', editable=False)
    updated_on = models.DateTimeField(auto_now=True)
    updated_by = models.CharField(max_length=100)
    created_on = models.DateTimeField(auto_now_add=True)
    created_by = models.CharField(max_length=100)

    objects = UserManager()

    REQUIRED_FIELDS = ['email', 'gender', 'updated_by', 'created_by']

    class Meta:
        abstract = True

    @classmethod
    def get_by_username(cls, username):
        """
        Returns user for specified username or raised DoesNotExist exception
        """
        return cls.objects.get(username__iexact=username)


class User(AbstractUser):
    """
    Extends Django authentication user model for Doorstep.
    """

Creates a new user in database, and also marked first user as staff and superuser # Is verified can be later use to verify user email address # First user will automatically become super user and staff member Generates a new password reset code returns user Set new password for the user # Password reset code shouldn't be used again Updates user's current password # Changing user's password if old password verifies An abstract class extending Django authentication user model for Doorstep. Returns user for specified username or raised DoesNotExist exception Extends Django authentication user model for Doorstep.