"""ORM Models for Users that could be members and user activation requests."""

import hashlib
import random
from datetime import timedelta
from os import path

from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.urls import reverse
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext as _
from easy_thumbnails.fields import ThumbnailerImageField

from utils import OverwriteStorage
from . import PAID_MEMBERSHIP_GROUP, GENDER_CHOICES


def get_upload_path(instance, filename):
    """Erstellt den Pfad und Dateinamen für den Upload dynmisch.

    @param instance: The Membership Object for the uploaded image
    @param filename: the filename of the uploaded image
    """
    extension = path.splitext(filename)[1]
    return 'membership/%s%s' % (instance.username, extension)


class ActivationManager(models.Manager):
    """Manages pending user registrations."""

    def activate(self, activation_key):
        """
        searches the pending registrations for the given activation key.
        Set the corresponding user to active, if the key was found
        and the key has not expired yet.s

        @param activation_key: the key found in the activation email
        """
        try:
            activation_request = self.get(activation_key=activation_key)
            if activation_request.expired():
                activation_request.user.delete()
                activation_request.delete()
                return False
            elif not activation_request.user.is_active:
                activation_request.user.is_active = True
                activation_request.user.save()
                activation_request.delete()
            return activation_request.user
        except self.model.DoesNotExist:
            return False

    def create_pending_registration(self, user):
        """Creates a PendingActivation instance with an random activation key.
        @param user: the user that requests activation.
        """
        salt = str(user.pk * random.random())
        salt += user.registration_date.isoformat()
        activation_key = hashlib.sha1(salt.encode()).hexdigest()
        return self.create(user=user, activation_key=activation_key)

    def expired(self):
        """Return all ActivationRequest that haven't been activated and
        whose valid activation time has been expired"""
        timespan = timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
        expiration_date = timezone.now() - timespan
        return self.filter(
            user__is_active=False,
            user__date_joined__lt=expiration_date
        )


class ActivationRequest(models.Model):
    """
    Each ActivationRequest contains an activation key and an user.
    The key will be send by email to the user
    if the user clicks on the link he can activate his in_active account.
    """
    user = models.OneToOneField(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        verbose_name=_('user')
    )
    activation_key = models.CharField(_('activation key'), max_length=40)
    objects = ActivationManager()

    class Meta:
        verbose_name = _('pending activation')
        verbose_name_plural = _('pending activations')

    def __str__(self):
        return _("user registration for %s") % self.user

    def activate(self):
        """Activate the user account and delete this ActivationRequest"""
        self.user.is_active = True
        self.user.save()
        self.delete()

    @property
    def expiration_date(self):
        """The datetime until this user activation request is valid."""
        timespan = timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
        return self.user.date_joined + timespan

    @property
    def email(self):
        """The email adress of the user that should be activated."""
        return self.user.email

    def expired(self):
        """True if user is inactive and expiration_date is in the past."""
        if self.user.is_active:
            return False
        elif timezone.now() >= self.expiration_date:
            return True
        else:
            return False

    expired.boolean = True

    @property
    def first_name(self):
        """First name of the user that should be activated."""
        return self.user.first_name

    @property
    def last_name(self):
        """Last name of the user that should be activated."""
        return self.user.last_name

    @property
    def registration_date(self):
        """Date when the user joined/registered his account."""
        return self.user.date_joined

    @property
    def username(self):
        """Loing name of the user that should be activated."""
        return self.user.username


class Membership(AbstractUser):
    """An expanded Django User with additional data to manage club memberships.
    """
    gender = models.CharField(
        _("Gender"),
        max_length=1,
        choices=GENDER_CHOICES,
        blank=True,
        null=True
    )
    website = models.URLField(blank=True)
    avatar = ThumbnailerImageField(
        upload_to=get_upload_path,
        storage=OverwriteStorage(),
        blank=True,
        null=True
    )
    membership = models.BooleanField(
        _('Membership'),
        default=False,
        help_text=_('Yes, I confirm that I am in agreement with the statutes \
        and would like to become a member.')
    )
    birthday = models.DateField(
        _("Birthday Date"),
        blank=True, null=True,
    )
    telephone = models.CharField(
        _("Telephone"),
        max_length=30,
        blank=True,
        null=True
    )
    street_name = models.CharField(
        _("Address"),
        max_length=75,
        blank=True,
        null=True
    )
    post_code = models.PositiveSmallIntegerField(
        _("Postcode"),
        blank=True,
        null=True
    )
    city = models.CharField(
        _("Town/City"),
        max_length=75,
        blank=True,
        null=True
    )
    deposit = models.PositiveSmallIntegerField(default=0, editable=False)
    registration_date = models.DateField(auto_now_add=True, editable=False)
    paid_until = models.DateField(
        _('Paid until'),
        blank=True,
        null=True,
        editable=True
    )
    confirmed = models.BooleanField(
        _('Confirmed'),
        default=False,
        help_text=_('This person has paid the membership fee.')
    )

    # comment = models.TextField(blank=True)
    # objects = MembershipManager()

    class Meta(object):
        """To manage object ordering and dispalynames on the admin interface."""
        ordering = ('username',)
        swappable = 'AUTH_USER_MODEL'
        verbose_name = _('Membership')
        verbose_name_plural = _('Memberships')

    @property
    def full_name(self):
        return " ".join([self.last_name, self.first_name])

    def __str__(self):
        return self.username

    def get_absolute_url(self):
        """:return the URL for this Membership DetailView"""
        return reverse('membership-details', kwargs={'username': self.username})

    def save(self, *args, **kwargs):
        """Save the Useraccount, and add him tho the "Paid Membership" group
        if he activated the "membership" checkbox and has been validated.

        :param args: passed through the save() method from django
        :param kwargs: passed through the save() method from django
        """
        super(Membership, self).save(*args, **kwargs)
        if self.confirmed:
            self.groups.add(PAID_MEMBERSHIP_GROUP)
        else:
            self.groups.remove(PAID_MEMBERSHIP_GROUP)
