#!/usr/bin/env python
# -*- coding: utf-8 -*-

import datetime
import json
import uuid
from itertools import chain, groupby

from django.db.models import Q

from gm_types.gaia import (
    AI_QA_QUESTION_TYPE,
    AI_QA_NEXT_TYPE,
    AI_QA_MESSAGE_TYPE,
    TAG_V3_TYPE,
)

from agile.services import TagV3Service
from api.tool.datetime_tool import get_timestamp_epoch
from launch.models.ai_qa import (
    Theme,
    AIAnswer,
    AIAnswerTag,
    AIConsultInfo,
    AIMessage,
    AIMessageGroup,
    AIQuestion,
    AIQuestion2Answer,
    FollowQuestionGroup,
    RecommendCard,
)
from rpc.cache import ai_qa_msg_cache


class BaseService(object):
    model = None  # 必填
    base_query = Q(is_online=True)

    @staticmethod
    def filter_ids(ids):
        return set(map(int, filter(None, ids)))

    @staticmethod
    def get_objs_by_query_and_order(model, query, order_by=[]):
        """
        获取 query_set对象
        :param model:
        :param query:
        :param order_by:
        :return:
        """
        _queryset = model.objects.filter(
            query).order_by(*order_by)

        return _queryset

    @staticmethod
    def data_not_found_prompt():
        """
        数据未找到提示
        :return:
        """
        # 我这儿是不是要raise 下？
        return {}

    @staticmethod
    def get_format_data(fields, obj, fields_to_deserialize=None):
        """
        获取格式化数据
        :param fields:
        :param obj:
        :param fields_to_deserialize: 需要反序列化的字段，有的话需要传字段列表
        :return:
        """
        _data = {
            field: getattr(obj, field, "") for field in fields
        }

        if fields_to_deserialize:
            for _field in fields_to_deserialize:
                _data[_field] = json.loads(_data[_field])

        return _data


class ThemeService(BaseService):
    """
    获取主题相关数据
    """
    model = Theme
    fields = ["id", "first_welcome_contents", "redo_welcome_contents", "conclusion", "entrance_question_id", "ass_uid"]

    @classmethod
    def get_theme_info(cls, query, order_by):
        valid_theme = cls.get_objs_by_query_and_order(
            model=cls.model,
            query=query,
            order_by=order_by
        ).only(*cls.fields).first()

        if not valid_theme:
            return cls.data_not_found_prompt()

        return cls.get_format_data(
            cls.fields, valid_theme,
            fields_to_deserialize=['first_welcome_contents', 'redo_welcome_contents']
        )

    @classmethod
    def get_current_valid_theme(cls):
        """
        获取当前有效的内容
        :return:
        """
        return cls.get_theme_info(query=cls.base_query, order_by=["-id"])

    @classmethod
    def get_theme_info_by_id(cls, theme_id):
        """
        通过主题ID获取主题信息
        :param theme_id:
        :return:
        """
        return cls.get_theme_info(query=cls.base_query & Q(pk=theme_id), order_by=[])


class AIQuestionService(BaseService):
    """
    问题相关
    """
    model = AIQuestion
    fields = ["id", "title", "question_type"]

    @classmethod
    def get_question_by_id(cls, question_id):
        """
        通过问题ID查数据
        :param question_id:
        :return:
        """
        question_obj = cls.get_objs_by_query_and_order(
            model=cls.model,
            query=Q(pk=question_id)
        ).only(*cls.fields).first()

        if not question_obj:
            return cls.data_not_found_prompt()

        return cls.get_format_data(cls.fields, question_obj)


