# coding=utf-8

from __future__ import unicode_literals, absolute_import, print_function

import datetime, time
import re
from collections import defaultdict
from django.db import transaction
from django.db.models import Q, Count
from django.utils.html import escape
from gm_types.error import ERROR as CODES
from gm_types.gaia import DIARY_OPERATE
from gm_types.gaia import FILTER_WORD_TYPE
from gm_types.gaia import PROBLEM_FLAG_CHOICES
from gm_types.gaia import TAG_TYPE
from gm_types.gaia import TOPIC_TYPE
from gm_types.mimas import VOTE_TASK_TYPE

from talos.manager.topic import topic_list_manager

from talos.manager.topic_reply import comment_data_by_reply_ids
from talos.models.diary.diary import Diary
from talos.models.diary import DiaryExtra, DiaryFavor
from talos.models.draft import Draft
from talos.models.topic.topicreply import TopicReply
from talos.models.topic.video import Video
from talos.rpc import bind, bind_context
from talos.services import DoctorService
from talos.services import TagService
from talos.services import UserService
from talos.services import get_user_from_context
from talos.services.user import User
from talos.tasks.vote import process_topic_vote_for_create
from talos.tools.fake_vote_tool import is_special_user, fake_vote_v1
from talos.views.topic import topic_specialized
from utils.rpc import gen, logging_exception, get_current_user
from talos.models.topic import ProblemTag, Problem, TopicImage, TopicScore, TopicVote, ProblemFavor
from talos.libs.datetime_utils import get_timestamp_or_none
from talos.tools.filterword_tool import filterword_by_custom
from live.tasks import set_water_mark_to_video
from utils.UploadVideoPicture import UploadVideoPicture
from utils.group_routine import GroupRoutine


# todo fix the cache
@bind_context('topic/get/topic')
def get_topic_info_detail(ctx, id):
    topic_data = topic_specialized.get_topic_info(ctx, id)
    tag_v3_infos = topic_list_manager.get_topic_tagv3_info_by_topic_ids([id])
    topic_data["problem"].update({
        "tags_v3": tag_v3_infos.get(id, []),
    })

    return topic_data


@bind_context('topic/create')
def create_topic(
        ctx, topic_type_id, content,
        tag_ids=[], images=[], device_id=None,
        title=None, is_mixed=0, pure_words='', draft_id=None):
    """create discuss and consulting topic only."""
    if topic_type_id not in (TOPIC_TYPE.ASK, TOPIC_TYPE.TOPIC):
        return gen(CODES.OPERATION_NOT_SUPPORTED)
    pure_words = pure_words if pure_words else ""
    #创建话题时进行敏感词过滤，以前的代码进行了数字的过滤，并只过滤了pure_words部分 本次沿用
    filterword_by_custom(filter_type=FILTER_WORD_TYPE.TOPIC_CONTENT, content=pure_words)

    if not content.strip():
        return gen(CODES.TOPIC_CONTENT_CAN_NOT_BE_EMPTY)

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

    with transaction.atomic():
        p = Problem()
        p.topic_type = topic_type_id
        p.user_id = user.id
        p.answer = content
        p.ask = title
        p.is_mixed = is_mixed

        p.set_review_status(len(images))

        if device_id:
            p.device_id = device_id

        p.save()

        for image_url in images:
            if image_url:
                t = TopicImage(topic_id=p.id, image_url=image_url)
                t.save()

    # 处理创建topic之后的数据放大

    # 点赞灌水统一 http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=27102037
    # special_user = is_special_user(user_id=user.id)
    # fake_vote_v1(task_type=VOTE_TASK_TYPE.TOPIC, business_id=p.id, special_user=special_user)

    has_geo_tag = False
    tags = TagService.get_tags_by_tag_ids(tag_ids)
    for tag in tags:
        if tag.tag_type in (TAG_TYPE.PROVINCE, TAG_TYPE.CITY):
            has_geo_tag = True

        ProblemTag(problem=p, tag_id=tag.id).save()

    user_city_tagid = user.city_tag_id
    if not has_geo_tag and user_city_tagid:
        tag = TagService.get_tag_by_tag_id(user_city_tagid)
        if tag is None:
            logging_exception()
        else:
            ProblemTag(problem=p, tag_id=tag.id).save()

    tag_first = p.problemtag_set.first()
    if tag_first:
        tag_id = tag_first.tag_id
        # tag = TagService.get_tag_by_tag_id(tag_id)
        tag =TagService.get_tag_by_tag_id(tag_id)
        for item in tag:
            data = {'tag_id': item.get("tag_id"), 'name': item.get("tag_name")}
    else:
        data = {}

    data['topic_id'] = p.id

    if draft_id:
        try:
            draft_id = int(draft_id)
            draft = Draft.objects.get(pk=draft_id)
            draft.delete()
        except:
            logging_exception()

    return data


