# -*-coding: utf8 -*-

from __future__ import unicode_literals, absolute_import, print_function

from django.contrib.auth.models import User
from django.db import models
from gm_types.gaia import (
    SOCIAL_FOLLOW_STATUS,
)

from social.consts import SUOZHANG_UID
from social.utils import social_cache
from talos.cache.gaia import follow_cache
from talos.libs.datetime_utils import ts_now_as_score
from talos.services import UserService


class UserFollow(models.Model):
    class Meta:
        verbose_name = '用户关注关系'
        unique_together = ('user', 'follow')
        app_label = 'social'

    user = models.ForeignKey(User, related_name="fans", verbose_name="用户")
    follow = models.ForeignKey(User, verbose_name="关注的用户")
    bond = models.BooleanField(default=True, verbose_name='关注关系是否还有效')
    update_time = models.DateTimeField(verbose_name='关注时间', null=True)
    # 2017.5.31 涨粉策略
    is_virtual_fan = models.BooleanField(verbose_name=u'是否是虚拟粉丝', default=False)


class _FollowInfoCacheStore(object):
    _cache = follow_cache

    def __init__(self, uid):
        self.uid = uid

    @staticmethod
    def get_follow_key(follow_id):
        return 'follow_' + str(follow_id)

    def follow(self, follow_id):
        key = self.get_follow_key(follow_id)
        self._cache.sadd(key, self.uid)

    def unfollow(self, follow_id):
        key = self.get_follow_key(follow_id)
        self._cache.srem(key, self.uid)

    def get_count(self):
        key = self.get_follow_key(self.uid)
        num = self._cache.scard(key)
        return num

    def clear_count(self):
        key = self.get_follow_key(self.uid)
        self._cache.delete(key)

    def get_all_fans(self, follow_id):
        key = self.get_follow_key(follow_id)
        ids = self._cache.smembers(key)
        return [int(fans_id) for fans_id in ids]


class _SocialInfoCacheStore(object):
    """redis cmd wrapper."""

    _cache = social_cache  # redis client

    def __init__(self, uid):
        self.uid = uid
        # big v mailbox cache
        self._big_v_cache_key = 'bv:mb:{uid}'.format(uid=uid)
        # last time check ts for user for big v

    @property
    def _big_v_last_check_ts_key(self):
        return 'bv:mb:cts:{{big_v_uid}}:{uid}'.format(uid=self.uid)

    def last_check_mailbox_bigv_ts(self, big_v_uid):
        k = self._big_v_last_check_ts_key.format(big_v_uid=big_v_uid)
        ts = self._cache.get(k) or 0
        return int(ts)

    def update_last_check_mailbox_bigv_ts(self, big_v_uid):
        k = self._big_v_last_check_ts_key.format(big_v_uid=big_v_uid)
        return self._cache.set(k, ts_now_as_score())

    def delete_bigv_mailbox_last_check_ts(self, big_v_uid):
        k = self._big_v_last_check_ts_key.format(big_v_uid=big_v_uid)
        return self._cache.delete(k)


class _FansIterator(object):
    """iterate through fans list."""

    def __init__(self, social_info):
        self.social_info = social_info
        self.next_i = 0
        self.steps = 10
        self._data = []

    def _load_more(self):
        if not self._data:
            self._data = self.social_info.fans(self.next_i, self.steps)
            if not self._data: raise StopIteration
            self.next_i += self.steps

    def __iter__(self):
        self._load_more()

        while self._data:
            d = self._data.pop()
            yield d
            self._load_more()


