# -*- coding: utf-8 -*-
"""
Copyright 2013-2014 Olivier Cortès <oc@1flow.io>.

This file is part of the 1flow project.

1flow is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

1flow is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public
License along with 1flow.  If not, see http://www.gnu.org/licenses/

"""

import uuid
import logging

from django.db import models
from django.db.models.signals import pre_save
from django.utils.translation import ugettext_lazy as _
from django.utils.text import slugify
from django.contrib.auth.models import Group
from django.contrib.contenttypes import generic

from mptt.models import MPTTModel, TreeForeignKey

from sparks.django.models.mixins import DiffMixin

from oneflow.base.fields import IntRedisDescriptor
from oneflow.base.utils import register_task_method

from common import (
    FOLDER_ANONYMOUS_MEMBERSHIP_TYPES,
    PROPOSAL_ACTION_TYPES,
    lowername,
    DjangoUser as User,
)


LOGGER = logging.getLogger(__name__)


__all__ = [
    'Folder',

    'folder_all_items_count_default',
    'folder_starred_items_count_default',
    'folder_unread_items_count_default',
    'folder_archived_items_count_default',
    'folder_bookmarked_items_count_default',
]


def qs_order_by_lower_name(queryset, field=None):
    """ Order a QuerySet by lowercase(field); field is “name” by default. """

    return queryset.extra(
        select={'_lower_order_by_field_': 'lower({0})'.format(field or 'name')}
    ).order_by('_lower_order_by_field_')


def get_folder_image_upload_path(instance, filename):

    if not filename.strip():
        filename = uuid.uuid4()

    # The filename will be used in a shell command later. In case the
    # user/admin forgets the '"' in the configuration, avoid problems.
    filename = filename.replace(u' ', u'_')

    if instance:
        return 'user/{0}/folder/{1}/images/{2}'.format(
            instance.user.username, instance.id, filename)

    return u'images/%Y/%m/%d/{0}'.format(filename)


# ——————————————————————————————————————————————————————————————— Redis Helpers


def folder_all_items_count_default(folder, *args, **kwargs):

    return folder.reads.all().count()


def folder_starred_items_count_default(folder, *args, **kwargs):

    return folder.reads.filter(is_starred=True).count()


def folder_unread_items_count_default(folder, *args, **kwargs):

    return folder.reads.filter(is_read=False).count()


def folder_archived_items_count_default(folder, *args, **kwargs):

    return folder.reads.filter(is_archived=True).count()


def folder_bookmarked_items_count_default(folder, *args, **kwargs):

    return folder.reads.filter(is_bookmarked=True).count()


# —————————————————————————————————————————————————————————————————————— Models


class Folder(MPTTModel, DiffMixin):

    """ A simple folder structure. """

    class Meta:
        app_label = 'core'
        verbose_name = _(u'Folder')
        verbose_name_plural = _(u'Folders')
        unique_together = ('name', 'user', 'parent', )

    user = models.ForeignKey(User, verbose_name=_(u'Owner'),
                             related_name='folders')

    name = models.CharField(verbose_name=_(u'Name'), max_length=255)
    slug = models.CharField(verbose_name=_(u'slug'), max_length=255,
                            null=True, blank=True)

    parent = TreeForeignKey('self', null=True, blank=True,
                            related_name='children')

    date_created = models.DateTimeField(auto_now_add=True, blank=True,
                                        verbose_name=_(u'Date created'))

    # Allow the user to also customize the visual of his/her subscription.
    image = models.ImageField(
        verbose_name=_(u'Image'), null=True, blank=True,
        upload_to=get_folder_image_upload_path, max_length=256,
        help_text=_(u'A custom image for the folder. Takes precedence over '
                    u'image_url if both are filled.'))

    image_url = models.URLField(
        verbose_name=_(u'Image URL'), null=True, blank=True, max_length=384,
        help_text=_(u'A full URL of an online image, if you prefer hosting '
                    u'it outside of 1flow.'))

    # ——————————————————————————————————————————————————— Shared folders fields

    is_shared = models.BooleanField(
        verbose_name=_(u'Share this folder'),
        default=False, blank=True
    )
    is_moderated = models.BooleanField(
        verbose_name=_(u'Moderate members proposals'),
        default=True, blank=True,
        help_text=_(u'Any feed addition/removal/change in this folder must '
                    u'be validated by the owner/creator or a folder staff '
                    u'member before beiing accepted.'))
    is_restricted = models.BooleanField(
        verbose_name=_(u'All content is restricted'),
        default=True, blank=True,
        help_text=_(u'In case un-authenticated users have access to this '
    
                u'folder, marking it restricted will avoid them to see '
                    u'the folder items full content, but only excerpts '
                    u'or thumbnails instead. If a fol
der is restricted, '
                    u'this takes precedence over feed and item restricted '
                    u'state, and makes them restricted even if they are not.'))

    staff_group = models.OneToOneField(Group, null=True, blank=True,
                                       related_name='staff_folder')
    members_group = models.OneToOneField(Group, null=True, blank=True,
                                         related_name='members_folder')
    guests_group = models.OneToOneField(Group, null=True, blank=True,
                                        related_name='guests_folder')

    # Not used yet.
    # is_permissive = models.BooleanField(default=False, blank=True)

    anonymous_membership = models.IntegerField(
        verbose_name=_(u'Anonymous visitors have'),
        choices=FOLDER_ANONYMOUS_MEMBERSHIP_TYPES.get_choices(),
        default=FOLDER_ANONYMOUS_MEMBERSHIP_TYPES.NO_ACCESS,
        help_text=_(u'Define the access that un-authenticated users will '
                    u'have on this shared folder.'))

    # Moderation proposals to include / remove things in / from this folder.
    proposals = generic.GenericRelation(
        'Proposal',
        content_type_field='context_content_type',
        object_id_field='context_id'
    )

    # —————————————————————————————————————————————————————— Cached descriptors

    all_items_count = IntRedisDescriptor(
        attr_name='f.aa_c', default=folder_all_items_count_default,
        set_default=True)

    unread_items_count = IntRedisDescriptor(
        attr_name='f.ua_c', default=folder_unread_items_count_default,
        set_default=True)

    starred_items_count = IntRedisDescriptor(
        attr_name='f.sa_c', default=folder_starred_items_count_default,
        set_default=True)

    archived_items_count = IntRedisDescriptor(
        attr_name='f.ra_c',
        default=folder_archived_items_count_default,
        set_default=True, min_value=0)

    bookmarked_items_count = IntRedisDescriptor(
        attr_name='f.ba_c', default=folder_bookmarked_items_count_default,
        set_default=True)

    @property
    def read_items_count(self):
        """ Return all items count minus unread items count. """

        return self.all_items_count - self.unread_items_count

    # ————————————————————————————————————————————————————————— Django & Python

    def __unicode__(self):
        """ Wake up, pep257. That's just the unicode method. """

        return _(u'{0} (#{1}, level {2}) for user {3}{4}{5}').format(
            self.name, self.id, self.level, self.user,
            _(u', parent: {0} (#{1})').format(self.parent.name, se