from collections import defaultdict
from django.db.models import Q, Sum, Count, F
from django.utils import timezone
from django.conf import settings
from collections import defaultdict

from gm_types.error import ERROR
from gm_types.mimas import (
    TRACTATE_PLATFORM,
    TRACTATE_REPLY_HIGHLIGHT
)
from gm_dataquery.dict_mixin import to_dict

from .base import BaseService
from utils.rpc import gen
from talos.libs.datetime_utils import get_timestamp_or_none
from talos.models.tractate import TractateReply, Tractate, TractateReplyImages
from talos.cache.base import tractate_reply_count_cache, tractate_reply_vote_count_cache


class TractateReplyService(BaseService):

    model = TractateReply

    TOP_ID_NULL = 0
    REPIED_ID_NULL = 0
    reply_cache_key = '{top_id}_reply_num'

    @classmethod
    def inc_reply_vote(cls, reply):
        tr = TractateReply.objects.get(id=reply.id)
        tr.vote_num = F('vote_num') + 1
        tr.save(update_fields=['vote_num'])
        return tractate_reply_vote_count_cache.incrby(str(reply.id), 1)

    @classmethod
    def dec_reply_vote(cls, reply):
        tr = TractateReply.objects.get(id=reply.id)
        if tr.vote_num > 0:
            tr.vote_num = F('vote_num') - 1
            tr.save(update_fields=['vote_num'])
            return tractate_reply_vote_count_cache.decr(str(reply.id), 1)
        return int(tractate_reply_vote_count_cache.get(str(reply.id)) or 0)

    @classmethod
    def healthy(cls, reply_id):
        if not reply_id:
            gen(ERROR.PARAMS_INCOMPLETE)

        reply = cls.get_by_id(pk=reply_id)

        if not reply:
            return gen(ERROR.REPLY_NOT_FOUND)

        if not reply.is_online:
            return gen(ERROR.REPLY_OFF_LINE)

        return reply

    @classmethod
    def get_replies_info(cls, reply_ids):
        replies = cls.model.objects.filter(pk__in=reply_ids, is_online=True)
        result = {}
        for item in replies:
            result[item.id] = to_dict(item)
            result[item.id]['create_time'] = get_timestamp_or_none(item.create_time)
            result[item.id]['update_time'] = get_timestamp_or_none(item.update_time)

        return result

    @classmethod
    def list_top_reply_by_tractate_ids(cls, tractate_ids, offset, count):
        """一组帖子的一级评论"""
        result = {}
        for tractate_id in tractate_ids:
            result[tractate_id] = cls.list_top_reply_by_tractate_id(tractate_id=tractate_id,
                                                                    offset=offset,
                                                                    count=count)
        return result

    @classmethod
    def list_top_reply_by_tractate_id(cls, tractate_id, offset, count, sort_condition=None, top_id=None):
        """帖子的一级评论"""

        if not sort_condition:
            sort_condition = '-id'

        replies = cls.model.objects.filter(
            tractate_id=tractate_id,
            top_id=cls.TOP_ID_NULL,
            is_online=True,
        )
        if top_id:
            replies = replies.exclude(id=top_id)

        top = None
        if top_id:
            top = cls.model.objects.filter(
                tractate_id=tractate_id,
                id=top_id,
                is_online=True,
            ).first()

        if isinstance(sort_condition, list) or isinstance(sort_condition, tuple):
            replies = replies.order_by(*sort_condition)
        else:
            replies = replies.order_by(sort_condition)

        count = count - 1 if top else count
        replies = list(replies[offset: offset + count])
        if top:
            replies.insert(0, top)

        result, target_info = [], []
        for item in replies:
            result.append({
                "id": item.id,
                "tractate_id": item.tractate_id,
                "replied_id": item.replied_id,
                "user_id": item.user_id,
                #"top_id": item.top_id, 暂时不传递
                "status": TRACTATE_REPLY_HIGHLIGHT.DARK,
                "reply_count": int(cls.top_reply_count(item.id)),
                "is_vote": False,

                "content": item.content,
                "vote_num": item.vote_num,
                "create_time": item.create_time.timestamp(),
            })

        return result

    @classmethod
    def offline_reply(cls, reply_id):
        """下线评论"""
        reply = cls.get_by_id(reply_id)
        if not reply:
            return gen(ERROR.TRACTATEREPLY_NOT_FOUND)

        reply.is_online = False
        reply.save()

    @classmethod
    def create(cls, content, user_id, tractate_id, extra={}):
        """创建评论。

        extra： 额外的参数。包括：top_id, replied_id, source_id, is_fake
        """

        reply = cls.model()

        reply.content = content
        reply.user_id = user_id
        reply.top_id = extra.get("top_id") or cls.TOP_ID_NULL
        reply.tractate_id = tractate_id

        reply.source_id = extra.get("source_id", TRACTATE_PLATFORM.GM)
        reply.replied_id = extra.get("replied_id") or cls.REPIED_ID_NULL
        reply.is_fake = extra.get("is_fake", False)

        reply.save()

        # 评论创建图片
        if extra.get("images", []):
            reply_images = [
                TractateReplyImages(
                    reply_id=reply.id,
                    image_url=item.get("image_url", ""),
                    width=item.get("width", 0),
                    height=item.get("height", 0)
                ) for item in extra["images"]
            ]
            TractateReplyImages.objects.bulk_create(reply_images)

        return reply

    @classmethod
    def top_reply_count(cls, top_reply_id):
        """一级评论下的评论数"""
        reply_num = tractate_reply_count_cache.get(cls.reply_cache_key.format(top_id=top_reply_id))
        if not reply_num:
            return 0
        return reply_num

    @classmethod
    def update_reply_count(cls, top_reply_id):
        reply_num = tractate_reply_count_cache.incr(cls.reply_cache_key.format(top_id=top_reply_id))
        return reply_num

    @classmethod
    def set_reply_count(cls, top_reply_id, num):
        return tractate_reply_count_cache.set(cls.reply_cache_key.format(top_id=top_reply_id), num)

    @classmethod
    def get_second_reply_by_top_id(
        cls, top_id, offset=0, count=10, sort_condition=None,
        target_reply_id=None, reply_id=None
    ):
        """获取一级评论下的二级评论"""
        if not sort_condition:
            sort_condition = '-id'

        top_replies = cls.model.objects.filter(
            top_id=top_id, is_online=True,
        )
        if reply_id:
            top_replies = top_replies.exclude(id=top_id)

        reply = None
        if offset == 0 and reply_id:
            reply = cls.model.objects.filter(
                id=reply_id, is_online=True,
            ).first()

        if isinstance(sort_condition, list) or isinstance(sort_condition, tuple):
            top_replies = top_replies.order_by(*sort_condition)
        else:
            top_replies = top_replies.order_by(sort_condition)

        count = count - 1 if reply else count
        top_replies = list(top_replies[offset: offset + count])
        if reply:
            top_replies.insert(0, reply)

        result = []
        for item in top_replies:
            temp_item = {
                "id": item.id,
                "tractate_id": item.tractate_id,
                "replied_id": item.replied_id,
                "user_id": item.user_id,
                "top_id": item.top_id,
                "status": TRACTATE_REPLY_HIGHLIGHT.DARK,
                "is_vote": False,

                "content": item.content,
                "vote_num": item.vote_num,
                "create_time": item.create_time.timestamp(),
                "replied_user_id": "",
                "replied_user_name": "",
                "replied_user_portrait": "",
            }
            if target_reply_id and target_reply_id == item.id:
                temp_item['status'] = TRACTATE_REPLY_HIGHLIGHT.LIGHT
            result.append(temp_item)

        return result

    @classmethod
    def get_highlight_reply(cls, target_reply_id):
        target_reply_info = cls.get_by_id(pk=target_reply_id)
        if not target_reply_info or not target_reply_info.is_online:
            return gen(ERROR.REPLY_NOT_FOUND)

        if target_reply_info.top_id:
            top_reply_info = cls.get_by_id(pk=target_reply_info.top_id)
            if not top_reply_info or not top_reply_info.is_online:
                return gen(ERROR.REPLY_NOT_FOUND)
        else:
            top_reply_info = target_reply_info

        top_info = {
            "id": top_reply_info.id,
            "tractate_id": top_reply_info.tractate_id,
            "replied_id": top_reply_info.replied_id,
            "user_id": top_reply_info.user_id,
            # "top_id": top_reply_info.top_id,
            "status": TRACTATE_REPLY_HIGHLIGHT.LIGHT if target_reply_id == top_reply_info.id else TRACTATE_REPLY_HIGHLIGHT.DARK,
            "reply_count": cls.top_reply_count(top_reply_info.id),
            "is_vote": False,

            "content": top_reply_info.content,
            "vote_num": top_reply_info.vote_num,
            "create_time": top_reply_info.create_time.timestamp(),
            "replies": [],
        }

        return top_info

    @classmethod
    def get_tractate_reply_count_info(cls, user_id):

        tractate_ids = list(Tractate.objects.filter(
            user_id=user_id, is_online=True
        ).values_list("id", flat=True))

        reply_ids = list(cls.model.objects.filter(
            user_id=user_id, is_online=True
        ).values_list("id", flat=True))

        qs = cls.model.objects.filter(
            Q(is_read=False, is_online=True) &
            (Q(tractate_id__in=tractate_ids) | Q(replied_id__in=reply_ids))
        ).exclude(user_id=user_id)

        count = qs.count()
        last_reply = qs.order_by("-create_time").first()

        return {
            "count": count,
            "last_user_id": last_reply.user_id if last_reply else None,
            "create_time": last_reply.create_time if last_reply else None,
        }

    @classmethod
    def read_all(cls, user_id):

        reply_ids = list(cls.model.objects.filter(
            user_id=user_id, is_online=True
        ).values_list("id", flat=True))
        cls.model.objects.filter(
            is_online=True, replied_id__in=reply_ids, is_read=False
        ).update(is_read=True, update_time=timezone.now())

        tractate_ids = list(Tractate.objects.filter(
            user_id=user_id, is_online=True
        ).values_list("id", flat=True))
        cls.model.objects.filter(
            is_online=True, tractate_id__in=tractate_ids, is_read=False
        ).update(is_read=True, update_time=timezone.now())

    @classmethod
    def get_sub_reply_ids(cls, reply_id):
        reply_ids = cls.model.objects.filter(replied_id=reply_id, is_online=True).values_list('id', flat=True)

        sub_reply_ids = []
        sub_reply_ids.extend(reply_ids)
        for reply_id in reply_ids:
            sub_reply_ids.extend(cls.get_sub_reply_ids(reply_id))
        return sub_reply_ids

    @classmethod
    def get_replies(cls, tractate_id, last_id, size):
        """
        获取一级评论和次级评论，一级评论按照时间先后排序，次级评论紧跟一级评论
        :return:
        """
        replys = list()
        top_replys = list(cls.model.objects.filter(
            tractate_id=tractate_id,
            top_id=0,
            is_online=True,
            id__gt=last_id
        )[0: size])

        # 依次查一级评论下二级评论，总数够size则返回
        for top_reply in top_replys:
            replys.append(top_reply)
            if len(replys) == size:
                break

            # 查询当前一级评论的次级评论
            sub_replys = list(cls.model.objects.filter(
                tractate_id=tractate_id,
                top_id=top_reply.id,
                is_online=True,
            )[0: size - len(replys)])

            replys.extend(sub_replys)

            if len(replys) == size:
                break

        return replys

    @classmethod
    def top_replies_by_tractate_ids(cls, tractate_ids, start_num=0, count=2, sort_type=None):
        """
        获取帖子一级评论
        :param tractate_ids:
        :param start_num:
        :param count:
        :param sort_type:  排序
        :return:
        """
        if not sort_type:
            sort_type = '-id'

        replies = cls.model.objects.using(settings.SLAVE_DB_NAME).filter(
            tractate_id__in=tractate_ids,
            top_id=cls.TOP_ID_NULL,
            is_online=True,
        ).order_by(sort_type)
        replies = list(replies[start_num: start_num + count])

        first_reply_dict, user_ids, first_reply_ids = {}, set(), []
        for item in replies:
            first_reply_dict[(item.tractate_id, item.id)] = {
                "reply_id": item.id,
                "tractate_id": item.tractate_id,
                "user_id": item.user_id,
                "status": TRACTATE_REPLY_HIGHLIGHT.DARK,
                "reply_count": int(cls.top_reply_count(item.id)),
                "is_vote": False,
                "content": item.content,
                "vote_num": item.vote_num,
                "create_time": item.create_time.timestamp(),
                "images": [],
            }
            user_ids.add(item.user_id)
            first_reply_ids.append(item.id)
        return {
            'first_replies': first_reply_dict,
            'first_reply_ids': first_reply_ids,
            'first_reply_user_ids': user_ids,
        }

    @classmethod
    def get_second_replies_by_first_ids(cls, first_reply_ids, count=1):
        """
        通过一级评论ids获取二级评论
        :param first_reply_ids:
        :param count:  需要1条二级评论
        :return:
        """
        if not first_reply_ids:
            return {}
        replies, second_reply_user_ids, second_reply_ids = {}, set(), []
        for _id in first_reply_ids:
            second_reply = cls.get_second_reply_by_top_id(
                top_id=_id, offset=0, count=count
            )
            if second_reply:
                second_reply = second_reply[0]
                second_reply['images'] = []

            else:
                continue
            replies[_id] = second_reply
            second_reply_user_ids.add(second_reply.get('user_id'))
            second_reply_ids.append(second_reply.get('id', 0))

        return {
            "second_reply": replies,
            "second_reply_user_ids": second_reply_user_ids,
            "second_reply_ids": second_reply_ids,
        }

    @classmethod
    def get_second_replies_count(cls, second_reply_ids):
        if not second_reply_ids:
            return {}
        replies = TractateReply.objects.using(settings.SLAVE_DB_NAME).filter(
            replied_id__in=second_reply_ids, is_online=True
        ).values('replied_id').annotate(count=Count('id')).values('replied_id', 'count')
        result = {}
        for item in replies:
            result[item['replied_id']] = item['count']

        return result

    @staticmethod
    def get_reply_images_by_reply_ids(reply_ids):
        """
        通过评论ID列表获取评论图片
        :param reply_ids:
        :return:
        """
        reply_images = TractateReplyImages.objects.filter(
            reply_id__in=reply_ids
        ).values("reply_id", "image_url", "width", "height")

        _result = defaultdict(list)
        for image_item in reply_images:
            reply_id = image_item.get("reply_id", 0)
            _data = {
                "image_url": image_item.get("image_url", ""),
                "width": image_item.get("width", 0),
                "height": image_item.get("height", 0),
            }
            _result[reply_id].append(_data)

        return dict(_result)

    @classmethod
    def get_reply_count_by_tractate_id(cls, tractate_id):
        return cls.model.objects.using(settings.SLAVE_DB_NAME).filter(
            tractate_id=tractate_id
        ).count()
