from django.contrib.auth.models import BaseUserManager
from django.contrib.sites.models import Site
from django.core import checks
from django.db import models
from django.utils import timezone
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _

from user_management.utils.notifications import (
    PasswordResetNotification,
    ValidationNotification,
)


class UserManager(BaseUserManager):
    """Django requires user managers to have create_user & create_superuser."""
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError(_('The given email address must be set'))
        email = self.normalize_email(email).lower()
        user = self.model(
            email=email,
            last_login=timezone.now(),
            **extra_fields
        )
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_superuser(self, email, password, **extra_fields):
        fields = {
            'is_staff': True,
            'is_superuser': True,
        }
        fields.update(extra_fields)
        user = self.create_user(email, password, **fields)
        return user

    def get_by_natural_key(self, email):
        """Get user by email with case-insensitive exact match.

        `get_by_natural_key` is used to `authenticate` a user, see:
        https://github.com/django/django/blob/c5780adeecfbd85a80b5aa7130dd86e78b23e497/django/contrib/auth/backends.py#L16
        """
        return self.get(email__iexact=email)


class DateJoinedUserMixin(models.Model):
    date_joined = models.DateTimeField(
        verbose_name=_('date joined'),
        default=timezone.now,
        editable=False,
    )

    class Meta:
        abstract = True


class EmailUserMixin(models.Model):
    email = models.EmailField(
        verbose_name=_('Email address'),
        unique=True,
        max_length=511,
    )
    email_verification_required = False

    objects = UserManager()

    USERNAME_FIELD = 'email'

    class Meta:
        abstract = True


class IsStaffUserMixin(models.Model):
    is_staff = models.BooleanField(_('staff status'), default=False)

    class Meta:
        abstract = True


@python_2_unicode_compatible
class NameUserMethodsMixin:
    def get_full_name(self):
        return self.name

    def get_short_name(self):
        return self.name

    def __str__(self):
        return self.name


class NameUserMixin(NameUserMethodsMixin, models.Model):
    name = models.CharField(
        verbose_name=_('Name'),
        max_length=255,
    )
    REQUIRED_FIELDS = ['name']

    class Meta:
        abstract = True
        ordering = ['name']


class BasicUserFieldsMixin(
        DateJoinedUserMixin, EmailUserMixin, IsStaffUserMixin, NameUserMixin):
    class Meta:
        abstract = True


class ActiveUserMixin(models.Model):
    is_active = models.BooleanField(_('active'), default=True)

    class Meta:
        abstract = True


class VerifyEmailManager(UserManager):
    def create_superuser(self, email, password, **extra_fields):
        fields = {
            'is_active': True,
        }
        fields.update(extra_fields)
        user = super(VerifyEmailManager, self).create_superuser(
            email,
            password,
            **fields)
        return user


class EmailVerifyUserMethodsMixin:
    """
    Define how validation and password reset emails are sent.

    `password_reset_notification` and `validation_notification` can be overriden to
    provide custom settings to send emails.
    """
    password_reset_notification = PasswordResetNotification
    validation_notification = ValidationNotification

    def send_validation_email(self):
        """Send a validation email to the user's email address."""
        if not self.email_verification_required:
            raise ValueError(_('Cannot validate already active user.'))

        site = Site.objects.get_current()
        notification = self.validation_notification(
            user=self,
            site=site,
        )
        notification.notify()

    def send_password_reset(self):
        """Send a password reset to the user's email address."""
        site = Site.objects.get_current()
        notification = self.password_reset_notification(
            user=self,
            site=site,
        )
        notification.notify()


class EmailVerifyUserMixin(EmailVerifyUserMethodsMixin, models.Model):
    is_active = models.BooleanField(_('active'), default=False)
    email_verification_required = models.BooleanField(
        _('Email verification required?'),
        default=True,
        help_text=_('Indicates if the email address needs to be verified.'))

    objects = VerifyEmailManager()

    class Meta:
        abstract = True

    @classmethod
    def check(cls, **kwargs):
        errors = super(EmailVerifyUserMixin, cls).check(**kwargs)
        errors.extend(cls._check_manager(**kwargs))
        return errors

    @classmethod
    def _check_manager(cls, **kwargs):
        if isinstance(cls.objects, VerifyEmailManager):
            return []

        return [
            checks.Warning(
                "Manager should be an instance of 'VerifyEmailManager'",
                hint="Subclass a custom manager from 'VerifyEmailManager'",
                obj=cls,
                id='user_management.W001',
            ),
        ]


class VerifyEmailMixin(EmailVerifyUserMixin, BasicUserFieldsMixin):
    class Meta:
        abstract = True


class AvatarMixin(models.Model):
    avatar = models.ImageField(upload_to='user_avatar', null=True, blank=True)

    class Meta:
        abstract = True

@@ -336,78 +336,17 @@
 tils
-.notifications import (%0A    PasswordResetNotification,%0A    ValidationN
+ import n
 otif
@@ -356,11 +356,9 @@
 tion
-,%0A)
+s
 %0A%0A%0Ac
@@ -3591,16 +3591,30 @@
 ation = 
+notifications.
 Password
@@ -3661,16 +3661,30 @@
 ation = 
+notifications.
 Validati
@@ -3970,39 +3970,24 @@
 nt()%0A       
- notification =
  self.valida
@@ -4008,85 +4008,29 @@
 ion(
-%0A            user=self,%0A            site=site,%0A        )%0A        notification
+user=self, site=site)
 .not
@@ -4189,23 +4189,8 @@
     
- notification =
  sel
@@ -4223,85 +4223,29 @@
 ion(
-%0A            user=self,%0A            site=site,%0A        )%0A        notification
+user=self, site=site)
 .not

Refactor how the user in notified