from collections import defaultdict

from django.conf import settings

from gm_types.gaia import FAVOROBJECT, NotificationType
from gm_types.error import ERROR as CODES
from gm_rpcd.all import bind

from qa.service import QaFavorServices
from talos.libs.datetime_utils import get_timestamp_or_none
from talos.cache.base import favor_cache
from talos.decorators import list_interface
from talos.models.diary import DiaryFavor
from talos.models.topic import ProblemFavor
from talos.manager import diary_list_manager, topic_list_manager
from talos.services.tractate import TractateFavorService
from talos.services.tractate import TractateService
from talos.services import (
    UserService,
    UserConvertService,
)
from talos.tools.favor_tool import FavorTool
from utils.rpc import gen, get_current_user


def get_user_unread_favor_count(user_id):
    diary_favor = DiaryFavor.list_unread(user_id)
    if diary_favor:
        df_unread_count = diary_favor.count()
    else:
        df_unread_count = 0

    problem_favor = ProblemFavor.list_unread(user_id)
    if problem_favor:
        pf_unread_count = ProblemFavor.list_unread(user_id).count()
    else:
        pf_unread_count = 0
    tf_unread_count = TractateFavorService.get_unread_favorite_num(user_id)

    q_unread_count = QaFavorServices.get_message_tab_question_favor_count(user_id)
    a_unread_count = QaFavorServices.get_message_tab_answer_favor_count(user_id)
    qa_count = q_unread_count.get('count', 0) + a_unread_count.get('count', 0)

    return df_unread_count + pf_unread_count + tf_unread_count + qa_count


def list_tractates_by_favor_ids(favors):

    tractates = TractateService.list_tractate_by_ids(
        [item.tractate_id for item in favors]
    )
    return tractates


def list_topics_by_favor_ids(favors):

    topics = topic_list_manager.list_problems_by_ids(
        [item.problem_id for item in favors]
    )
    return topics


def list_diaries_by_favor_ids(favors):

    diaries = diary_list_manager.list_diaries_by_ids(
        [item.diary_id for item in favors]
    )
    return diaries


def list_question_by_favor_ids(favors):
    questions = QaFavorServices.get_question_by_ids(
        [qv.question_id for qv in favors]
    )
    return {
        question.id: question for question in questions
    }


def list_answer_by_favor_ids(favors):
    answers = QaFavorServices.get_answer_by_ids(
        [av.answer_id for av in favors]
    )
    return {
        answer.id: answer for answer in answers
    }


@bind('mimas/favors/received')
def tractate_favor_received(start_num=0, count=10):
    """消息页面的收藏通知。"""

    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    favor_tool = FavorTool(favor_cache, user.id)
    ids = favor_tool.get_favors_received(start_num, count)

    type_ids = defaultdict(list)
    for obj_id, _type in ids:
        type_ids[_type].append(obj_id)

    favor_objs_func = {
        FAVOROBJECT.DIARY: DiaryFavor.list_by_ids,
        FAVOROBJECT.TOPIC: ProblemFavor.list_by_ids,
        FAVOROBJECT.TRACTATE: TractateFavorService.list_by_ids,
        FAVOROBJECT.QUESTION: QaFavorServices.question_get_valid_favor,
        FAVOROBJECT.ANSWER: QaFavorServices.answer_get_valid_favor,
    }

    objs_func = {
        FAVOROBJECT.DIARY: list_diaries_by_favor_ids,
        FAVOROBJECT.TOPIC: list_topics_by_favor_ids,
        FAVOROBJECT.TRACTATE: list_tractates_by_favor_ids,
        FAVOROBJECT.QUESTION: list_question_by_favor_ids,
        FAVOROBJECT.ANSWER: list_answer_by_favor_ids,
    }

    # 获取对应的数据方法
    favor_objs = defaultdict(list)  # 收藏记录
    entries = defaultdict(dict)  # 收藏的对象
    for _type, _ids in type_ids.items():
        get_favor_objs_func = favor_objs_func.get(_type)
        if get_favor_objs_func:
            fvors = get_favor_objs_func(_ids)
            favor_objs[_type] = fvors
            items = objs_func.get(_type)(fvors)
            entries[_type] = items

    result = []
    user_ids = []
    for _type, objs in favor_objs.items():

        for obj in objs:
            user_ids.append(obj.user_id)

            favor_time = None
            if hasattr(obj, "update_time"):
                favor_time = obj.update_time.timestamp()
            elif hasattr(obj, "created_time"):
                favor_time = obj.created_time.timestamp()
            elif hasattr(obj, "create_time"):
                favor_time = obj.create_time.timestamp()

            info = {
                'vote_id': obj.id,
                'type': _type,
                'user_id': obj.user_id,
                'favor_time': favor_time,
            }
            if _type == FAVOROBJECT.DIARY:
                diary = entries.get(_type, {}).get(obj.diary_id)
                info["content"] = diary.title if diary else ""
                info["id"] = obj.diary_id

            elif _type == FAVOROBJECT.TOPIC:
                topic = entries.get(_type, {}).get(obj.problem_id)
                info["content"] = topic.answer if topic else ""
                info["id"] = obj.problem_id

            elif _type == FAVOROBJECT.TRACTATE:
                tractate = entries.get(_type, {}).get(obj.tractate_id)
                info["content"] = tractate.content if tractate else ""
                info["id"] = obj.tractate_id
            elif _type == FAVOROBJECT.QUESTION:
                question = entries.get(_type, {}).get(obj.question_id)
                info["content"] = question.content if question else ""
                info["id"] = obj.question_id
            elif _type == FAVOROBJECT.ANSWER:
                answer = entries.get(_type, {}).get(obj.answer_id)
                info["content"] = answer.content if answer else ""
                info["id"] = obj.answer_id

            result.append(info)

    users = UserConvertService.get_user_info_by_user_ids(user_ids)
    for item in result:

        user = users.get(item["user_id"])
        if not user:
            continue

        item.update({
            "user_info": user,
            # 以下是冗余字段
            "user_id": user.get("user_id", 0),
            "nickname": user.get("user_name", ""),
            "portrait": user.get("portrait", ""),
            "membership_level": user.get("membership_level", "0"),
        })

    # 根据初始顺序进行排序
    result.sort(key=lambda item: ids.index((str(item.get("vote_id")), item.get("type"))))

    return result