class AIAnswerService(BaseService):
    """
    回答相关
    """
    model = AIAnswer
    fields = ["id", "title", "image_url", "color", "answer_type"]

    @classmethod
    def get_answer_infos_by_answer_ids(cls, answer_ids):
        """
        通过回答IDS获取回答信息
        :param answer_ids:
        :return:
        """
        result = {}
        answer_ids = cls.filter_ids(answer_ids)
        if not answer_ids:
            return result

        answer_objs = cls.get_objs_by_query_and_order(
            model=cls.model,
            query=cls.base_query & Q(pk__in=answer_ids)
        ).only(*cls.fields)

        for answer_obj in answer_objs:
            result[answer_obj.id] = cls.get_format_data(cls.fields, answer_obj)

        return result

    @classmethod
    def get_answer_rel_tag_v3_infos_by_answer_ids(cls, answer_ids):
        """
        通过回答ID获取关联的3.0标签
        :param answer_ids:
        :return:
        """
        result = {}
        answer_ids = cls.filter_ids(answer_ids)
        if not answer_ids:
            return result

        answer_rel_tag_v3_ids = cls.get_objs_by_query_and_order(
            model=AIAnswerTag,
            query=Q(ai_answer_id__in=answer_ids)
        ).values_list("ai_answer_id", "tag_id")

        answer_map_tag_ids_dic = {}
        _func = lambda item: item[0]
        for answer_id, items in groupby(sorted(answer_rel_tag_v3_ids, key=_func), key=_func):
            if answer_id not in answer_map_tag_ids_dic:
                answer_map_tag_ids_dic[answer_id] = []

            _rel_tag_ids = list(map(lambda item: item[1], items))
            answer_map_tag_ids_dic[answer_id] = sorted(set(_rel_tag_ids), key=lambda x: _rel_tag_ids.index(x))

        tagv3_infos = TagV3Service.get_tags_v3_by_ids(set(chain.from_iterable(answer_map_tag_ids_dic.values())))

        for answer_id, _tag_ids in answer_map_tag_ids_dic.items():
            _tag_v3_data = []
            for _tag_id in _tag_ids:
                _tag_info = tagv3_infos.get(_tag_id)
                if _tag_info:
                    _tag_v3_data.append(_tag_info)

            result[answer_id] = _tag_v3_data

        return result

    @classmethod
    def get_answer_rel_tag_expand_infos(cls, tag_info_list):
        """
        获取回答关联的所有标签扩展后的数据
        :param tag_info_list:
        :return:
        """
        result = {
            "item_tag_ids": [],
            "item_same_appeal_ids": [],  # 项目标签同诉求下的项目标签
            "is_expand": False,  # 是否是扩展的
        }

        _tag_classify_dic = {}
        _item_tag_types = [TAG_V3_TYPE.NORMAL]  # 3级项目标签
        _filter_tag_types = list(chain(_item_tag_types, [TAG_V3_TYPE.FIRST_APPEAL, TAG_V3_TYPE.SECOND_APPEAL]))


        def get_attr_rel_item_ids(attr_ids):
            """
            获取属性关联的项目标签
            :param attr_ids:
            :return:
            """
            tagv3_infos = TagV3Service.list_tags_by_attrs(attr_ids).values()
            item_tag_ids = [
                tag_v3_info["id"] for tag_v3_info in tagv3_infos
                if tag_v3_info.get("tag_type") in _item_tag_types
            ]

            return item_tag_ids

        for tag_v3_info in tag_info_list:
            _tag_id = tag_v3_info.get("id", 0)
            _tag_type = tag_v3_info.get("tag_type")

            if _tag_type not in _tag_classify_dic:
                _tag_classify_dic[_tag_type] = []

            if _tag_type in _filter_tag_types:
                _tag_classify_dic[_tag_type].append(_tag_id)

            else:
                continue

        # 有项目类的标签，要扩展关联的同诉求下的三级项目标签
        item_tag_ids = list(chain.from_iterable(_tag_classify_dic.get(_tag_type, []) for _tag_type in _item_tag_types))
        if item_tag_ids:
            second_appeal_attrs = TagV3Service.list_second_appeal_attrs(item_tag_ids)
            result.update({
                "item_tag_ids": item_tag_ids,
                "item_same_appeal_ids": get_attr_rel_item_ids([attr.id for attr in chain.from_iterable(second_appeal_attrs.values())]),
            })

            return result

        # 无项目类
        first_appeal_ids = _tag_classify_dic.get(TAG_V3_TYPE.FIRST_APPEAL, [])
        second_appeal_ids = _tag_classify_dic.get(TAG_V3_TYPE.SECOND_APPEAL, [])

        if first_appeal_ids:  # 若有一级诉求标签
            _expand_rel_tag_ids = TagV3Service.get_child_tags(
                parent_ids=first_appeal_ids,
                parent_id_type=TAG_V3_TYPE.FIRST_APPEAL,
                child_id_type=TAG_V3_TYPE.SECOND_APPEAL
            )
            second_appeal_ids.extend(list(map(lambda item: item[1], _expand_rel_tag_ids)))

        if second_appeal_ids:  # 若有二级诉求标签
            result.update({
                "item_same_appeal_ids": get_attr_rel_item_ids(second_appeal_ids),
                "is_expand": True,
            })

        return result


