# coding:utf-8
import json
import random
from collections import defaultdict
from itertools import chain
from datetime import datetime, timedelta

from api.util.user_util import simple_user_info_by_user_ids
from agile.models.agile_tag import AgileTagMapping
from api.models import Tag
from group.models import Group, GroupTag, GroupContent, GroupFollow, \
    GroupTagV3, GroupTopicRelation
from rpc.tool.error_code import gen, CODES
from rpc.tool.dict_mixin import to_dict
from rpc.cache import group_cache
from agile.services import TagV3Service
from agile.models import TagV3


class GroupService(object):

    _cache = group_cache
    join_key = "{group_id}"
    follow_key = "group:follow:{group_id}"

    @classmethod
    def init_join_cnt(cls, group_id):
        key = cls.join_key.format(group_id=group_id)
        join_cnt = random.randint(2000, 20000)
        cls._cache.set(key, join_cnt)
        return join_cnt

    @classmethod
    def _join_cnt(cls, group_id):
        key = cls.join_key.format(group_id=group_id)
        if not cls._cache.exists(key):
            return cls.init_join_cnt(group_id)

        return int(cls._cache.get(key))

    @classmethod
    def follow_num(cls, group_id):
        key = cls.follow_key.format(group_id=group_id)
        return int(cls._cache.get(key) or 0)

    @classmethod
    def incr_join_cnt(cls, group_id):
        cls.incr_follow_num(group_id)
        key = cls.join_key.format(group_id=group_id)
        return cls._cache.incr(key)

    @classmethod
    def incr_follow_num(cls, group_id):
        key = cls.follow_key.format(group_id=group_id)
        return cls._cache.incr(key)

    @classmethod
    def decr_follow_num(cls, group_id):
        key = cls.follow_key.format(group_id=group_id)
        if cls._cache.exists(key):
            return cls._cache.decr(key)

    @classmethod
    def decr_join_cnt(cls, group_id):
        cls.decr_follow_num(group_id)
        key = cls.join_key.format(group_id=group_id)
        if cls._cache.exists(key):
            return cls._cache.decr(key)

    @classmethod
    def join_cnt(cls, group):

        # 前30天内，每小时内第一次访问增加：13~25随机
        # 30-60：9~17
        # 60-180：3~9
        # 180天后：1~3

        join_cnt = cls._join_cnt(group.id)

        now = datetime.now()
        hour = now.hour
        hour_join_key = "{group_id}:{hour}".format(group_id=group.id, hour=hour)
        if cls._cache.incr(hour_join_key) > 1:
            return join_cnt

        cls._cache.expire(hour_join_key, 3600)

        duration = now - group.create_time
        if duration <= timedelta(days=30):
            amount = random.randint(13, 25)
        elif duration <= timedelta(days=60):
            amount = random.randint(9, 17)
        elif duration <= timedelta(days=180):
            amount = random.randint(3, 9)
        else:
            amount = random.randint(1, 3)

        return cls._cache.incr(cls.join_key.format(group_id=group.id), amount)

    @classmethod
    def safe_get(cls, group_id):

        try:
            group = Group.objects.get(pk=group_id)
        except Group.DoesNotExist:
            gen(CODES.GROUP_NOT_FOUND)

        if not group.is_online:
            gen(CODES.GROUP_OFFLINE)

        return group

    @classmethod
    def is_follow(cls, group_id, user_id):

        follow = GroupFollow.objects.filter(
            group_id=group_id, user_id=user_id, is_online=True
        ).first()
        if follow:
            return True

        return False

    @classmethod
    def detail(cls, group):
        return {
            "id": group.id,
            "name": group.name,
            "intro": group.introduction,
            "header_image": group.header_image,
            "bg_img": group.bg_image,
            "join_cnt": cls.join_cnt(group),
            "is_online": group.is_online,
            "tags": group.tag_list,
            "group_type": group.group_type,  # 小组类型
            "is_single_feed": group.is_single_feed,  # 单列feed
            "show_service_tab": group.show_service_tab,
            "show_video_tab": group.show_video_tab,
        }

    @classmethod
    def join_users(cls, group_id, limit=6):

        follow_ids = GroupFollow.objects.filter(
            group_id=group_id, is_online=True).order_by("-id").values_list("user_id", flat=True)[:limit]
        users = simple_user_info_by_user_ids(follow_ids)

        ret = []
        for user_id in follow_ids:
            user = users.get(str(user_id))
            if user:
                ret.append(user)

        return ret

    @classmethod
    def follow_cnt(cls, group_id):

        return GroupFollow.objects.filter(group_id=group_id, is_online=True).count()

    @classmethod
    def follow(cls, group_id, user_id):

        try:
            obj = GroupFollow.objects.get(
                group_id=group_id, user_id=user_id)
        except:
            GroupFollow.objects.create(
                group_id=group_id, user_id=user_id, is_online=True)
            cls.incr_join_cnt(group_id)
            return

        if obj.is_online:
            gen(CODES.GROUP_FOLLOWED)
        else:
            obj.is_online = True
            obj.save()
            cls.incr_join_cnt(group_id)

    @classmethod
    def unfollow(cls, group_id, user_id):

        try:
            group = GroupFollow.objects.get(group_id=group_id, user_id=user_id)
            if group.is_online:
                group.delete()
                cls.decr_join_cnt(group_id)
        except:
            return

    @classmethod
    def add_content(cls, group_id, card_type, card_id):
        GroupContent.objects.update_or_create(
            group_id=group_id, content_type=card_type,
            content_id=card_id, is_online=True,
        )

    @classmethod
    def get_group_by_topic_id(cls, topic_id):

        relation = GroupTopicRelation.objects.filter(is_online=True, topic_id=topic_id).first()
        if not relation:
            return None

        return Group.objects.filter(is_online=True, pk=relation.group_id).first()

    @classmethod
    def other_group_ids_by_topic_ids(cls, group_id, topic_ids):

        return list(
            GroupTopicRelation.objects.filter(
                is_online=True, topic_id__in=topic_ids
            ).exclude(group_id=group_id).values_list("topic_id", flat=True)
        )

    @classmethod
    def group_relation_tag_v3s_by_ids(cls, group_ids):
        """
        获取小组关联的标签3.0的数据
        :param group_ids:
        :return:
        """
        relation_tag_ids = GroupTagV3.objects.filter(
            group_id__in=group_ids, is_online=True
        ).values_list("group_id", "tag_v3_id")

        tagv3_infos = TagV3Service.get_tags_v3_by_ids(set(item[1] for item in relation_tag_ids))

        group_rel_tag_v3_dic = {}
        for group_id, tag_v3_id in relation_tag_ids:
            if group_id not in group_rel_tag_v3_dic:
                group_rel_tag_v3_dic[group_id] = []

            tag_v3_info = tagv3_infos.get(tag_v3_id, {})
            if not tag_v3_info:
                continue

            group_rel_tag_v3_dic[group_id].append(tag_v3_info)

        return group_rel_tag_v3_dic

    @classmethod
    def tags_v3_by_group_id(cls, group_id):

        tag_ids = list(
            GroupTagV3.objects.filter(
                group_id=group_id, is_online=True).values_list("tag_v3_id", flat=True)
        )
        # second_appeal_attrs = TagV3Service.list_second_appeal_attrs(tag_ids)
        # tags_v3 = TagV3Service.list_tags_by_attrs([attr.id for attrs in second_appeal_attrs.values() for attr in attrs])
        # tags_v3.update(TagV3Service.get_tags_v3_by_ids(tag_ids))

        # v7.29.0 变更，不再扩展查询，扩展关系交给策略
        tags_v3 = TagV3Service.get_tags_v3_by_ids(tag_ids)
        return tags_v3

    @classmethod
    def tags_by_group_id(cls, group_id):

        tag_ids = list(
            GroupTag.objects.filter(
                group_id=group_id, is_online=True).values_list("tag_id", flat=True)
        )

        agile_tag_ids = []
        mapping = AgileTagMapping.objects.filter(old_tag_id__in=tag_ids)
        for item in mapping:
            tag_ids.append(item.old_tag_id)
            agile_tag_ids.append(item.agile_tag_id)

        tags = Tag.objects.filter(pk__in=tag_ids, is_online=True)

        children = chain(*[tag.online_child_tags() for tag in tags])
        parent = chain(*[tag.online_parent_tags() for tag in tags])

        tags_v3 = cls.tags_v3_by_group_id(group_id)

        return {
            "children": [to_dict(t, fields=('id', 'name', 'tag_type')) for t in children],
            "parent": [to_dict(t, fields=('id', 'name', 'tag_type')) for t in parent],
            "tags": [to_dict(t, fields=('id', 'name', 'tag_type')) for t in tags],
            "tags_v3": list(tags_v3.values()),
        }

    @classmethod
    def list_by_tag_v3_ids(cls, tag_ids):

        group_tags = list(GroupTagV3.objects.filter(
            tag_v3_id__in=tag_ids, is_online=True
        ).values("group_id", "tag_v3_id"))
        group_ids = [item["group_id"] for item in group_tags]
        groups = Group.objects.filter(pk__in=group_ids, is_online=True).in_bulk(group_ids)
        tag_groups = defaultdict(list)
        from group.tasks.tasks import GROUP_INFO_KEY
        for group_tag in group_tags:

            group = groups.get(group_tag["group_id"])
            if not group:
                continue
            cache_key = GROUP_INFO_KEY.format(group.id)
            extra_info = json.loads(group_cache.get(cache_key) or "{}")
            group_reply_count = extra_info.get('reply_count', 0)
            group_content_count = extra_info.get('content_count', 0)
            tag_groups[str(group_tag["tag_v3_id"])].append({
                "id": group.id,
                "name": group.name,
                "group_type": group.group_type,
                "header_image": group.header_image,
                "reply_count": group_reply_count,
                "content_count": group_content_count,
            })

        return dict(tag_groups)

    @classmethod
    def list_ids_by_tag_v3_ids(cls, tag_ids):
        # 根据标签id，返回在线的group_id跟tag_id的对应关系
        group_tag_v3_ids = list(GroupTagV3.objects.filter(
            tag_v3_id__in=tag_ids, is_online=True
        ).values_list('group_id', 'tag_v3_id'))
        # 此处同一个group可能对应多个标签，但从产品逻辑角度可忽略
        group_tag_v3_map = {_obj[0]: _obj[1] for _obj in group_tag_v3_ids}
        online_group_ids = list(Group.objects.filter(
            pk__in=group_tag_v3_map.keys(), is_online=True
        ).values_list('id', flat=True))
        return {_group_id: group_tag_v3_map[_group_id] for _group_id in online_group_ids}

    @classmethod
    def get_tag2group_map(cls):
        result = defaultdict(list)

        group_ids = list(Group.objects.filter(is_online=True).values_list(
            'id', flat=True
        ))
        group_tags = GroupTagV3.objects.filter(
            group_id__in=group_ids, is_online=True
        ).values('group_id', 'tag_v3_id')

        for _item in group_tags:
            result[_item['tag_v3_id']].append(_item['group_id'])

        offline_tag__ids = TagV3.objects.filter(id__in=result.keys(), is_online=False).values_list(
            'id', flat=True
        )

        for _offline_tag_ids in offline_tag__ids:
            result.pop(_offline_tag_ids)

        return dict(result)