@bind('mimas/favor/received_aggregation')
def get_reseived_favor_aggregation():

    No_new_favor = {
        'is_about_me': False,
        'toptitle': '',
        'type': NotificationType.VOTE_FAVOR,
        'unread_count': 0,
        "title": u'收藏',
        "url": "",
        "content": "",
        "create_time": "",
        "user": "",
        "is_view": False,
        "imgheader": settings.NOTIFICATION_VOTE_IMG,
        "pic": "",
        "id": '',
    }
    user = get_current_user()
    if not user:
        return No_new_favor

    favor_tool = FavorTool(favor_cache, user.id)

    count = get_user_unread_favor_count(user.id)
    if count == 0:
        return No_new_favor

    # get latest favor user info: prevent cache id can not find favor item, try 3 times
    else:
        favor = None
        for r in favor_tool.get_favors_received(0, 3):
            _id, _type = r

            if _type == FAVOROBJECT.DIARY:
                try:
                    favor = DiaryFavor.objects.get(pk=_id)
                    user = UserService.get_user_by_user_id(favor.user_id)
                    return format_result(
                        user, count, create_time=get_timestamp_or_none(favor.created_time))

                except DiaryFavor.DoesNotExist:
                    continue

            if _type == FAVOROBJECT.TOPIC:
                try:
                    favor = ProblemFavor.objects.get(pk=_id)
                    user = UserService.get_user_by_user_id(favor.user_id)
                    return format_result(
                        user, count, create_time=get_timestamp_or_none(favor.created_time))

                except ProblemFavor.DoesNotExist:
                    continue

            if _type == FAVOROBJECT.TRACTATE:
                from talos.models.tractate.favor import TractateFavor
                try:
                    favor = TractateFavor.objects.get(pk=_id)
                    user = UserService.get_user_by_user_id(favor.user_id)
                    return format_result(
                        user, count, create_time=get_timestamp_or_none(favor.update_time))

                except TractateFavor.DoesNotExist:
                    continue

        if not favor:
            No_new_favor['unread_count'] = count
            return No_new_favor


def format_result(latest_user_info, count, create_time=None):

    nickname = latest_user_info.nickname
    portrait = latest_user_info.portrait

    title = '{}等人给你点了赞'.format(nickname) if count > 1 else '{}赞了你'.format(nickname)
    return {
        'is_about_me': False,
        'toptitle': '',
        'type': NotificationType.VOTE_ME,
        'unread_count': count,
        "title": title,
        "url": "",
        "content": "",
        "create_time": create_time,
        "user": "",
        "is_view": False,
        "imgheader": portrait,
        "pic": "",
        "id": '',
    }