class RecommendCardService(BaseService):
    """
    推荐卡片相关
    """
    model = RecommendCard
    fields = ["id", "title", "desc", "url", "scheme_popup"]

    @classmethod
    def get_recommend_card_info_by_ids(cls, reco_card_ids):
        """
        获取推荐卡片数据信息
        :param reco_card_ids:
        :return:
        """
        result = {}
        reco_card_ids = cls.filter_ids(reco_card_ids)
        if not reco_card_ids:
            return result

        reco_objs = cls.get_objs_by_query_and_order(
            model=cls.model,
            query=cls.base_query
        ).only(*cls.fields)

        for reco_obj in reco_objs:
            result[reco_obj.id] = cls.get_format_data(cls.fields, reco_obj)

        return result


class FollowQuestionGroupService(BaseService):
    """推荐问题组相关"""
    model = FollowQuestionGroup
    fields = ["id", "title", "content"]

    @classmethod
    def get_follow_question_group_info_by_id(cls, group_id):
        """
        通过问题组ID获取详细数据
        :param group_id:
        :return:
        """
        follow_group_obj = cls.get_objs_by_query_and_order(
            model=cls.model,
            query=cls.base_query & Q(pk=group_id)
        ).only(*cls.fields).first()

        if not follow_group_obj:
            return cls.data_not_found_prompt()

        data = cls.get_format_data(
            cls.fields,
            follow_group_obj,
            fields_to_deserialize=["content"]
        )

        data["question_group"] = data.pop("content", []) or []

        return data


class AIConsultInfoService(BaseService):
    """
    私信 咨询问题文案
    """
    model = AIConsultInfo
    fields = ["id", "title"]

    @classmethod
    def get_all_consult_infos(cls, count):
        """
        获取所有的咨询问题文案列表
        :return:
        """

        objs = cls.get_objs_by_query_and_order(
            model=cls.model,
            query=cls.base_query,
            order_by=["id"]
        ).only(*cls.fields)[:count]

        return [cls.get_format_data(cls.fields, obj) for obj in objs]