@bind('topic/delete')
def mark_topic_as_delete(id):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    try:
        problem = Problem.objects.get(pk=id)
    except Problem.DoesNotExist:
        return gen(CODES.TOPIC_NOT_FOUND)

    if user and problem.user_id == user.id:  # 是自己发布的帖子
        if problem.reply_num != 0:  # 有评论不能自己删除
            if problem.topic_type != TOPIC_TYPE.TOPIC:  # 医生也只能没限制的删除讨论帖
                return gen(CODES.ASK_SUOZHANG_TO_DELETE_TOPIC)

            if UserService.user_is_doctor(user.id):  # 但是医生除外，2.2.0医生端可以任意删除自己的讨论帖
                return gen(CODES.ASK_SUOZHANG_TO_DELETE_TOPIC)

        problem.flag = PROBLEM_FLAG_CHOICES.MARK_DELETED
        problem.is_online = False
        problem.is_public = False
        problem.last_modified = datetime.datetime.now()
        problem.save()

        # 帖子关联日记本时间更新
        if problem.diary:
            problem.diary.last_modified = datetime.datetime.now()
            problem.diary.save()

        user_info = UserService.get_user_by_user_id(user.id)
        user_info.decr_topic_count()

        # 记录操作 DELETE_TOPIC
        if problem.diary is not None:
            problem.diary.set_diary_operate(DIARY_OPERATE.DELETE_TOPIC, problem.id)

            try:
                diary_extra = DiaryExtra.objects.get(diary=problem.diary)
                if diary_extra.topic_count:
                    diary_extra.topic_count -= 1
                    diary_extra.save()
            except DiaryExtra.DoesNotExist:
                pass
    else:
        return gen(CODES.NO_PERMISSION)

    return gen(CODES.SUCCESS)


@bind("topic/update_one")
def topic_update_one(topic_id, content=None, images=[]):
    """话题更新
    topic_ids: 话题ID, 必选
    content: 回复的内容,
    is_online: 下线
    """
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)
    topic = Problem.get_by_id(topic_id)

    if not topic or topic.user_id != user.id:
        gen(CODES.TOPIC_NOT_FOUND)

    if not topic.is_online:
        return gen(CODES.TOPIC_HAS_BEEN_DELETED)

    if content:
        topic.answer = content

    if images is None:
        images = []

    if len(images) > 0:
        with transaction.atomic():
            TopicImage.objects.filter(topic=topic).delete()  # 删掉老帖子
            for image in images:
                if not all(i in image for i in ('image', 'type')):
                    continue

                TopicImage.objects.create(
                    topic=topic, image_url=image['image'],
                    image_type=image['type']
                )  # 添加新帖子

    topic.set_review_status(len(images))
    topic.last_modified = datetime.datetime.now()
    topic.save()

    # 帖子关联日记本时间更新
    if topic.diary:
        topic.diary.last_modified = datetime.datetime.now()
        topic.diary.save()

    # 记录操作 UPDATE_TOPIC
    if topic.diary is not None:
        topic.diary.set_diary_operate(DIARY_OPERATE.UPDATE_TOPIC, topic.id)


@bind("topic/get")
def get_topic(topic_id):
    try:
        topic = Problem.objects.get(pk=topic_id)

        if not (topic.is_online and topic.flag == PROBLEM_FLAG_CHOICES.NORMAL):
            return gen(CODES.TOPIC_NOT_FOUND)

        return topic.data()

    except Problem.DoesNotExist:
        return gen(CODES.TOPIC_NOT_FOUND)


