"""
This module defines the database models for mail handling.

"""
from __future__ import unicode_literals

from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext as _

from passlib.hash import sha512_crypt
from model_utils.models import TimeStampedModel

from domains.models import MailDomain
from osusers.models import User as OsUser

from fileservertasks.tasks import (
    create_file_mailbox,
    delete_file_mailbox,
)


class ActivateAbleMixin(models.Model):
    """
    Mixin for model classes that can be active or inactive.

    """
    active = models.BooleanField(default=True)

    class Meta:
        abstract = True


class MailboxManager(models.Manager):
    """
    This is the default manager class for :py:class:`Mailbox`.
    """

    def get_next_mailbox_name(self, osuser):
        """
        Get the next available mailbox name for the given operating system
        user.

        :param osuser: a :py:class:`operating system user <osuser.models.User>`
            instance
        :return: name of the mailbox
        :rtype: str

        """
        count = 1
        mailboxformat = "{0}p{1:02d}"
        mailboxname = mailboxformat.format(osuser.username, count)

        for box in self.values('username').filter(osuser=osuser).order_by(
            'username'
        ):
            if box['username'] == mailboxname:
                count += 1
                mailboxname = mailboxformat.format(osuser.username, count)
            else:
                break
        return mailboxname


@python_2_unicode_compatible
class Mailbox(ActivateAbleMixin, TimeStampedModel, models.Model):
    """
    This is the model class for a mailbox.

    """
    osuser = models.ForeignKey(OsUser)
    username = models.CharField(max_length=128, unique=True)
    password = models.CharField(max_length=255)

    objects = MailboxManager()

    class Meta:
        ordering = ['osuser', 'username']
        verbose_name = _('Mailbox')
        verbose_name_plural = _('Mailboxes')

    def set_password(self, password):
        """
        Set the hashed password for the mailbox.

        :param str password: the clear text password

        """
        self.password = sha512_crypt.encrypt(password)

    def save(self, *args, **kwargs):
        create_file_mailbox.delay(self.osuser.username, self.username).get()
        super(Mailbox, self).save(*args, **kwargs)

    def delete(self, *args, **kwargs):
        delete_file_mailbox.delay(self.osuser.username, self.username).get()
        super(Mailbox, self).delete(*args, **kwargs)

    def __str__(self):
        return self.username


@python_2_unicode_compatible
class MailAddress(ActivateAbleMixin, TimeStampedModel, models.Model):
    """
    This is the model class for a mail address.

    """
    localpart = models.CharField(max_length=128)
    domain = models.ForeignKey(MailDomain)

    class Meta:
        ordering = ['domain', 'localpart']
        unique_together = ('localpart', 'domain')
        verbose_name = _('Mail address')
        verbose_name_plural = _('Mail addresses')

    def __str__(self):
        return "{0}@{1}".format(self.localpart, self.domain)


@python_2_unicode_compatible
class MailAddressMailbox(TimeStampedModel, models.Model):
    """
    This is the model class to assign a mail address to a mailbox.

    """
    mailaddress = models.OneToOneField(MailAddress, primary_key=True)
    mailbox = models.ForeignKey(Mailbox)

    class Meta:
        unique_together = ('mailaddress', 'mailbox')

    def __str__(self):
        return self.mailbox.username


class MailAddressForward(TimeStampedModel, models.Model):
    """
    This is a model class to map mail addresses to forwarding addresses.

    """
    mailaddress = models.ForeignKey(MailAddress)
    target = models.EmailField(max_length=254)

    class Meta:
        unique_together = ('mailaddress', 'target')