import collections
import hashlib
import os
import re
import time
import uuid
from datetime import datetime

from django.conf import settings
from django.core.cache import cache
from django.db import connection, models, transaction

import caching.base as caching

import amo
import amo.models
import sharing.utils as sharing
from access import acl
from addons.models import Addon, AddonRecommendation
from amo.helpers import absolutify, user_media_path, user_media_url
from amo.urlresolvers import reverse
from amo.utils import sorted_groupby
from stats.models import CollectionShareCountTotal
from translations.fields import (LinkifiedField, save_signal,
                                 NoLinksNoMarkupField, TranslatedField)
from users.models import UserProfile
from versions import compare

SPECIAL_SLUGS = amo.COLLECTION_SPECIAL_SLUGS


class TopTags(object):
    """Descriptor to manage a collection's top tags in cache."""

    def key(self, obj):
        return '%s:top-tags:%s' % (settings.CACHE_PREFIX, obj.id)

    def __get__(self, obj, type=None):
        if obj is None:
            return self
        return cache.get(self.key(obj), [])

    def __set__(self, obj, value):
        two_days = 60 * 60 * 24 * 2
        cache.set(self.key(obj), value, two_days)


class CollectionQuerySet(caching.CachingQuerySet):

    def with_has_addon(self, addon_id):
        """Add a `has_addon` property to each collection.

        `has_addon` will be `True` if `addon_id` exists in that
        particular collection.
        """
        has_addon = """
            select 1 from addons_collections as ac
                where ac.addon_id = %s and ac.collection_id = collections.id
                limit 1"""

        return self.extra(
            select={'has_addon': has_addon},
            select_params=(addon_id,))


class CollectionManager(amo.models.ManagerBase):

    def get_query_set(self):
        qs = super(CollectionManager, self).get_query_set()
        qs = qs._clone(klass=CollectionQuerySet)
        return qs.transform(Collection.transformer)

    def manual(self):
        """Only hand-crafted, favorites, and featured collections should appear
        in this filter."""
        types = (amo.COLLECTION_NORMAL, amo.COLLECTION_FAVORITES,
                 amo.COLLECTION_FEATURED, )

        return self.filter(type__in=types)

    def listed(self):
        """Return public collections only."""
        return self.filter(listed=True)

    def publishable_by(self, user):
        """Collections that are publishable by a user."""
        owned_by = models.Q(author=user.id)
        publishable_by = models.Q(users=user.id)
        collections = self.filter(owned_by | publishable_by)
        return collections.distinct().order_by('name__localized_string')


class CollectionBase:
    """A mixin with methods common to Collection and SyncedCollection."""

    @classmethod
    def make_index(cls, addon_ids):
        ids = ':'.join(map(str, sorted(addon_ids)))
        return hashlib.md5(ids).hexdigest()

    def get_recs(self, app, version):
        addons = list(self.addons.values_list('id', flat=True))
        return self.get_recs_from_ids(addons, app, version)

    @classmethod
    def get_recs_from_ids(cls, addons, app, version, compat_mode='strict'):
        vint = compare.version_int(version)
        recs = RecommendedCollection.build_recs(addons)
        qs = (Addon.objects.public()
              .filter(id__in=recs, appsupport__app=app.id,
                      appsupport__min__lte=vint))
        if compat_mode == 'strict':
            qs = qs.filter(appsupport__max__gte=vint)
        return recs, qs


class Collection(CollectionBase, amo.models.ModelBase):

    TYPE_CHOICES = amo.COLLECTION_CHOICES.items()

    uuid = models.CharField(max_length=36, blank=True, unique=True)
    name = TranslatedField(require_locale=False)
    # nickname is deprecated.  Use slug.
    nickname = models.CharField(max_length=30, blank=True, unique=True,
                                null=True)
    slug = models.CharField(max_length=30, blank=True, null=True)

    description = NoLinksNoMarkupField(require_locale=False)
    default_locale = models.CharField(max_length=10, default='en-US',
                                      db_column='defaultlocale')
    type = models.PositiveIntegerField(db_column='collection_type',
                                       choices=TYPE_CHOICES, default=0)
    icontype = models.CharField(max_length=25, blank=True)

    listed = models.BooleanField(
        default=True, help_text='Collections are either listed or private.')

    subscribers = models.PositiveIntegerField(default=0)
    downloads = models.PositiveIntegerField(default=0)
    weekly_subscribers = models.PositiveIntegerField(default=0)
    monthly_subscribers = models.PositiveIntegerField(default=0)