@bind('topic/get_topic')
def get_topic_info(topic_id):
    """
    NOTE:
        DESC: 获取帖子信息
        :param topic_id: int
        :return:
        version: v5.7
    """
    try:
        topic = Problem.get_by_id(topic_id)
        if topic is None or not topic.visible:
            return gen(CODES.TOPIC_NOT_FOUND)

        image = topic.images.first() if hasattr(topic, 'images') else None
        image_url = image.image_url if image else ''

        user = UserService.get_user_by_user_id(topic.user_id)
        result = {
            'id': topic.id,
            'title': topic.title,
            'reply_num': topic.reply_num,
            'user_id': topic.user_id,
            'nick_name': user.nickname,
            'portrait': user.portrait,
            'content': topic.answer,
            'membership_level': user.membership_level,
            'doctor_id': '',
            'hospital_id': '',
            'image_url': image_url,
            'diary_id': topic.diary_id,
        }

        user_bz = UserService.get_doctor_hospital_id_by_user_id(topic.user_id)
        if user_bz:
            result['user_id'] = user_bz['user_id']
            result['doctor_id'] = user_bz['doctor_id']
            result['hospital_id'] = user_bz['hospital_id']

        result['share_data'] = topic.get_topic_share_data_from_db()

        return result

    except Problem.DoesNotExist:
        return gen(CODES.TOPIC_NOT_FOUND)


@bind('topic/get_doctor_topics')
def get_doctor_topics(doctor_id=None, start_num=0, count=10, subtype=0):
    """医生参与的话题
       subtype: 0 表示查询结果集按 id 排序，否则按 like_num 和 id 排序
    """
    replies_ids = TopicReply.objects.filter(doctor_id=doctor_id, problem__isnull=False,
                                            problem__is_online=True, is_online=True).values_list('id', flat=True)

    # 排序(按照id的降序排列)，分页
    if subtype == "0":
        replies_ids = replies_ids.order_by("-id")[start_num:start_num + count]
    else:
        # 按照like_num降序排列，like_num相同的按照id降序排列
        replies_ids = replies_ids.order_by("-like_num", "-id")[start_num:start_num + count]

    replies = TopicReply.objects.filter(id__in=list(replies_ids))

    return [reply.get_reply_info_for_doctor() for reply in replies]


@bind('topic/get_topic_num_by_ids')
def topic_vote_amount(ids):
    """
    :param ids: topic_id list
    :return:
    [
        {
            'id': 1,
            'data': {
                'vote_amount': int,
                'view_amount': int,
            }
        }
    ]
    """
    if len(ids) == 0:
        return []

    topics = Problem.objects.filter(id__in=ids)
    result = []
    for topic in topics:
        data = {
            'vote_amount': topic.vote_amount,
            'view_amount': Problem.get_view_amount_of_id(topic.id),
        }
        d = {
            'id': topic.id,
            'data': data,
        }
        result.append(d)
    return result


@bind('topic/get_topic_info_by_ids')
def topic_info(ids, user_id=None):
    """
    :param ids: topic_id list
     user_id
    :return:
    [
        {
            'id': 1,
            'data': topic_info 一个大字典
        }
    ]
    """
    if len(ids) == 0:
        return []

    user = User(user_id, None, None, None)
    topics = Problem.objects.filter(id__in=ids)
    result = []
    for topic in topics:
        d = {
            'id': topic.id,
            'data': topic.get_topic_info(user),
        }
        result.append(d)
    return result


@bind('topic/get_topic_info_by_user_ids')
def topic_info_by_user_ids(user_ids, viewer_user_id=None, topic_id_only=False):
    """

    :param user_ids:
    :param topic_id_only:
    :return:
    [{
        "id": 1,
        "user_id": 2,
        "data": topic_info
    }, ...]
    """
    if len(user_ids) == 0:
        return []

    topics = Problem.objects.filter(user_id__in=user_ids, is_online=True)
    result = []
    for topic in topics:
        d = {'id': topic.id, 'user_id': topic.user_id}
        if not topic_id_only:
            d['data'] = topic.get_topic_info(viewer_user_id)
        result.append(d)
    return result


