""" This module holds the models of the ``metaplans`` app. """

from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.functional import cached_property
from django.utils.text import Truncator
from django.utils.translation import ugettext_lazy as _

from mtp.apps.common.models import Orderable
from mtp.apps.sync.models import Syncable
from mtp.apps.trainings.models import TrainingActivity

from .managers import (
    MetaplanPropositionManager, MetaplanPropositionQuerySet,
    MetaplanManager, MetaplanQuerySet,
    MetaplanTemplateManager, MetaplanTemplateQuerySet,
)


class MetaplanTemplate(TrainingActivity):
    """ A Template is a Metaplan model.

    Attributes
    ----------
    name : django ``CharField``
        The name of the metaplan template, mandatory.
        Inherited from ``TrainingActivity``.
    training : django ``ForeignKey``
        The training the metaplan template belongs to.
        Inherited from ``TrainingActivity``.
    sort_order : django ``PositiveIntegerField``
        The sort order of this metaplan template.
        Inherited from ``Orderable``.

    """
    objects = MetaplanTemplateManager.from_queryset(MetaplanTemplateQuerySet)()

    verbose_activity_name = _('Metaplan')

    class Meta:
        ordering = ['sort_order']

    def __str__(self):
        return '%s (template)' % self.name

    @cached_property
    def activity_type(self):
        """Return a verbose name for this activity type."""

        return _('Metaplan')

    def save(self, *args, **kwargs):
        """Manually crop the "name" field

        For the parameters, see ``django.db.models.Model``
        """

        if len(self.name) > 100:
            self.name = Truncator(self.name).chars(100, truncate='(...)')

        super().save(*args, **kwargs)

    @classmethod
    def get_create_url(cls, training):
        """ Get the url of to create a new metaplan template.

        Returns
        -------
        string
            The wanted url.

        """
        return reverse(
            'trainings:metaplan-template-create',
            kwargs={'training_uuid': training.pk}
        )

    def get_absolute_url(self):
        """ Get the url of the current metaplan template.

        It actually returns the edit url, as there is no "read" view
        for a metaplan template.

        Returns
        -------
        string
            The wanted url.

        """
        return self.get_detail_url()

    def get_detail_url(self):
        """ Get the url to see details of current metaplan template.

        Returns
        -------
        string
            The wanted url.

        """
        return reverse(
            'metaplans:template-detail',
            kwargs={'template_uuid': self.pk}
        )

    def get_edit_url(self):
        """ Get the url to edit the current metaplan template.

        Returns
        -------
        string
            The wanted url.

        """
        return reverse(
            'metaplans:template-update',
            kwargs={'template_uuid': self.pk}
        )

    @classmethod
    def get_import_url(cls, training):
        """ Get the url of to import a new metaplan.

        Returns
        -------
        string
            The wanted url.

        """
        return reverse(
            'metaplans:import-xls',
            kwargs={'training_uuid': training.pk}
        )

    def get_export_url(self):
        """ Get the url of to export a metaplan.

        Returns
        -------
        string
            The wanted url.

        """
        return reverse(
            'metaplans:export-xls',
            kwargs={'metaplan_template_uuid': self.pk}
        )

    def get_qrcode_url(self):
        """Get the url to download the QRCode able to launch activity.

        We disable this option for metaplan template.

        """
        return None


class Metaplan(Orderable, Syncable):
    """ A Metaplan allows to organize propositions on a work subject and
    assign them a color in a way similar to Post-It notes.

    A Metaplan is built by copying a MetaplanTemplate. Unlike it, though,
    it is linked to a training session, instead of a training.

    Attributes
    ----------
    name : django ``CharField``
        The name of the metaplan, mandatory.
    session : foreign key to ``mtp.apps.trainings.TrainingSession``

    Notes
    -----
    This model is ``Syncable``.
    """

    name = models.CharField(_("name"), max_length=100)
    session = models.ForeignKey('trainings.TrainingSession', related_name='metaplans')

    objects = MetaplanManager.from_queryset(MetaplanQuerySet)()

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        """
        A Metaplan can inherit MetaplanTemplate data, so before saving it we verify
        if a template is given in kwargs for populate attributes of Metaplan.

        """
        try:

            # If a metaplan template was specified, we should populate this
            # metaplan with its data.
            template = kwargs.pop('template')
            self.name = Truncator(template.name).chars(100, truncate='(...)')
            self.sort_order = template.sort_order

        except KeyError:

            # No template, no problem.
            pass

        finally:

            # We can now save the metaplan...
            super().save(*args, **kwargs)

    def get_propositions(self):
        """
        Returns
        -------
        list
            Return all propositions of metaplan.

        """
        return self.propositions.non_deleted()

    def get_grouped_propositions(self):
        """
        Returns
        -------
        dict
            Return all propositions organized by group.

        """

        propositions_grouped = {}
        for proposition in self.propositions.filter(is_deleted=False):
            prop_group = proposition.group
            if prop_group in propositions_grouped:
                propositions_grouped[prop_group].append(proposition)
            else:
                propositions_grouped[prop_group] = [proposition]
        return propositions_grouped


class MetaplanProposition(Syncable):
    """ A Metaplan Proposition

    Attributes
    ----------
    metaplan : foreign key to ``mtp.apps.metaplans.Metaplan``
    group : django ``PositiveIntegerField``
        Group of the proposition.
        The iOS client app currently uses integers from 0 to 3
        to distinguish groups.
    user : django ``ForeignKey``
        The user who entered this proposition.
    text : django ``CharField``
        The text of the proposition.

    Notes
    -----
    This model is ``Syncable``.

    """
    metaplan = models.ForeignKey('metaplans.Metaplan', related_name='propositions')
    group = models.PositiveIntegerField(_("group"), default=0)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_("user"))
    text = models.CharField(_("text"), max_length=100)

    objects = MetaplanPropositionManager.from_queryset(MetaplanPropositionQuerySet)()
