import operator
from django.db.models import Q, Sum, Count, F
from django.utils import timezone

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
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):
        """帖子的一级评论"""
        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,
        ).order_by(sort_condition)[offset: offset + count]

        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
        """

        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.save()
        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 get_second_reply_by_top_id(cls, top_id, offset=0, count=10, sort_condition=None, target_reply_id=None):
        """获取一级评论下的二级评论"""
        if not sort_condition:
            sort_condition = '-id'
        top_replies = cls.model.objects.filter(
            top_id=top_id, is_online=True,
        ).order_by(sort_condition)[offset: offset + count]

        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())