class SocialInfo(object):
    """user social information."""

    @classmethod
    def social_info(cls, uid):
        """get instance by uid."""
        return cls(uid)

    def __init__(self, uid):
        self.uid = uid
        self._cache_store = _SocialInfoCacheStore(self.uid)
        self._follow_cache = _FollowInfoCacheStore(self.uid)

    def unsubscribe_from_bigv_mailbox(self, big_v_uid):
        return self._cache_store.delete_bigv_mailbox_last_check_ts(big_v_uid)

    @classmethod
    def is_big_v(cls, uid):
        """check if user is a big v.

        args:
            uid, should be int

        NOTE:
            currently, we only check if the uid is suozhang_uid, we can redefine
            this method to check if the fans count of this user large than 2k or
            some other threshold.
        """
        try:
            uid = int(uid)
        except:
            return False

        return uid == SUOZHANG_UID

    @property
    def fans_count(self):
        # avoid scan table
        if self.uid == SUOZHANG_UID:
            return 9999999

        return UserFollow.objects.filter(follow_id=self.uid, bond=True).count()

    @property
    def following_count(self):
        return UserFollow.objects.filter(user_id=self.uid, bond=True).count()

    def get_new_follow_count(self):
        num = self._follow_cache.get_count()
        if num >= 99:
            num = 99
        return num

    def fans(self, start_num=0, count=10):
        """get fans uid list."""
        rels = UserFollow.objects.filter(follow_id=self.uid, bond=True).order_by('-update_time')
        result = []
        for rel in rels[start_num:start_num + count]:
            result.append(rel.user_id)
        return result

    def following(self, start_num=0, count=10):
        """get following uids."""
        rels = UserFollow.objects.filter(user_id=self.uid, bond=True).order_by('-update_time')
        result = []
        for rel in rels[start_num:start_num + count]:
            result.append(rel.follow_id)
        return result

    def is_following_user(self, uid):
        """check if user following uid."""
        rel = UserFollow.objects.filter(user_id=self.uid, follow_id=uid, bond=True).count()
        return rel > 0

    def is_following_users(self, uids):
        rels = UserFollow.objects.filter(user_id=self.uid, follow_id__in=uids, bond=True)
        if not rels:
            return {}

        result = {rel.follow_id: True for rel in rels}
        for uid in uids:
            if uid not in result:
                result[uid] = False

        return result

    def follow_status(self, uid):
        is_following_user = SOCIAL_FOLLOW_STATUS.FOLLOWING if self.is_following_user(
            uid) else SOCIAL_FOLLOW_STATUS.STRANGER
        is_followed_user = SOCIAL_FOLLOW_STATUS.FOLLOWED if self.is_followed_user(
            uid) else SOCIAL_FOLLOW_STATUS.STRANGER
        status = is_following_user + is_followed_user
        return status

    def is_followed_user(self, uid):
        rel = UserFollow.objects.filter(user_id=uid, follow_id=self.uid, bond=True).count()
        return rel > 0

    def add_fans(self, uid, is_virtual_fan=False):
        try:
            u = User.objects.filter(id=uid).first()
            if u:
                rel = UserFollow(user_id=uid, follow_id=self.uid, bond=True, is_virtual_fan=is_virtual_fan)
                rel.save()
        except:
            # logging_exception()
            pass

    def del_fans(self, uid):
        try:
            rel = UserFollow.objects.get(user_id=uid, follow_id=self.uid)
        except UserFollow.DoesNotExist:
            return

        rel.bond = False
        rel.save()

    def get_fans_iterator(self):
        return _FansIterator(self)

    @classmethod
    def add_to_fans(cls, to_uid, uid, is_virtual_fan=False):
        m = cls(to_uid)
        return m.add_fans(uid, is_virtual_fan=is_virtual_fan)

    @classmethod
    def del_from_fans(cls, from_uid, uid):
        m = cls(from_uid)
        return m.del_fans(uid)

    def follow(self, uid):
        """add uid into following list."""
        if uid == self.uid: return

        me = UserService.get_user_by_user_id(self.uid)
        target = UserService.get_user_by_user_id(uid)
        if not (me and target): return

        rel, created = UserFollow.objects.get_or_create(user=me, follow=target)
        self._follow_cache.follow(uid)
        if (not created) and rel.bond:
            return
        elif not created:
            rel.bond = True
            rel.save()
        return SocialInfo.add_to_fans(uid, self.uid)

    def clear_follow_num(self):
        self._follow_cache.clear_count()

    def unfollow(self, uid):
        """remove uid from following list."""
        try:
            rel = UserFollow.objects.get(user_id=self.uid, follow_id=uid)
            rel.bond = False
            rel.save()
        except UserFollow.DoesNotExist:
            return

        SocialInfo.del_from_fans(uid, self.uid)
        self._follow_cache.unfollow(uid)
        # delete bigv mailbox check ts
        if self.is_big_v(uid): self.unsubscribe_from_bigv_mailbox(uid)