@bind('topic/get_tags_by_topic_ids')
def topic_get_tags(ids):
    """
    :param ids: topic_id list
    :return:
    [
        {
            'id': 1,
            'data': [{
                'name': str,
                'tag_id': int,
            }, ...]
        }
    ]
    """
    if len(ids) == 0:
        return []

    topic_tags = ProblemTag.objects.filter(problem_id__in=ids)
    topic_tag_dict = defaultdict(lambda: [])
    tags = []
    for tt in topic_tags:
        topic_tag_dict[tt.problem_id].append(tt.tag_id)
        tags.append(tt.tag_id)

    tag_objs = TagService.get_tags_by_tag_ids(list(set(tags)))
    tag_obj_dict = {t.id: t for t in tag_objs}

    result = []
    for topic_id in topic_tag_dict:
        tag_ids = topic_tag_dict[topic_id]
        tag_data = []
        for tag_id in tag_ids:
            tag_obj = tag_obj_dict.get(tag_id)
            if not tag_obj:
                continue

            tag_data.append({'tag_id': tag_obj.id, 'name': tag_obj.name})

        d = {
            'id': topic_id,
            'data': tag_data,
        }
        result.append(d)
    return result


@bind('topic/update_reply_num_by_ids')
def topic_update_reply_num(ids, is_online=False):
    """
    :param ids: id list, is_online
    :return:
    [
        {
            'id': 1,
            'data': bool,
        }
    ]
    """
    if len(ids) == 0:
        return []

    topics = Problem.objects.filter(id__in=ids)
    result = []
    for topic in topics:
        d = {
            'id': topic.id,
            'data': topic.update_reply_num(is_online),
        }
        result.append(d)
    return result


@bind_context('topic/check')
def topic_check(ctx, start_num, count, reply_id):
    result = {}
    user = get_user_from_context(ctx)
    try:
        reply = TopicReply.objects.get(pk=reply_id)
        try:
            if reply.problem.diary:
                # 如果是某一个日记本下的回复的话，则跳转到兼容的方法处理
                return TopicReply.reply_detail_diary(user=user, reply_id=reply_id, start_num=start_num, count=count)
        except:
            pass
        detail = reply.get_reply_detail(user.id)
        result.update(detail)
        result['reply_id'] = detail['reply']['reply_id']
        result['content'] = detail['topic']['problem_answer']
        result['user_portrait'] = detail['user']['portrait']
        result['user_id'] = detail['user']['user_id']
        result['doctor_id'] = detail['user']['doctor_id']
        result['user_nickname'] = detail['user']['user_name']
        result['doctor_name'] = detail['user']['doctor_name']
        result['favor_amount'] = detail['reply']['favor_amount']
        result['reply_date'] = detail['reply']['reply_date']

        result['comments'] = []
        result['topic_id'] = reply.problem.id
        result['title'] = reply.problem.get_title()
        result['comment_count'] = reply.comments.count()

        doctor_reply_sql = """
            (case when api_topicreply.doctor_id !='' then 1 else 0 end)
            """
        comments = reply.comments.extra(
            select={'doctor_reply': doctor_reply_sql})
        comments = comments.extra(order_by=['-doctor_reply', 'id'])
        comments = comments[start_num: start_num + count]
        _comments_ids = [c.id for c in comments]
        result['comments'] = comment_data_by_reply_ids(_comments_ids)
        return result
    except TopicReply.DoesNotExist:
        result = {'error': 1, 'message': u'不存在'}
        result['notexists'] = True
        return result


@bind('topic/create/check')
def create_check(content="", is_new_version=False):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)
    dd = datetime.datetime.now() - datetime.timedelta(seconds=30)
    user_problems = Problem.objects.filter(user_id=user.id, created_time__gt=dd)
    if user_problems and not is_new_version:
        return {'error': 1, 'message': u'为避免恶意发布，请间隔30s后再发'}    # TODO 把msg迁移至下游服务? 目前来说是没时间精力...

    if not DoctorService.doctor_can_create_topic(user_id=user.id):
        return {'error': 1, 'message': u'抱歉，您本周发帖次数已达上限'}

    # 对日记贴更新进行敏感词过滤，这里是对帖子部分进行过滤，以前的代码进行了数字的过滤，本次沿用
    content = content if content else ""
    filterword_by_custom(filter_type=FILTER_WORD_TYPE.TOPIC_CONTENT, content=content)
    return {"error": 0}