class AIQuestion2AnswerService(BaseService):
    """
    问答关联相关
    """
    model = AIQuestion2Answer

    @classmethod
    def get_rel_answer_ids_by_question_id(cls, question_id):
        """
        通过问题ID获取关联的回答ID
        :param question_id:
        :return:
        """
        fields = ["ai_question_id", "ai_answer_id", "rank"]
        qa_rel_objs = cls.get_objs_by_query_and_order(
            model=cls.model,
            query=Q(ai_question_id=question_id),
        ).values(*fields)

        answer_ids = []
        for item in sorted(qa_rel_objs, key=lambda item: item.get("rank", 999)):
            answer_ids.append(item["ai_answer_id"])

        return answer_ids

    @classmethod
    def get_rel_info_by_answer_id(cls, answer_id):
        """
        通过回答ID找关联的数据
        :param answer_id:
        :return:
        """
        fields = ["ai_question_id", "next_type", "relation_data"]
        qa_rel_info = cls.get_objs_by_query_and_order(
            model=cls.model,
            query=Q(ai_answer_id=answer_id)
        ).only(*fields).first()

        if not qa_rel_info:
            return cls.data_not_found_prompt()

        return cls.get_format_data(fields, qa_rel_info, fields_to_deserialize=["relation_data"])

    @staticmethod
    def get_qa_infos_by_question_id(question_id):
        """
        通过问题获取问题数据 + 回答列表
        :param question_id:
        :return:
        """
        result = {}
        question_info = AIQuestionService.get_question_by_id(question_id)

        if not question_info:
            return result

        _answer_ids = AIQuestion2AnswerService.get_rel_answer_ids_by_question_id(question_id)
        answers_dic = AIAnswerService.get_answer_infos_by_answer_ids(_answer_ids)
        question_info["answers"] = [answers_dic[_aid] for _aid in _answer_ids if _aid in answers_dic]

        return question_info

    @staticmethod
    def get_qa_rel_reco_card_list(reco_card_ids):
        """
        获取qa关联的推荐卡片列表
        :param reco_card_ids:
        :return:
        """
        result = []
        if not reco_card_ids:
            return result

        _raw_reco_card_ids = list(map(int, filter(None, reco_card_ids)))
        _reco_card_ids = sorted(set(_raw_reco_card_ids), key=lambda item: _raw_reco_card_ids.index)
        reco_card_dic = RecommendCardService.get_recommend_card_info_by_ids(_reco_card_ids)

        return [reco_card_dic[_card_id] for _card_id in _reco_card_ids if _card_id in reco_card_dic]

    @classmethod
    def get_next_data_by_answer_id(cls, answer_id):
        """
        通过回答ID获取下一步的内容
        :param answer_id:
        :return:
        """
        fields = ["ai_answer_id", "next_type", "relation_data"]
        next_rel_obj = cls.get_objs_by_query_and_order(
            model=cls.model,
            query=Q(ai_answer_id=answer_id)
        ).only(*fields).first()

        if not next_rel_obj:
            return cls.data_not_found_prompt()

        rel_data = cls.get_format_data(fields, next_rel_obj, fields_to_deserialize=["relation_data"])

        _next_type = rel_data["next_type"]
        relation_data = rel_data["relation_data"]

        result = {
            "answer_id": answer_id,
            "next_type": _next_type,
            "next_rel_data": {},
        }

        if _next_type == AI_QA_NEXT_TYPE.AI_QUESTION:
            _ai_question_id = relation_data.get("question_id", 0)  # TODO AI 问题字段
            question_info = cls.get_qa_infos_by_question_id(_ai_question_id)
            if not question_info:
                return result

            result["next_rel_data"] = question_info

        elif _next_type == AI_QA_NEXT_TYPE.RECOMMEND_CARD:

            follow_group_infos = FollowQuestionGroupService.get_follow_question_group_info_by_id(
                relation_data.pop("follow_group_id", 0)
            )
            _data = {
                "reco_desc": relation_data.pop("reco_desc", ""),
                "follow_group_infos": follow_group_infos,
                "reco_card_infos": cls.get_qa_rel_reco_card_list(relation_data.pop("reco_card_ids", [])),
            }
            _data.update(relation_data)

            result["next_rel_data"] = _data

        return result

    @classmethod
    def get_next_rel_data_by_answer_id(cls, user_id, answer_id):
        """
        :param user_id:
        :param answer_id:
        :return:
        """
        result = cls.get_next_data_by_answer_id(answer_id)

        msg_base_data_from_cache = AIQAMessageService.get_group_id_and_ass_uid(user_id)
        result.update({
            "group_id": msg_base_data_from_cache.get("group_id", ""),
        })

        _next_type = result.get("next_type")
        follow_group_infos = (result.get("next_rel_data") or {}).get("follow_group_infos")

        # 若下一步为推荐卡片数据并且问题组数据没有的话，需要触发消息记录规则
        if _next_type == AI_QA_NEXT_TYPE.RECOMMEND_CARD and not follow_group_infos:
            msg_record_data = AIQAMessageService.msg_record(
                user_id,
                answer_id,
                AI_QA_MESSAGE_TYPE.QUESTION_GROUP,
                {}
            )
            if msg_record_data.pop("can_finished", False):  # 如果能结束，则终止
                _group_id = result.get("group_id", "")
                AIQAMessageService.update_msg_group_status(  # 更新消息组状态
                    query=Q(user_id=user_id, group_id=_group_id)
                )
                AIQAMessageService.del_cache(user_id)

        return result


class AIQAMessageService(BaseService):
    """
    消息相关
    """
    msg_fields = ["id", "group_id", "user_id", "ass_uid", "msg", "type", "ident", "create_time"]
    group_cache_name = "ai_msg_group_uuid"
    msg_ass_uid_cache_name = 'ai_msg_ass_uids'

    @classmethod
    def get_group_id_and_ass_uid(cls, user_id, only_get_ass_uid=False):
        """
        获取组ID + 消息用户
        :param user_id:
        :param only_get_ass_uid: 仅获取关联的消息用户信息
        :return:
        """
        result = {
            "group_id": "",
            "ass_uid": 0,
        }

        group_id = ai_qa_msg_cache.hget(cls.group_cache_name, user_id)
        msg_ass_uid = ai_qa_msg_cache.hget(cls.msg_ass_uid_cache_name, user_id)

        if not all([group_id, msg_ass_uid]):  # 两个值都不存在的情况下，查下消息组数据
            group_obj = cls.get_objs_by_query_and_order(
                model=AIMessageGroup,
                query=cls.base_query & Q(user_id=user_id, finished=False),
                order_by=["-id"]
            ).only("group_id", 'ass_uid').first()  # 看最后一个未结束的消息组是否存在

        else:
            group_obj = None

        if not msg_ass_uid:
            if group_obj:
                msg_ass_uid = group_obj.ass_uid
            else:
                valid_theme = ThemeService.get_current_valid_theme()
                msg_ass_uid = valid_theme.get("ass_uid", 0)

            ai_qa_msg_cache.hset(cls.msg_ass_uid_cache_name, user_id, msg_ass_uid)

        result["ass_uid"] = int(msg_ass_uid)

        if only_get_ass_uid:
            return result

        if not group_id:
            group_id = group_obj and group_obj.group_id or uuid.uuid4().hex
            ai_qa_msg_cache.hset(cls.group_cache_name, user_id, group_id)

        result["group_id"] = str(group_id)

        return result

    @classmethod
    def del_cache(cls, user_id):
        """
        删除缓存中的数据
        :param user_id:
        :return:
        """
        for cache_name in [cls.group_cache_name, cls.msg_ass_uid_cache_name]:
            ai_qa_msg_cache.hdel(cache_name, user_id)

    @classmethod
    def get_msg_group_by_query(cls, query, order_by=None):
        """
        通过查询条件获取消息组对象列表
        :param query:
        :param order_by:
        :return:
        """
        if not order_by:
            order_by = []

        return cls.get_objs_by_query_and_order(
            model=AIMessageGroup,
            query=query,
            order_by=order_by
        )

    @classmethod
    def update_msg_group_status(cls, query):
        """
        更新消息组状态
        """
        cls.get_msg_group_by_query(
            query
        ).update(finished=True, last_modified=datetime.datetime.now())

    @classmethod
    def get_user_unread_msg_group_count(cls, user_id):
        """
        获取用户未读的消息组数量
        :param user_id:
        :return:
        """
        return cls.get_msg_group_by_query(
            query=cls.base_query & Q(user_id=user_id, is_read=False)
        ).count()

    @classmethod
    def update_user_unread_msg_group_status(cls, user_id):
        """
        更新用户组消息的已读状态
        :param user_id:
        :return:
        """
        return cls.get_msg_group_by_query(
            query=cls.base_query & Q(user_id=user_id, is_read=False)
        ).update(is_read=True, update_time=datetime.datetime.now())

    @classmethod
    def get_user_last_msg_groups_info(cls, user_id, n):
        """
        获取用户最近 n 组的组ID
        :param user_id:
        :param n:
        :return:
        """
        result = {
            "group_ids": [],
            "ass_uids": []
        }
        group_ids = list(cls.get_msg_group_by_query(
            query=cls.base_query & Q(user_id=user_id),
            order_by=["-id"]
        ).values_list("group_id", 'ass_uid')[:n])

        for group_id, ass_uid in group_ids:
            result["group_ids"].append(group_id)

            if ass_uid not in result["ass_uids"]:
                result["ass_uids"].append(ass_uid)

        result["group_ids"] = result["group_ids"][::-1]  # 翻转下，最新回复的在后

        return result

    @classmethod
    def format_msg_info(cls, msg_obj):
        """
        格式化消息的内容
        :param msg_obj:
        :return:
        """
        _data = cls.get_format_data(
            cls.msg_fields,
            msg_obj,
        )
        # 创建时间切换为时间戳
        _data["create_timestamp"] = get_timestamp_epoch(_data.pop("create_time", None))

        return _data

    @classmethod
    def get_msg_info_by_group_ids(cls, group_ids):
        """
        获取组下的消息
        [
            AI_QA_MESSAGE_TYPE.RECOMMEND_CARD,
            AI_QA_MESSAGE_TYPE.FIRST_WEICOME,
            AI_QA_MESSAGE_TYPE.REDO_WEICOME
        ]: msg 为json序列化后的数据，对外处理时记得格式化
        :param group_ids:
        :return:
        """
        result = {}
        msg_objs = cls.get_objs_by_query_and_order(
            model=AIMessage,
            query=cls.base_query & Q(group_id__in=group_ids)
        ).only(*cls.msg_fields)

        for msg_obj in msg_objs:
            _group_id = msg_obj.group_id
            if _group_id not in result:
                result[_group_id] = []

            result[_group_id].append(cls.format_msg_info(msg_obj))

        for group_id, v in result.items():
            result[group_id] = sorted(v, key=lambda item: item["id"])

        return result

    @classmethod
    def get_lastest_msg_by_group_ids(cls, group_ids):
        """
        通过消息组ID获取最新的消息
        :param group_ids:
        :return:
        """
        msg_obj = cls.get_objs_by_query_and_order(
            model=AIMessage,
            query=cls.base_query & Q(group_id__in=group_ids),
            order_by=["-id"]
        ).only(*cls.msg_fields).first()

        if not msg_obj:
            return cls.data_not_found_prompt()

        return cls.format_msg_info(msg_obj)

    @classmethod
    def get_user_lastest_msg_info(cls, user_id):
        """
        获取用户最新的一条历史消息
        :param user_id:
        :return:
        """
        group_result = cls.get_user_last_msg_groups_info(user_id, 1)
        group_ids = group_result.get("group_ids", [])
        if not group_ids:
            return cls.data_not_found_prompt()

        return cls.get_lastest_msg_by_group_ids(group_ids)

    @classmethod
    def write_a_message_in_sql_by_theme(cls, user_id, msg_type):
        """
        为功能开始写入一条消息
        :param user_id:
        :param msg_type: # 消息类型
        :return:
        """
        new_base_data_from_cache = cls.get_group_id_and_ass_uid(user_id)

        result = {
            "status": False,  # 整个流程创建的状态
            'msg_record_status': False,  # 消息创建的状态
            "group_id": new_base_data_from_cache.get("group_id", ""),
            "ass_uid": new_base_data_from_cache.get("ass_uid", 0),
        }

        if msg_type in [AI_QA_MESSAGE_TYPE.CONCLUSION]:  # 如果是意外退出的话，则直接返回数据，不再记录
            return result

        _base_create_data = dict(
            group_id=new_base_data_from_cache["group_id"],
            is_online=True
        )

        msg_group_create_data = dict(
            user_id=user_id,
            ass_uid=new_base_data_from_cache["ass_uid"]
        )
        msg_group_create_data.update(_base_create_data)

        if msg_type == AI_QA_MESSAGE_TYPE.REDO_WEICOME:
            msg_group_create_default = {
                "is_read": True,
            }
        else:
            msg_group_create_default = {}

        # 记录欢迎语
        valid_theme_data = ThemeService.get_current_valid_theme()
        if valid_theme_data:
            defaults = {
                "ident": valid_theme_data["id"],
            }
            msg_create_data = {
                "user_id": new_base_data_from_cache["ass_uid"],
                "ass_uid": user_id,
                "type": msg_type,
            }

            if msg_type == AI_QA_MESSAGE_TYPE.FIRST_WEICOME:  # 首次进入
                defaults.update({
                    "msg": json.dumps(valid_theme_data["first_welcome_contents"]),
                })

            elif msg_type == AI_QA_MESSAGE_TYPE.REDO_WEICOME:   # 重新开始
                defaults.update({
                    "msg": json.dumps(valid_theme_data["redo_welcome_contents"]),
                })

            # elif msg_type == AI_QA_MESSAGE_TYPE.CONCLUSION:  # 意外退出
            #     defaults.update({
            #         "msg": valid_theme_data.get("conclusion", ""),
            #     })

            else:
                return result

            msg_group_obj, _ = AIMessageGroup.objects.get_or_create(
                defaults=msg_group_create_default,
                **msg_group_create_data
            )

            defaults.update({
                "data": json.dumps({
                    "msg_group_id": msg_group_obj.id,
                    "theme_id": valid_theme_data["id"],
                }),
            })
            msg_create_data.update(_base_create_data)
            _, msg_create_status = AIMessage.objects.get_or_create(
                defaults=defaults,
                **msg_create_data
            )

            result.update({
                "msg_record_status": msg_create_status,
                "status": True
            })

        return result

    @classmethod
    def msg_record(cls, user_id, answer_id, record_type, others):
        """
        消息记录
        :return:
        """
        # 通过 answer_id 找关联的问题ID.
        # 通过问题ID 找问题标题 + 问题类型记录
        # 若是问题组。则需要记录 问题组引导语、推荐卡片引导语、问题组消息、推荐卡片数据。消息结束，删除缓存
        will_bulk_create_list = []

        # 从缓存中获取基本创建数据
        msg_base_data_from_cache = AIQAMessageService.get_group_id_and_ass_uid(user_id)
        _group_id, _ass_uid = msg_base_data_from_cache["group_id"], msg_base_data_from_cache["ass_uid"]
        msg_create_base_data = {
            "is_online": True,
            "ident": answer_id,
            "group_id": _group_id,
        }

        result = {
            "record_status": False,
            "can_finished": False,
            "group_id": _group_id,
        }

        # 找关联信息
        answer_rel_info = AIQuestion2AnswerService.get_rel_info_by_answer_id(answer_id)
        if not answer_rel_info:
            return result

        # 需要记录 问题标题、回答文案
        if record_type in [AI_QA_MESSAGE_TYPE.BUTTON, AI_QA_MESSAGE_TYPE.LIST]:

            # 找回答的信息
            answer_info = AIAnswerService.get_answer_infos_by_answer_ids([answer_id]).get(answer_id, {})
            if not answer_info:
                return result

            # 找问题的信息
            question_info = AIQuestionService.get_question_by_id(answer_rel_info.get('ai_question_id', 0))
            if not question_info:
                return result

            if question_info["question_type"] == AI_QA_QUESTION_TYPE.BUTTON:
                _type = AI_QA_MESSAGE_TYPE.BUTTON
            else:
                _type = AI_QA_MESSAGE_TYPE.LIST

            qa_msg_create_data = {
                "type": _type,
                "data": json.dumps({
                    "answer_id": answer_id,
                    "question_id": question_info["id"],
                    "question_type": question_info["question_type"],
                })
            }
            qa_msg_create_data.update(msg_create_base_data)

            will_create_list = [
                {
                    "user_id": _ass_uid,
                    "ass_uid": user_id,
                    "msg": question_info["title"],
                },
                {
                    "user_id": user_id,
                    "ass_uid": _ass_uid,
                    "msg": answer_info.get("title", ""),
                }
            ]

            for item in will_create_list:
                item.update(qa_msg_create_data)
                will_bulk_create_list.append(item)

        elif record_type == AI_QA_MESSAGE_TYPE.QUESTION_GROUP:
            # 需要记录 问题组引导语、勾选的问题组信息、推荐卡片引导语、推荐卡片数据

            _next_rel_data = answer_rel_info.get("relation_data", {})
            # 问题组引导语
            question_group_info = FollowQuestionGroupService.get_follow_question_group_info_by_id(
                _next_rel_data.get("follow_group_id", 0)
            )
            # 推荐卡片引导语 + 推荐卡片数据 合并为一条数据
            reco_card_infos = AIQuestion2AnswerService.get_qa_rel_reco_card_list(
                _next_rel_data.get("reco_card_ids", [])
            )

            if not any([question_group_info, reco_card_infos]):  # 如果这两者数据都没有的话，则不更新状态
                return result

            will_create_list = []
            if question_group_info:  # 如果问题组的数据存在
                will_create_list.append({
                    "user_id": _ass_uid,
                    "ass_uid": user_id,
                    "type": AI_QA_MESSAGE_TYPE.QUESTION_GROUP_WELCOME,
                    "msg": question_group_info.get("title", ""),
                })

                # 勾选的问题组
                will_create_list.append({
                    "type": AI_QA_MESSAGE_TYPE.QUESTION_GROUP,
                    "user_id": user_id,
                    "ass_uid": _ass_uid,
                    "msg": others.get("community_question_msg", ""),
                })

            if reco_card_infos:
                # 推荐卡片引导语
                # will_create_list.append({
                #     "user_id": msg_base_data_from_cache["ass_uid"],
                #     "ass_uid": user_id,
                #     "type": AI_QA_MESSAGE_TYPE.RECO_CARD_WELCOME,
                #     "msg": _next_rel_data.get("reco_desc", ""),
                # })

                will_create_list.append({
                    "user_id": _ass_uid,
                    "ass_uid": user_id,
                    "type": AI_QA_MESSAGE_TYPE.RECOMMEND_CARD,
                    "msg": json.dumps({
                        "reco_desc": _next_rel_data.get("reco_desc", ""),
                        "reco_card_infos": reco_card_infos,
                    }),
                })

            qa_msg_create_data = {
                "data": json.dumps({
                    "answer_id": answer_id,
                    "question_group_id": _next_rel_data.get("follow_group_id", 0),
                    "reco_card_ids": _next_rel_data.get("reco_card_ids", []),
                    "selected_question_ids": others.get("com_question_ids", []),
                    "raw_msg": others.get("community_question_msg", ""),
                })
            }
            qa_msg_create_data.update(msg_create_base_data)
            for item in will_create_list:
                item.update(qa_msg_create_data)
                will_bulk_create_list.append(item)

            result["can_finished"] = True

        if will_bulk_create_list:
            AIMessage.objects.bulk_create([AIMessage(**item) for item in will_bulk_create_list])
            result["record_status"] = True

        return result