@bind_context('topic/pre_create_topic')
def topic_pre_info(ctx, diary_id):
    user = get_user_from_context(ctx)
    if not user:
        return gen(CODES.LOGIN_REQUIRED)
    try:
        diary = Diary.objects.get(pk=diary_id, user_id=user.id)
    except:
        return gen(CODES.DIARY_NOT_FOUND)

    if diary.operation_time:
        days = (datetime.datetime.now() - diary.operation_time).days

        # v7.6.25 不管时间如何 都必须返回获取到的时间,当天的零点时间戳 整数类型
        timestamp = int(time.mktime(diary.operation_time.replace(hour=0, minute=0, second=0).timetuple()))
    else:
        days = 0
        timestamp = None  # 避免取不到的情况 直接制空

    diary_tags = diary.tags
    if diary_tags:
        tags = [
            {'tag_id': tag.id, 'name': tag.name}
            for tag in diary_tags
            if tag.tag_type in [
                TAG_TYPE.BODY_PART,
                TAG_TYPE.BODY_PART_SUB_ITEM,
                TAG_TYPE.ITEM_WIKI
            ]
        ]
    else:
        tags = []

    # change 09-10, 对用户服务上传视频不做限制
    can_create_video = user.can_create_video

    topic_nums = diary.topic_num
    return {
        'days': days,
        'timestamp': timestamp,
        'tags': tags,
        'can_create_video': can_create_video,
        'topics_num': topic_nums,
    }


@bind("topic/sink")
def topic_sinking(topic_id):
    """
        话题下沉
    """
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)
    try:
        topic = Problem.objects.get(id=topic_id)
        topic_score, created = TopicScore.objects.get_or_create(topic_id=topic.id, user_id=user.id)
        topic_score.score = 0
        topic_score.save()
    except:
        return {
            'updated': False,
        }
    return {
        'updated': True,
    }


@bind('topic/info_for_doctor_notification')
def info_for_notification(topic_id):
    if topic_id is None:
        return None
    try:
        topic = Problem.objects.get(id=topic_id)
    except Problem.DoesNotExist:
        return gen(CODES.TOPIC_NOT_FOUND)

    topic_info = {
        'id': topic.id,
        'content': escape(topic.answer),
    }
    image = topic.images.first()
    if image:
        topic_info['image'] = image.image_url
    else:
        u = UserService.get_user_by_user_id(topic.user_id)
        topic_info['image'] = u.portrait

    diary = topic.diary
    info = {
        'id': diary.id,
        'diary_title': diary.title,
        'service_id': diary.service_id,
        'topic': topic_info,
    }
    return info


@bind('topic/get_user_topics_count')
def get_user_topics_count(user_id):
    query = Q(is_online=True, user_id=user_id) & (Q(topic_type=TOPIC_TYPE.ACTIVITY))
    return Problem.objects.filter(query).count()


@bind_context('topic/get_index_banner_topic_by_ids')
def get_index_banner_topic_by_ids(ctx, topic_ids):
    user = get_user_from_context(ctx)
    _info = topic_list_manager.generate_info_for_index_banner_by_ids(topic_ids, user.id)
    return _info


@bind('topic/check_topic')
def check_topic_for_multi_topic(topic_id):
    try:
        p = Problem.objects.get(id=topic_id)
        if p.user_id == 22:
            return False, []
        if p.topic_type != TOPIC_TYPE.SHARE:
            return False, []
        return True, p.get_tags()

    except Problem.DoesNotExist:
        return False, []


@bind('topic/get_topic_count_by_tag_ids')
def get_topic_count_by_tag_ids(tag_ids, annotate=False):
    '''
        粗略计算标签下的日记帖数,之后如果要加精细的过滤条件, 可直接调整, 目前只用于非精确的统计展示
    '''
    if annotate:
        topic_count_list = ProblemTag.objects.filter(tag_id__in=tag_ids). \
            values("tag_id").annotate(count=Count('problem_id'))
        return {str(item['tag_id']): item['count'] for item in topic_count_list}

    diary_topic_count = ProblemTag.objects. \
        filter(tag_id__in=tag_ids). \
        values_list('problem', flat=True).distinct().count()

    return {'diary_topic_count': diary_topic_count}


@bind('topic/update_video_cover')
def update_video_cover(topic_id, video_url, second=0):
    cover_url = UploadVideoPicture(url=video_url, second=second)
    video=Video.objects.get(topic_id=topic_id)
    video.video_pic=cover_url
    video.save()
    set_water_mark_to_video.delay(video.id)
    return {'cover_url': cover_url}


@bind_context("topic/get_infos_by_ids")
def get_topics_by_ids(ctx, topic_ids):
    """
    获取topic 用户主页相册专用 相册+内容 信息
    其中，点赞数、评论数是日记本下的日记帖之和，收藏数是日记本的数据
    目前用户主页相册使用该接口~713
    """
    user = get_user_from_context(ctx)
    if not topic_ids:
        return {}

    result, diary_ids, topic_classify, social_data = [], [], {}, {}
    topics = Problem.objects.filter(id__in=topic_ids, is_online=True)

    for item in topics:
        content_data = item.data()
        if item.diary_id:
            diary_ids.append(item.diary_id)

        if not topic_classify.get(item.diary_id):
            social_data[item.diary_id] = {}
            topic_classify[item.diary_id] = [item.id]
        else:
            topic_classify[item.diary_id].append(item.id)

        item_data = {
            "id": item.id,
            "diary_id": item.diary_id,
            "images": content_data['topic'].get("images", []),
            "content": content_data['topic'].get("content", []),
            "video": item.get_video_info(),
            "is_favored": item.is_favored_by(user.id) if user else False,
            "is_voted": item.is_voted(user.id) if user else False,
            "create_time": item.created_time.timestamp()
        }
        result.append(item_data)

    diaries = Diary.objects.filter(id__in=diary_ids)
    diaries_info = {}
    for diary in diaries:
        diaries_info[diary.id] = {
            "vote_num": diary.vote_num,
            "comment_count": diary.reply_num,
        }
    for diary_id, topic_ids in topic_classify.items():
        topic_ids = Problem.objects.filter(diary_id=diary_id).values_list("id", flat=True)

        social_data[diary_id]['vote_count'] = diaries_info[diary_id].get("vote_num", 0) if diaries_info.get(diary_id) else 0
        social_data[diary_id]['comment_count'] = diaries_info[diary_id].get("comment_count", 0) if diaries_info.get(diary_id) else 0

        social_data[diary_id]['favor_count'] = ProblemFavor.objects.filter(problem_id__in=list(topic_ids),
                                                                           is_deleted=False).count()

        social_data[diary_id]['favor_count'] += DiaryFavor.objects.filter(diary_id=diary_id, is_deleted=False).count()

    for topic in result:
        if topic.get("diary_id") in social_data:
            topic.update(social_data.get(topic.get("diary_id")))

    return result


@bind('mimas/diary_audit/fake_vote')
def diary_audit_fake_vote(diary_id, content_level):
    """
    新增的日记帖 审核等级大于等于良好时 触发点赞灌水 且只需要灌水一次，重复审核不再灌水
    :param diary_id:
    :param content_level:
    :return:
    """
    diary = Diary.objects.get(id=diary_id)
    audit_time = diary.audit_time
    if not audit_time:
        topics = Problem.objects.filter(
            diary_id=diary.id, is_online=True,
        ).order_by('-id').values('id', 'user_id')
    else:
        topics = Problem.objects.filter(
            diary_id=diary.id, is_online=True, created_time__gte=audit_time,
        ).order_by('-id').values('id', 'user_id')
    if not topics:
        return

    for topic in topics[:5]:
        special_user = is_special_user(user_id=topic['user_id'])
        fake_vote_v1(
            task_type=VOTE_TASK_TYPE.TOPIC, business_id=topic['id'], special_user=special_user,
            content_level=content_level,
        )


@bind("topic/get_topic_rel_infos")
def get_topic_rel_infos(topic_id):
    """
    获取帖子关联的数据
    :param topic_id:
    :return:
    """
    result = {}

    if not topic_id:
        return result

    routine = GroupRoutine()
    _map_list = [
        ("v1_rel_tags_info", topic_list_manager.get_topic_tag_info_by_topic_ids),
        ("v3_rel_tags_info", topic_list_manager.get_topic_tagv3_info_by_topic_ids),
    ]

    for key, func in _map_list:
        routine.submit(key, func, [topic_id])
    routine.go()

    for key, _ in _map_list:
        _data = routine.results.get(key, {})
        result.update({
            key: _data.get(topic_id, [])
        })

    return result
