# -*- coding: utf-8 -*-

from __future__ import unicode_literals, absolute_import, print_function

import datetime
import json
import random

from django.conf import settings
from django.db.models import F
from django.db.models import Q
from django.db.models import Count
from django.utils import timezone
from gm_rpcd.all import bind, RPCDFaultException
from gm_types.antispam import DocType
from gm_types.doctor import DOCTOR_TITLE_TYPE
from gm_types.error import ERROR as CODES
from gm_types.gaia import FILTER_WORD_TYPE
from gm_types.push import AUTOMATED_PUSH
from gm_types.gaia import (
    QUESTION_TYPE,
    QUESTION_ORDER_TYPE,
    USER_TYPE,
    TOP_TYPE,
    PLATFORM_CHOICES,
    HOSPITAL_TYPE,
    ANSWER_ORDER_TYPE,
    DOCTOR_PROCESS_STATUS
)
from gm_types.mimas import (
    QUESTION_SOURCE,
    QA_CONTENT_TYPE,
    MEDIA_IMAGE_URL_SOURCE,
    QUESTION_AUDIT_STATUS,
    IMAGE_TYPE,
    NOTICATION_LIST_CATEGORY,
    CONTENT_CLASS,
)
from gm_types.mimas.qa import (
    QUESTION_TYPE as QA_QUESTION_TYPE,
    VIDEO_SOURCE_TYPE,
    GRABBING_PLATFORM,
)
from gm_types.push import PUSH_INFO_TYPE
from gm_types.user_hierarchy import EventType
from six.moves import xrange

from communal.tasks import intelligent_push_task
from live.tasks import get_qiniu_persistent_ids
from qa.cache import (
    high_quality_question_cache,
    question_inviter_cache,
    complains_question_cache,
)
from qa.cache.manager import AnswerSortCacheManager
from qa.libs import (
    get_unread_count_by_user_id,
    get_answers_by_ids,
    get_answer_reply_by_pks,
    get_unread_answer_info_by_user_id,
    get_unread_answer_vote_num_by_user_id,
    get_answer_vote_infos_by_ids,
    update_answer_reply_read_status,
    _get_content_text,
    refine_qa_content_data, format_qa_content_v2)
from qa.service import AnswerReplyImagesService
from qa.services import QualityQuestionService
from qa.models import AnswerFavor, QualityUserQuestion, QualityAuthorAnswer, AnswerReplyImages
from qa.models import QuestionFavor
from qa.models import Answer, AnswerVote, AnswerVoteReply, BDAnswer, AnswerImage, AnswerTag, AnswerTagV3, TagV3Service
from qa.manager.question_manager import QuestionManager
from qa.models import AnswerReply, OverHeadAnswer, OverHeadQuestion
from qa.models import Question, QuestionImage, QuestionTag, AnswerTop, QuestionTagV3
from qa.models import UserAnswerQuestion, QuestionInviter
from talos.models.doctor import DoctorMessageList
from qa.serializers import (
    QuestionSerializer,
    AnswerSerializer,
    AnswerReplySerializer,
    AnswerVoteSerializer,
    AnswerReplyVoteSerializer,
    QuestionTagSerializer
)
from qa.tasks import (
    add_answer_view,
    add_question_view,
    check_spam,
    comment_push,
    add_doctor_question,
    gif_create_webp,
    applet_replied_push,
    applet_reply_summary_push,
    applet_voted_push,
    applet_answer_summary_push,
    applet_answer_voted_push
)
from qa.manager.qa_media_manager import (
    answer_media,
    question_media,
)

from talos.services.doctor import DoctorService
from qa.utils.decorator import listing
from qa.utils.image import get_w_path, get_half_path, handle_image_type
from qa.utils.time import get_timestamp_epoch
from qa.tools.answer import AnswerTools, QaTool
from qa.tools import (
    AnswerCreateMethod,
)
from talos.portal import (
    get_doctor_by_user_id,
)
from talos.libs.datetime_utils import tzlc
from talos.rpc import bind_context
from talos.tools.filterword_tool import filterword_by_custom
from talos.services import (
    UserService,
    get_user_from_context,
    UserConvertService,
    TagService)
from user_hierarchy.portal import process_task

from utils.user import get_user_gm_url
from utils.common import get_data_from_rich_text
from utils.protocol import gm_protocol
from utils.push import push_task_to_user_multi
from utils.rpc import gen, rpc_client, get_current_user
from utils.stat_log import SocialStatLogForUserAction
from utils.tasks import common_push
from ..libs import (
    get_qa_content_text,
    format_qa_content,
    get_media_info_from_content,
    get_media_extra_info,
    get_head_image_info,
)
from utils.gevent_jobs import Tasks
from qa.manager.answer_manager import AnswerManager
from social.models import UserFollow
from talos.tools.vote_tool import VoteTool as NewVoteTool
from talos.cache.base import vote_cache as new_vote_cache
from utils.group_routine import GroupRoutine
from gm_protocol import GmProtocol
from talos.services.other import get_user_lastest_device_app_version_by_user_id
from utils.common import is_version_gray


@bind("qa/question/create")
def create_question(title, content, tags, images, city=None, in_white_list=False, is_invite_doctor=False, platform=None,
                    question_type=QA_QUESTION_TYPE.TRADE, head_images=[]):
    user = get_current_user()

    # add tag of 大杂烩 if in_white_list is True
    if in_white_list:
        tags.append(settings.MULTI_TOPIC_TAG_ID)
        tags = list(set(tags))

    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    #创建问题部分，增加了对内容和标题的同时过滤,由于图文混排，需要过滤出content中的文本部分
    filter_content = get_qa_content_text(platform, content)
    filterword_by_custom(filter_type=FILTER_WORD_TYPE.TOPIC_CONTENT, content=filter_content + title)

    tags_dict = [{'tag': tag} for tag in tags]
    images = [{'image_url': image['image']} for image in images]

    data = {
        'user': user.id, 'title': title, 'content': content,
        'city_id': city, 'images': images,
        'tags': tags_dict, 'is_invite_doctor': is_invite_doctor,
        'question_type': question_type, 'audit_status': QUESTION_AUDIT_STATUS.UNAUDITED,
        'is_online': False,
    }
    if platform:
        data['platform'] = platform
        # 仅分离出文本内容做关键词校验  7675版本支持图文混排
        data['content'] = get_qa_content_text(platform, content)
        media_info_dic = get_media_info_from_content(platform, content)
        _image_list = get_media_extra_info(media_info_dic.get("images_list", []))
        for image_dic in _image_list:
            image_dic["image_url_source"] = MEDIA_IMAGE_URL_SOURCE.RICH_TEXT
        data["images"] = _image_list

    head_images = get_head_image_info(head_images)
    data["images"] = head_images + data["images"]

    serializer = QuestionSerializer(data=data)
    if not serializer.is_valid(raise_exception=False):
        return gen(CODES.CONTENT_CREATE_ERROR)
    convert_content_dict = format_qa_content(platform, content)  # 格式化content 内容
    question = serializer.save(**convert_content_dict)
    # 粉丝消息页面通知数据保存到db
    # result = create_information_db.delay(
    #     user_id=user.id, source_type=ATTENTION_NOTIFY_TYPE.QUESTION, business_id=question.id,
    # )
    # info_logger.info('开始保存给粉丝的消息:{},问题id:{}'.format(result.task_id, question.id))

    check_spam.delay(question.id, ['title', 'content'], DocType.QUESTION)
    # push to questioner fans
    common_push.delay(uname=user.nick_name, uid=user.id, topic_id=question.id, push_type="question")
    add_doctor_question.apply_async((question.id,),
                                    eta=tzlc(datetime.datetime.now()) + datetime.timedelta(seconds=3 * 60))

    # 问题富文本内的视频处理, 需要处理成带水印的
    if question.content_type == QA_CONTENT_TYPE.VIDEO:
        _, video_list = get_data_from_rich_text(question.content, u'//video[not(@name="new_video")]/@src')
        if video_list:
            get_qiniu_persistent_ids.delay(
                source_id=question.id,
                video_type=VIDEO_SOURCE_TYPE.QUESTION,
                url_list=video_list
            )

    return {'question_id': question.id}


@bind("qa/answer/create")
def create_answer(content, images, question_id, platform=None, platform_channel=None, need_answer=False, head_images=[],
                  tags=[]):

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

    # 问题回答部分，对回答内容进行过滤，以前的逻辑对数字进行了过滤，本次沿用
    content_data = get_qa_content_text(platform, content)
    filterword_by_custom(filter_type=FILTER_WORD_TYPE.TOPIC_CONTENT, content=content_data)

    # 序列化一下
    if platform == GRABBING_PLATFORM.GM and content:
        content = json.loads(content)
    try:
        answer_data = AnswerCreateMethod.create(
            user=user,
            question_id=question_id,
            content=content,
            images=images,
            platform=platform,
            tags=tags,
            head_images=head_images
        )
        _answer_id = answer_data.get("id", 0)

        data = {"answer_id": _answer_id}
        if need_answer:
            data["content"] = answer_data.get("content", "")
            data["author"] = {
                                'gm_url': get_user_gm_url(user.user_id),
                                'user_id': user.user_id,
                                'user_type': USER_TYPE.NORMAL,
                                'user_name': user.nick_name,
                                'user_portrait': user.portrait,
                            }

        event_data = process_task(
            user_id=user.id,
            event_type=EventType.ANSWER_QUESTION,
            related_item=_answer_id
        )

        # 24小时后，如果回答没有收到评论或点赞，则发送相关小程序push
        applet_answer_summary_push.apply_async((_answer_id,), countdown=86400)

        data.update({
            "growth_value": event_data['growth_value'],
            "point_value": event_data['point_value'],
            "submit_count": event_data['submit_count'],
        })

        return data
    except RPCDFaultException as e:
        return gen(CODES.CONTENT_CREATE_ERROR)


@bind('qa/answer/create_reply')
def create_answer_reply(content, answer_id, answer_reply_id=None, platform_channel=None, images=[]):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    # 这里是回复限制部分的评论的评论，沿用以前对数字进行过滤的逻辑
    filterword_by_custom(filter_type=FILTER_WORD_TYPE.TOPIC_REPLY, content=content)
    data = {
        'user': user.id,
        'content': content,
        'answer': answer_id,
        'commented_reply': answer_reply_id
        }
    serializer = AnswerReplySerializer(data=data)

    if not serializer.is_valid(raise_exception=False):
        return gen(CODES.CONTENT_CREATE_ERROR)
    is_commented_reply = True if data.get('commented_reply') else False
    answer_reply = serializer.save()
    # 评论创建图片
    reply_image_objects = [
        AnswerReplyImages(
            reply_id=answer_reply.id,
            url=image.get('image', ''),
            width=image.get('width', 0),
            height=image.get('height', 0),
        ) for image in images]
    AnswerReplyImages.objects.bulk_create(reply_image_objects)

    try:
        doctor = get_doctor_by_user_id(user.id)
    except IndexError:
        doctor = None
    if doctor:
        DoctorMessageList.objects.filter(
            question_id=answer_reply.answer.question_id,
            doctor_id=doctor.id
        ).update(process_status=DOCTOR_PROCESS_STATUS.processed)

    data = dict()
    data['reply_data'] = answer_reply.reply_data_after_create()
    data['comment_data'] = answer_reply.comment_data_after_create()

    # 给被评论的人发送push请求 没有被评论的默认为回答人
    push_user_id = data['comment_data'].get('at_user_id')\
        if is_commented_reply else data['reply_data'].get('reply_user_id')
    if user.id != push_user_id:
        comment_push(
            answer_id=answer_id,
            comment_id=answer_reply.id,
            author_name=user.nick_name,
            content=content,
            user_id=push_user_id,
            is_commented_reply=is_commented_reply
        )

    event_data = process_task(
        user_id=user.id,
        event_type=EventType.COMMENT_OTHER,
        related_item=answer_reply.id
    )

    # 给被评论的评论用户发小程序push
    applet_replied_push.delay(answer_reply.id)

    # 24小时后，如果当前评论，没有被评论或被赞，发送相应push
    applet_reply_summary_push.apply_async((answer_reply.id,), countdown=86400)

    data['growth_value'] = event_data['growth_value']
    data['point_value'] = event_data['point_value']
    data['submit_count'] = event_data['submit_count']

    _answer = Answer.objects.filter(pk=answer_id).only("question_id").first()
    # 用户行为埋点 评论相关
    SocialStatLogForUserAction.stat_log_for_reply(
        content_type=SocialStatLogForUserAction.CONTENT_TYPE.question,
        user_id=user.id,
        content_id=_answer.question_id
    )
    images = AnswerReplyImages.objects.filter(reply_id=answer_reply.id).values(
        'reply_id', 'url'
    )
    reply_images = []
    for image in images:
        reply_images.append({
            'url': image['url'],
        })
    data['reply_data']['images'] = reply_images
    data['comment_data']['images'] = reply_images
    return data


@bind('qa/answer/vote')
def answer_vote(answer_id):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    data = {'user': user.id, 'answer': answer_id}
    serializer = AnswerVoteSerializer(data=data)

    if not serializer.is_valid(raise_exception=False):
        return gen(CODES.DIARY_HAS_VOTED)

    vote = serializer.save()
    vt_v1 = NewVoteTool(new_vote_cache, vote.answer.user_id, new_version=True)
    vt_v1.receive_answer_vote(vote.id)

    rpc_client['api/person/incr_vote'](user_id=vote.answer.user_id).unwrap()

    tag_ids = list(qt.tag.id for qt in vote.answer.question.tags if qt.tag)
    data = serializer.data
    data['tag_ids'] = tag_ids

    event_data = process_task(user_id=user.id, event_type=EventType.VOTE_MULTI_TIMES, related_item=data['id'])
    growth_value, point_value = event_data['growth_value'], event_data['point_value']
    submit_count = event_data['submit_count']
    data['growth_value'] = growth_value
    data['point_value'] = point_value
    data['submit_count'] = submit_count

    # 7715之后添加回答被赞的push操作
    push_url = gm_protocol.get_answer_list(answer=answer_id)
    answer = Answer.objects.get(id=answer_id)
    version = get_user_lastest_device_app_version_by_user_id(answer.user_id)
    # 推送跳转到消息页的赞列表
    if is_version_gray(version, '7.29.0'):
        push_url = GmProtocol().vote_or_favor_list(
            segment_index=NOTICATION_LIST_CATEGORY.VOTE,
            new_vote=True,
        )
    alert = u'有1位朋友给你点了赞，快来看看吧>>'
    intelligent_push_task.delay(
        content_id=answer.id,
        user_ids=[answer.user_id],
        push_type=AUTOMATED_PUSH.ANSWER_GET_VOTE,
        platform=None,
        extra={
            'type': PUSH_INFO_TYPE.GM_PROTOCOL,
            'pushUrl': push_url,
            'push_url': push_url,
        },
        alert=alert,
        others={
            "title": "@{} 给你点了赞".format(user.nick_name),
            "alert": alert,
        },
        labels={},
    )

    # 给回答用户发小程序push
    applet_answer_voted_push.delay(vote.id)

    # 用户行为埋点 点赞相关
    SocialStatLogForUserAction.stat_log_for_like(
        content_type=SocialStatLogForUserAction.CONTENT_TYPE.question,
        user_id=user.id,
        content_id=answer.question_id
    )

    return data


@bind('qa/answer/unvote')
def answer_unvote(answer_id):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    try:
        vote = AnswerVote.objects.get(user=user.id, answer_id=answer_id)
        vote.delete()

        answer = Answer.objects.get(id=answer_id)
        answer.like_num = F('like_num') - 1
        answer.save(update_fields=['like_num'])
        rpc_client['api/person/decr_vote'](user_id=answer.user_id).unwrap()
    except AnswerVote.DoesNotExist:
        return gen(CODES.OPERATION_NOT_SUPPORTED)

    return {}


@bind('qa/answer_reply/vote')
def answer_reply_vote(answer_reply_id):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    data = {'user': user.id, 'answerreply': answer_reply_id}

    serializer = AnswerReplyVoteSerializer(data=data)
    if not serializer.is_valid(raise_exception=False):
        return gen(CODES.OPERATION_NOT_SUPPORTED)

    answer_reply_vote = serializer.save()

    re_data = serializer.data
    reply = AnswerReply.objects.filter(id=int(answer_reply_id)).first()
    vt_v1 = NewVoteTool(new_vote_cache, reply.user_id, new_version=True)
    vt_v1.receive_answer_reply_vote(reply.id)

    # 用户行为埋点 点赞相关
    _answer = Answer.objects.filter(pk=reply.answer_id).only("question_id").first()
    SocialStatLogForUserAction.stat_log_for_like(
        content_type=SocialStatLogForUserAction.CONTENT_TYPE.question,
        user_id=user.id,
        content_id=_answer.question_id
    )

    # 给被点赞的评论用户发小程序push
    applet_voted_push.delay(answer_reply_vote.id)

    event_data = process_task(user_id=user.id, event_type=EventType.VOTE_MULTI_TIMES, related_item=re_data['id'])
    growth_value, point_value = event_data['growth_value'], event_data['point_value']
    submit_count = event_data['submit_count']
    re_data['growth_value'] = growth_value
    re_data['point_value'] = point_value
    re_data['submit_count'] = submit_count

    return re_data


@bind('qa/answer_reply/unvote')
def answer_reply_unvote(answer_reply_id):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    try:
        vote = AnswerVoteReply.objects.get(user=user.id, answerreply_id=answer_reply_id)
        vote.delete()

        answer = AnswerReply.objects.get(id=answer_reply_id)
        answer.like_num = F('like_num') - 1
        answer.save(update_fields=['like_num'])
    except AnswerVoteReply.DoesNotExist:
        return gen(CODES.OPERATION_NOT_SUPPORTED)

    return {}


@bind('qa/question/list')
@listing()
def question_list(start_num, count, refresh_count):
    user = get_current_user()

    data = []

    if refresh_count > 0 and start_num == 0:
        start_num = 10 * (refresh_count - 1)
        filters = {'has_recommended_answer': True}
        kwargs = {'offset': start_num, 'size': 10, 'sort_type': QUESTION_ORDER_TYPE.DROPDOWN_REFRESH, 'filters': filters}
    else:
        kwargs = {
            'offset': start_num, 'size': count + 5,
        }

    questions = rpc_client['api/question/filter'](**kwargs).unwrap()['question_ids']

    # 新增过滤置顶问答的内容
    questions_top = AnswerTop.objects.filter(start_time__lte=timezone.now(),
                                             end_time__gte=timezone.now(), enable=True).order_by('order')
    question_top_list = _get_online_answer_top(questions_top)
    top_id_set = {'question': set(), 'answer': set()}
    for item in question_top_list:
        if item.get('type') == QUESTION_TYPE.QUESTION:
            top_id_set['question'].add(item.get('obj').id)
        elif item.get('type') == QUESTION_TYPE.ANSWER:
            top_id_set['answer'].add(item.get('obj').id)

    # v 7.6.40 变更，记录发现页 问答的浏览量
    answer_ids = []
    question_ids = []
    author_ids = set()
    for question in questions:
        if question['answer']:
            if not (question['answer'] in top_id_set['answer']):
                try:
                    answer = Answer.objects.get(id=question['answer'])
                    if answer.user_id in author_ids:
                        continue

                    author_ids.add(answer.user_id)
                except Answer.DoesNotExist:
                    # qa object not exist.
                    pass
                else:
                    d = {
                        'answer': answer.data_for_list(user),
                        'type': QUESTION_TYPE.ANSWER,
                        'id': answer.question_id
                    }
                    data.append(d)
                    answer_ids.append(answer.id)

        elif question['question'] and (question['question'] not in top_id_set['question']):
            try:
                question = Question.objects.get(id=question['question'])
                if question.user_id in author_ids:
                    continue

                author_ids.add(question.user_id)
            except Question.DoesNotExist:
                # question object not exist
                pass
            else:
                d = {
                    'question': question.data_for_list(user),
                    'type': QUESTION_TYPE.QUESTION,
                    'id': question.id
                }
                data.append(d)
                question_ids.append(question.id)
    add_answer_view.delay(answer_ids=answer_ids)
    add_question_view.delay(question_ids)
    return data


@bind('qa/question/list_exclude_answer')
@listing()
def question_list_exclued_answer(start_num, count):
    data = []
    questions = Question.objects.filter(is_online=True).order_by('-id')
    for question in questions[start_num: start_num + count]:
        data.append(question.data_for_list())
    return {
        'questions': data,
    }


@bind('qa/question/list/discovery')
@listing()
def question_list_discovery(start_num, count, **filters):
    """新版新版本。http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=11599925"""

    fl = filters.get("filters", {})
    fls = {
        'answers_num_gte': 1,
        'answers_level_gte': 3,
    }
    if fl:
        fls.update(fl)

    kwargs = {
        'offset': start_num, 'size': count,
        'sort_type': QUESTION_ORDER_TYPE.DISCOVERY_UPDATE,
        'filters': fls,
    }

    questions = rpc_client['api/question/filter'](**kwargs).unwrap()['question_ids']

    data = []

    # 记录发现页 问答的浏览量
    question_ids = []
    author_ids = set()
    for question in questions:
        try:
            question = Question.objects.get(id=question['question'])
            if question.user_id in author_ids:
                continue
            author_ids.add(question.user_id)
        except Question.DoesNotExist:
            pass
        else:
            d = {
                'question': question.data_for_discovery(),
                'type': QUESTION_TYPE.QUESTION,
                'id': question.id
            }
            data.append(d)
            question_ids.append(question.id)

    add_question_view.delay(question_ids)

    return data


@bind('qa/question/my_list')
@listing()
def my_question(start_num, count):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)
    query = Q(user=user.id) & (Q(is_online=True) | Q(audit_status=QUESTION_AUDIT_STATUS.UNAUDITED))
    questions = Question.objects.filter(query).order_by('-create_time')[
                start_num: start_num + count]
    data = []
    question_ids = []
    for question in questions:
        data.append(question.data_for_my_list())
        question_ids.append(question.id)
    # v7.6.40 记录 我的问题 浏览量
    add_question_view.delay(question_ids)
    return data


@bind('qa/answer/my_list')
@listing()
def my_answer(start_num, count):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)
    # v 7.6.35  排序规则修改 降序排序 由id 变更为 create_time
    answers = Answer.objects.filter(user=user.id, is_online=True,
                                    question__is_online=True).order_by('-create_time')[start_num: start_num + count]
    data = []
    # 记录 我的回答 浏览量
    answer_ids = [answer.id for answer in answers]
    consult_info = QualityQuestionService.get_consult_count_by_answer_ids(answer_ids=answer_ids)

    for answer in answers:
        answer_info = answer.data_for_list(user)
        answer_info['consult_count'] = consult_info.get(answer.id, 0)
        data.append(answer_info)

    add_answer_view.delay(answer_ids=answer_ids)
    return data


@bind("qa/answer/question_info")
def get_question_info_by_answer_id(answer_id, pc_qa_show=False):
    answer = Answer.objects.get(id=answer_id)
    question = answer.question
    _data = {
        'question_id': question.id,
        'question_title': question.title,
        'answer_count': question.answer_num,
        'is_online': question.is_online,
        'answer_content': answer.content,
        'answer_content_text': _get_content_text(answer.content),
        'comment_count': answer.comment_num,
        'answer_platform': answer.platform,
    }
    if pc_qa_show:
        _data.update({
            "question_content": question.content,
            "question_images": question.content_images,
            "answer_images": answer.content_images,
            "answer_timestamp": int(answer.create_time.strftime('%s')),
        })

    return _data


@bind("qa/answer/get_answer_reply_count")
def get_answer_reply_count(answer_id):

    reply_count = AnswerReply.objects.using(settings.SLAVE_DB_NAME).filter(
        answer_id=answer_id,
        is_online=True,
        commented_reply=None
    ).count()

    quality_question_cnt = QualityQuestionService.count_by_answer_id(answer_id)

    return {
        "reply_cnt": reply_count,
        "quality_question_cnt": quality_question_cnt,
    }


@bind("qa/answer/get_answer_reply")
@listing()
def get_answer_reply(answer_id, start_num, count, has_comments=True, need_newest=True,
                     top_comment_id=None, new_order=False, new_response=False, sort_condition=None):
    """
    v 7.6.95 更新，回答 一级评论的子评论，返回最新回复的两条 7720改成返回时间最早的两条
    :param answer_id:
    :param start_num:
    :param count:
    :param has_comments: 是否取子评论
    :param need_newest: 是否取最新的子评论
    :param top_comment_id: 需要置顶的评论id，7-10-0 置顶后不移除原位置的数据
    :param new_order: 按创建时间排序，True 为正序，False 倒序
    :param new_response: {}
    :return:
    """
    user = get_current_user()
    top_comment = True if top_comment_id else False  # 置顶高亮标识
    is_second_level_comment = False
    try:
        order = 'create_time' if new_order else '-create_time'

        replies = AnswerReply.objects.filter(
            answer_id=answer_id,
            is_online=True,
            commented_reply=None
        )

        if not sort_condition:
            replies = replies.order_by(order)
        else:
            replies = replies.order_by(*sort_condition)

        first_reply_count = replies.count()
        if top_comment_id and start_num == 0:
            replies = replies.exclude(id=top_comment_id)
        replies = replies[start_num: start_num + count]

        if top_comment_id:
            my_reply = AnswerReply.objects.filter(id=top_comment_id).first()
            replies = list(replies)

            if my_reply and my_reply.first_reply:
                replies = [my_reply.first_reply] + replies
                is_second_level_comment = True
            elif my_reply:
                replies = [my_reply] + replies
            else:
                top_comment = False

    except AnswerReply.DoesNotExist:
        return gen(CODES.ANSWER_NOT_FOUND)

    data = []

    for reply in replies:
        if reply:
            if is_second_level_comment:
                data.append(reply.get_data_for_reply(user, has_comments, need_newest, new_order=new_order,
                                                     top_comment_id=top_comment_id))
            else:
                data.append(reply.get_data_for_reply(user, has_comments, need_newest, new_order=new_order))

    if top_comment and len(data) > 0:  # 置顶设置为高亮
        data[0].update({"high_light": 1})
    reply_ids = []
    for reply in data:
        reply_ids.append(reply['reply_id'])
    images = AnswerReplyImagesService.get_reply_images_by_id(reply_ids)
    for reply in data:
        reply['images'] = images.get(reply['reply_id'], [])

    if new_response:
        return {
            'reply_data': data,
            'first_reply_count': first_reply_count,  # 添加一级评论数
        }

    return data


@bind('qa/answer/get_answer_detail')
def __get_answer_detail(answer_id, header_image=False, current_user=False):
    user = get_current_user()
    try:
        answer = Answer.objects.get(id=answer_id, is_online=True)
    except Answer.DoesNotExist:
        return gen(CODES.ANSWER_NOT_FOUND)

    data = answer.data_for_detail(user)
    data['author'] = answer.data_for_author(user)
    _images = []
    if header_image:
        images = AnswerImage.objects.filter(
            answer=answer, image_url_source=MEDIA_IMAGE_URL_SOURCE.HEAD
        )
        for image in images:
            _images.append(image.image_info_data)
        data['header_images'] = _images
    data['current_user'] = {}
    if current_user and user:
        current_user = UserConvertService.get_user_info_by_user_id(user.id)
        data['current_user'].update(current_user)

    add_answer_view.delay(answer_ids=[answer_id])

    return data


@bind_context('qa/answer/get_answer_by_ids')
def get_answer_detail_by_ids(ctx, answer_ids):
    """
    获取多个answer
    """
    user = get_user_from_context(ctx)
    answers = Answer.objects.filter(id__in=answer_ids, is_online=True)

    data = []
    for answer in answers:
        add_answer_view.delay(answer_ids=[answer.id])
        answer_data = answer.data_for_detail(user)
        answer_data['image'] = answer.get_images()
        answer_data['videos'] = answer.get_videos()

        data.append(answer_data)

    return data


@bind('qa/answer/get_nearby_answer')
def get_next_answer(answer_id):
    pre_id = None
    next_id = None
    question_id = Answer.objects.get(id=answer_id).question_id
    answer_sort_cache_manager = AnswerSortCacheManager()
    sort_ids = answer_sort_cache_manager.get_sort_cache(question_id=question_id)

    for i in range(0, len(sort_ids)):
        if str(sort_ids[i]) == str(answer_id):
            pre_index = i - 1
            next_index = i + 1
            if pre_index < 0:
                pre_id = None
            else:
                pre_id = sort_ids[pre_index]

            if next_index >= len(sort_ids):
                next_id = None
            else:
                next_id = sort_ids[next_index]
            break

    if pre_id == next_id and len(sort_ids) > 1:
        pre_id = sort_ids[-1]

    return {'pre_id': pre_id, 'next_id': next_id}


@bind('qa/question/detail')
def get_question_detail(question_id, image_header=False):
    try:
        question = Question.objects.select_related().get(pk=question_id)
    except Question.DoesNotExist:
        return gen(CODES.QUESTION_NOT_FOUND)

    user = get_current_user()
    user_id = user and user.id
    if not question.user_can_view(user_id):
        return gen(CODES.QUESTION_NOT_FOUND)

    current_user_id = user and user.id or 0
    answer_num = question.answer_num  # 排除已下线问题
    ques_images = QuestionImage.objects.filter(
        question__pk=question_id, image_url_source=MEDIA_IMAGE_URL_SOURCE.CREATE)
    image_data = []
    if ques_images:
        for image in ques_images:
            data = {
                'image_half': get_half_path(image.image_url),
                'image_w': get_w_path(image.image_url)
            }
            image_data.append(data)

    tagSerializer = QuestionTagSerializer(question.tags, many=True)
    tags = [tag['tag'] for tag in tagSerializer.data if tag['tag']]

    if current_user_id:
        is_favored = QuestionFavor.objects.filter(
            question_id=question.id, user_id=current_user_id, is_online=True).exists()
    else:
        is_favored = False

    result = {
        'id': question_id,
        'title': question.title,
        'content': question.content,
        'is_online': question.is_online,
        'post_date': question.create_time.strftime('%y-%m-%d'),
        'answer_count': answer_num,
        'image': image_data,
        'tags': tags,
        'tags_v3': question.get_tags_v3_info(),
        'platform': question.platform,
        'create_time': get_timestamp_epoch(question.create_time),
        'view_num': question.view_amount,
        "is_favored": is_favored,
    }
    following_dic = QaTool.get_following_info([question.user_id], current_user_id)
    user_ids = [question.user_id]
    if current_user_id:
        user_ids.append(current_user_id)
    user_info_dic = UserConvertService.get_user_info_by_user_ids(user_ids)
    result.update({
        "is_following": following_dic.get(question.user_id, False),
        "author": user_info_dic.get(question.user_id, {}),
        'current_user': user_info_dic.get(current_user_id, {}),
    })
    if image_header:
        _images = []
        images = QuestionImage.objects.filter(
            question=question, image_url_source=MEDIA_IMAGE_URL_SOURCE.HEAD,
        )
        for image in images:
            _images.append(image.image_info_data)
        result['header_images'] = _images

    # 加入分享内容 数据返给backend 由backend做标签处理
    recommand_answer = question.recommend_answer
    if recommand_answer:
        result['share_answer'] = recommand_answer.content
    else:
        answer_most_like = Answer.objects.using(settings.SLAVE_DB_NAME).filter(
            question_id=question_id,is_recommend=False, is_online=True).order_by('-like_num', 'id')
        result['share_answer'] = answer_most_like[0].content if answer_most_like else ''
    add_question_view.delay(question_ids=[question_id])
    return result


@bind('qa/question/author')
def get_author(question_id, return_doctor_info=False):
    try:
        author = Question.objects.get(pk=question_id).user
    except Question.DoesNotExist:
        return gen(CODES.QUESTION_NOT_FOUND)

    result = {
        'user_id': author.id,
        'user_type': USER_TYPE.NORMAL,
        'user_name': author.nickname,
        'user_portrait': author.portrait,
        # add 7.2.5
        'membership_level': author.membership_level,
        'user_level': {
            'membership_icon': author.membership_icon,
            'level_icon': author.level_icon,
            'constellation_icon': author.constellation_icon,
        },
    }
    if return_doctor_info:
        user_dict = UserConvertService.get_user_info_by_user_id(author.id)
        result.update({
            "doctor_id": user_dict.get("doctor_id", ""),
            "doctor_type": user_dict.get("doctor_type", ""),
            "hospital_id": user_dict.get("hospital_id", ""),
        })
    return result


@bind('qa/question/current_user')
def _get_current_user():
    return {
        'current_user': get_current_user()
    }


@bind('qa/answer/detail')
def get_answer_detail(answer_id):
    try:
        answer_data = Answer.objects.select_related().get(pk=answer_id, is_online=True)
    except Answer.DoesNotExist:
        return gen(CODES.ANSWER_NOT_FOUND)

    question_id = answer_data.question.id
    answer_num = Answer.objects.filter(question__id=question_id, is_online=True).count()
    belong_question = {
        'question_id': question_id,
        'question_title': answer_data.question.title,
        'answer_count': answer_num,
    }
    result = {
        'belong_question': belong_question,
        'answer_id': answer_id,
        'answer_content': answer_data.content
    }
    return result


@bind('qa/question/answer_list')
@listing()
def get_answer_list_by_question(question_id=None, start_num=0, count=10,
                                time_sequence=ANSWER_ORDER_TYPE.NOT_ORDER_BY_TIME, need_recommand=True,
                                top_answer_id=None):
    """获取回答列表"""
    try:
        question = Question.objects.get(pk=question_id, is_online=True)
    except Question.DoesNotExist:
        return gen(CODES.QUESTION_NOT_FOUND)

    recommand_answer = question.recommend_answer if need_recommand else None

    answer_query = Answer.objects.filter(
        question_id=question_id, is_online=True,
        level__in=[CONTENT_CLASS.BAD, CONTENT_CLASS.GENERAL, CONTENT_CLASS.FINE, CONTENT_CLASS.EXCELLENT, CONTENT_CLASS.OUTSTANDING]
    )
    if time_sequence == ANSWER_ORDER_TYPE.TIME_REDUCE:
        answer_query = answer_query.order_by("-id")
    elif time_sequence == ANSWER_ORDER_TYPE.TIME_RISE:
        answer_query = answer_query.order_by("id")
    elif time_sequence == ANSWER_ORDER_TYPE.HOTEST:
        answer_query = answer_query.order_by("rank", '-like_num', 'id')
    else:
        answer_query = Answer.objects.filter(question_id=question_id,
                                             is_recommend=False,
                                             is_online=True).order_by("rank", '-like_num', 'id')
    answers = []
    if start_num == 0 and time_sequence == ANSWER_ORDER_TYPE.NOT_ORDER_BY_TIME:
        if recommand_answer:
            answers.append(recommand_answer)
            answers += list(answer_query[0: count - 1])
        else:
            answers = list(answer_query[0: count])
    else:
        if recommand_answer and time_sequence == ANSWER_ORDER_TYPE.NOT_ORDER_BY_TIME:
            start_num -= 1
        answers += list(answer_query[start_num: start_num + count])

    if top_answer_id:
        top_answer = Answer.objects.filter(id=top_answer_id, is_online=True).first()
        if top_answer:
            answers = [item for item in answers if item.id != top_answer_id]
            answers.insert(0, top_answer)

    # 记录问题详情页，回答列表浏览量
    ids = [answer.id for answer in answers]
    add_answer_view.delay(answer_ids=ids)

    current_user = get_current_user()
    return AnswerTools.get_questions_data(current_user, answers)

@bind('qa/question/hot_answer_list')
def get_hot_answer_list_by_question(question_id):
    """
    获取热门回复列表
    :param question_id:
    :return:
    """
    try:
        question = Question.objects.get(pk=question_id, is_online=True)
    except Question.DoesNotExist:
        return gen(CODES.QUESTION_NOT_FOUND)
    hot_answers = []
    recommand_answer = question.recommend_answer
    # 对热门回复进行过滤
    hot_answers_query = Answer.objects.using(settings.SLAVE_DB_NAME).filter(
        question_id=question_id, is_recommend=False, is_online=True,
        like_num__gte=4).order_by("rank", '-like_num', '-id')[:3]
    # 取前三个回复
    if recommand_answer:
        hot_answers.append(recommand_answer)
        hot_answers += list(hot_answers_query[0:2])
    else:
        hot_answers = list(hot_answers_query[0:3])
    # 后两个位置，取最新回复的且赞数达到2个的回答
    last_answer_query = Answer.objects.filter(question_id=question_id,
                                              is_recommend=False,
                                              is_online=True,
                                              like_num__gte=2).order_by('-update_time')[:5]
    for i in list(last_answer_query):
        if i not in hot_answers and len(hot_answers) < 5:
            hot_answers.append(i)

    current_user = get_current_user()
    return AnswerTools.get_questions_data(current_user, hot_answers)


@bind('qa/question/get_cards_info_by_answer_ids')
def get_question_card_by_answer_ids(ids):
    return get_answers_by_ids(ids)


@bind('qa/question/get_user_qa_list')
@listing()
def get_user_qa_list(user_id, start_num=0, count=10):
    """
    每次取十个，如果下线则直接去掉
    """
    result = []
    qa_list = UserAnswerQuestion.objects.filter(user=user_id, is_online=True).order_by('-create_time')[start_num:start_num + count]
    if not qa_list:
        return []
    answer_ids = []
    question_ids = []
    for qa in qa_list:
        if qa.answer and qa.answer.is_online and qa.answer.question.is_online:
            answer_ids.append(qa.answer_id)
            _detail = qa.answer.data_for_list()
            result.append({
                'type': QUESTION_TYPE.ANSWER,
                'answer_id': _detail['answer_id'],
                'title': _detail['title'],
                'comment_num': _detail['comment_num'],
                'vote_num': _detail['vote_num'],
                'content': _detail['content'],
                'is_voted': _detail['is_voted'],
                'question_id': _detail['question_id'],
                'platform': _detail['platform'],
            })

        elif qa.question and qa.question.is_online:
            question_ids.append(qa.question_id)
            _detail = qa.question.data_for_list()
            result.append({
                'type': QUESTION_TYPE.QUESTION,
                'answer_num': _detail['answer_num'],
                'title': _detail['title'],
                'question_id': _detail['question_id'],
                'platform': _detail['platform'],
            })
    add_question_view.delay(question_ids)
    add_answer_view.delay(answer_ids)
    return result


@bind('qa/question/get_user_qa_count')
def get_user_qa_count(user_id):
    """
    通过user_id 计算当前用户发布了多少回答
    update v 7.6.75 回答逻辑更改，需要再判断该回答对应的问题在线状态.
    :param user_id:
    :return:
    """
    query = Q(user=user_id, is_online=True)
    answer_query = query & Q(question__is_online=True)
    question_count = Question.objects.filter(query).count()
    answer_count = Answer.objects.filter(answer_query).count()

    return question_count + answer_count


@bind('qa/question/top')
def question_list_top(num):
    """
    问答置顶
    v 7.6.40 增加记录发现页 top3中 问答浏览量
    :param num: 获取置顶的数量
    :return: 生效时间大于当前，结束时间之前，顺序排序前3
    """
    user = get_current_user()

    data = []
    answer_ids = []
    question_ids = []
    current_time = timezone.now()
    questions_top = AnswerTop.objects.filter(
        start_time__lte=current_time, end_time__gte=current_time, enable=True
    ).exclude(top_type=TOP_TYPE.SLIDE).order_by('order')
    question_top_list = _get_online_answer_top(questions_top, num)
    for item in question_top_list:
        if item.get('type') == QUESTION_TYPE.ANSWER:
            d = {
                'answer': item.get('obj').data_for_list(user),
                'type': item.get('type'),
                'id': item.get('obj').question_id,
                'top_type': item.get('top_type')
            }
            answer_ids.append(item.get('obj').id)

        elif item.get('type') == QUESTION_TYPE.QUESTION:
            d = {
                'question': item.get('obj').data_for_list(user),
                'type': item.get('type'),
                'id': item.get('obj').id,
                'top_type': item.get('top_type')
            }
            question_ids.append(item.get('obj').id)
        data.append(d)
    # 记录发现页 top3 问答的浏览量
    add_answer_view.delay(answer_ids=answer_ids)
    add_question_view.delay(question_ids)
    return data


@bind('qa/answer/tag_related_question')
def get_tag_related_question(answer_id):
    """
    获取答案的问题的标签相关推荐的问题,返回3个
    :return:
    """
    re_data = {}
    question_info = ''
    try:
        question = Answer.objects.get(id=answer_id, is_online=True).question
    except Answer.DoesNotExist:
        return None
    tag_ids = [tag.tag_id for tag in question.tags.all()]

    if tag_ids:
        question_all_recommend = Question.objects.filter(is_online=True,
                                                         is_recommend=True).values_list('id', flat=True)
        question_answer_recommend = Answer.objects.filter(is_online=True,
                                                          is_recommend=True).values_list('question_id', flat=True)
        question_ids = set(list(question_all_recommend) + list(question_answer_recommend))
        if question.id in question_ids:
            question_ids.remove(question.id)
        if question_ids:
            question_quesyset_ids = list(QuestionTag.objects.filter(question__in=question_ids,
                                                                    tag__in=tag_ids).values_list('question_id',
                                                                                                 flat=True))

            question_quesyset = Question.objects.filter(id__in=question_quesyset_ids, is_online=True)
            indexs = question_quesyset.count()
            sample_index = random.sample(xrange(indexs), 3) if indexs >= 3 else random.sample(xrange(indexs), indexs)

            question_info = [
                question_quesyset[i] for i in sample_index
            ]

    if question_info:
        re_data['type'] = u'相关问题'
        re_data['data'] = [{'id': item.id, 'title': item.title, 'answer_count': item.answer_num}
                           for item in question_info]
    else:
        re_data['type'] = u'优质问题'
        ids = json.loads(high_quality_question_cache.get('high_quality'))
        if ids:
            if question.id in ids:
                ids.remove(question.id)

            if len(ids) >= 3:
                sample_index = random.sample(ids, 3)
            else:
                sample_index = random.sample(ids, len(ids))

            questions = Question.objects.filter(id__in=sample_index, is_online=True)
            re_data['data'] = [{'id': item.id, 'title': item.title, 'answer_count': item.answer_num}
                               for item in questions]
        else:
            re_data['data'] = []
    # v 7.6.40 记录问题浏览量
    question_ids = [q["id"] for q in re_data.get("data", [])]
    add_question_view.delay(question_ids)
    return re_data if re_data.get('data') else None


@bind('qa/answer_reply/list')
def qa_answer_reply_list(pks):
    """
    get qa reply list by pks.    first get info，after update is_read
    :param pks:
    :return:
    """
    user = get_current_user()
    data = get_answer_reply_by_pks(pks)
    # update to is_read
    update_answer_reply_read_status(user.id, pks)
    return data


@bind('qa/answer/read')
def qa_answer_read():

    user = get_current_user()
    if not user:
        return

    my_question_ids = list(Question.objects.filter(user=user.id, is_online=True).values_list("id", flat=True))
    Answer.objects.filter(
        question_id__in=my_question_ids,
        is_online=True,
        questioner_read=False
    ).update(questioner_read=True)

    QuestionInviter.objects.filter(
        user=user.id,
        user_read=False
    ).update(user_read=True)

@bind('qa/question/list_for_es')
def get_question_list_for_es(pks):
    """

    :param pks:
    :return:
    """
    questions = Question.objects.filter(id__in=pks)
    data = {str(question.id): question.data_for_es() for question in questions}
    #  v 7.6.40 记录问答浏览量 如果该问题下有回答，会按照第一个回答的信息获取
    question_ids = [int(question["question_id"]) for question in data.values() if question["question_id"]]
    answer_ids = [int(question["answer_id"]) for question in data.values() if question["answer_id"]]
    add_question_view.delay(question_ids)
    add_answer_view.delay(answer_ids)
    return data


@bind('qa/answer/list_for_es')
def get_answer_list_for_es(pks):
    """

    :param pks:
    :return:
    """
    answers = Answer.objects.filter(id__in=pks, question__is_online=True)
    data = {str(answer.id): answer.data_for_es() for answer in answers}
    #  v 7.6.40 记录回答浏览量
    ids = list(map(int, data.keys()))
    add_answer_view.delay(ids)
    return data


@bind('qa/question/zone_recommend')
def get_zone_related_qa(zone_id):
    """
    圈子推荐
    :return:
    """
    user = get_current_user()
    overHeadAnswers = OverHeadAnswer.objects.filter(zone=zone_id, deleted=False)
    overHeadQuestions = OverHeadQuestion.objects.filter(zone=zone_id, deleted=False)

    obj_list = []
    for answer in overHeadAnswers:
        obj_list.append({
            'question': '',
            'answer': answer.answer,
            'rank': answer.rank
        })
    for question in overHeadQuestions:
        obj_list.append({
            'question': question.question,
            'answer': '',
            'rank': question.rank
        })
    obj_list = sorted(obj_list, key=lambda x: x['rank'])
    result = []
    answer_ids = []
    question_ids = []
    for obj in obj_list:
        if obj['question']:
            question = obj['question']
            question_ids.append(question.id)
            result.append({
                'question': question.data_for_list(user),
                'type': QUESTION_TYPE.QUESTION,
                'id': question.id
            })
        elif obj['answer']:
            answer = obj['answer']
            answer_ids.append(answer.id)
            if answer.question.is_online:
                result.append({
                    'answer': answer.data_for_list(user),
                    'type': QUESTION_TYPE.ANSWER,
                    'id': answer.question_id
                })
    # v 7.6.40 记录问答浏览量
    add_question_view.delay(question_ids)
    add_answer_view.delay(answer_ids)
    return result


@bind('qa/question/tab_choice')
def get_choice_question_with_first_answer(question_pks):
    """
    tab 精选
    :param question_pks:
    :return:
    """
    data = []
    answer_ids = []
    question_ids = []
    user = get_current_user()
    for question in Question.objects.filter(id__in=question_pks):
        answer = question.fisrt_answer
        if answer and question.is_online:
            data.append({
                'answer': answer.data_for_list(user),
                'type': QUESTION_TYPE.ANSWER,
                'id': answer.question_id
            })
            answer_ids.append(answer.id)
        else:
            data.append({
                'question': question.data_for_list(user),
                'type': QUESTION_TYPE.QUESTION,
                'id': question.id
            })
            question_ids.append(question.id)
    # v 7.6.40 记录问答浏览量
    add_question_view.delay(question_ids)
    add_answer_view.delay(answer_ids)
    return data


def _get_online_answer_top(questions_top, num=3):
    """
    返回上线的置顶问答列表id
    :param questions_top: questions_top 条件内有效的置顶数据
    :param num:
    :return:
    [{'type': QUESTION_TYPE., 'obj': Answer obj,'top_type':''},{'type': QUESTION_TYPE.QUESTION, 'obj': Question obj,'top_type':''}]
    """
    count_item = 0
    top_id_list = []
    for item in questions_top:
        if count_item < num:
            if item.answer_id:
                answer = item.answer
                if answer.is_online and answer.question.is_online:
                    top_id_list.append(
                        {'type': QUESTION_TYPE.ANSWER, 'obj': answer, 'top_type': item.top_type})
                    count_item += 1
            elif item.question_id:
                question = item.question
                if question.is_online:
                    top_id_list.append(
                        {'type': QUESTION_TYPE.QUESTION, 'obj': question, 'top_type': item.top_type})
                    count_item += 1

    return top_id_list


@bind('qa/question/get_data_list_by_ids')
def get_question_data_by_ids(ids, new_info_control=False):
    user = get_current_user()
    questions = Question.objects.filter(id__in=ids, is_online=True)

    user_ids = questions.values_list("user", flat=True)
    users_info = UserConvertService.get_user_info_by_user_ids(user_ids)

    data = {}
    for question in questions:
        _data = question.data_for_list(user)
        header_images = QaTool.get_header_images(question)
        _data.update({
            "intact_question_images": QaTool.get_intact_images(question, "question_image"),
            "header_images": header_images,
        })

        if new_info_control:
            _data.update({
                "new_user_info": users_info.get(question.user_id, {}),
            })

        data.update({str(question.id): _data})
    # v 7.6.40 记录问题的浏览量
    question_ids = list(map(int, data.keys()))
    add_question_view.delay(question_ids=question_ids)
    return data


@bind('qa/question/check')
def check_question_for_multi_topic(question_id):
    try:
        q = Question.objects.get(id=question_id)
        tag_list = [tag['id'] for tag in q.tags]
        return True, tag_list
    except Question.DoesNotExist:
        return False, []


@bind('qa/answer/lst_for_task')
def get_answer_for_task(type_q, ids, begin_day=None, end_day=None):
    '''
    问答相关表2异步任务的
    :param type_q: 区分不同表内容
    :param ids:
    :param begin_day: 涨粉(虚拟)每日轮询,查询时间
    :param end_day: 涨粉(虚拟)每日轮询,查询时间
    :return:
    '''
    raw_data = []
    if type_q == 'answer':
        raw_data = Answer.objects.filter(id__in=ids)
    elif type_q == 'question':
        raw_data = Question.objects.filter(id__in=ids)
    elif type_q == 'answerreply':
        raw_data = AnswerReply.objects.filter(id__in=ids)
    elif type_q == 'answer_recommend':
        raw_data = Answer.objects.filter(update_time__range=[begin_day, end_day],
                                         is_recommend=True).values_list('user', flat=True).distinct()
        return list(raw_data)
    re_data = [{'id': item.id, 'is_spam': True if item.spam_label else False, 'content': item.content} for item in
               raw_data]

    return re_data


@bind('qa/foreignkey/question')
def question_list_for_foreignkey(question_ids):
    if not question_ids:
        return []

    questions = Question.objects.filter(pk__in=question_ids)
    return QuestionSerializer(questions, many=True).data


@bind('qa/foreignkey/answer')
def answer_list_for_foreignkey(answer_ids):
    if not answer_ids:
        return []
    answers = Answer.objects.filter(pk__in=answer_ids, question__is_online=True)
    return AnswerSerializer(answers, many=True).data


@bind('qa/answervote/get_unread_count')
def get_unread_count():
    user = get_current_user()
    if not user:
        return 0

    count = get_unread_answer_vote_num_by_user_id(user.id)
    return count


@bind('qa/answervote/read_votes')
def read_votes():
    user = get_current_user()
    if not user:
        return

    AnswerVote.objects.filter(answer__user=user.id, is_fake=False, unread=True).update(unread=False)
    AnswerVoteReply.objects.filter(answerreply__user=user.id, unread=True).update(unread=False)
    return


@bind('qa/answervote/get_info_by_ids')
def get_answer_vote_user_info(vote_ids):
    return get_answer_vote_infos_by_ids(vote_ids)


@bind('qa/answer_reply/get_unread_info')
def get_unread_answer_reply():
    user = get_current_user()
    if user:
        replies_count = get_unread_count_by_user_id(user.id)
    else:
        replies_count = 0

    return replies_count


@bind('qa/question/list_for_index_slide')
@listing()
def get_question_for_index_slide(start_num, count):
    now = timezone.now()
    queryset = AnswerTop.objects.filter(enable=True, top_type=TOP_TYPE.SLIDE, start_time__lt=now,
                                        end_time__gt=now)[start_num: start_num + count]
    data = []
    for item in queryset:
        if item.question_id and item.question.is_online:
            data.append({
                'id': item.id,
                'question_id': item.question_id,
                'title': item.question.title,
            })
        elif item.answer_id and item.answer.is_online and item.answer.question.is_online:
            data.append({
                'id': item.id,
                'question_id': item.answer.question_id,
                'title': item.answer.question.title
            })
    # v 7.6.40 记录问题浏览量
    question_ids = [int(item["question_id"]) for item in data]
    add_question_view.delay(question_ids)
    return data


@bind('qa/answer/list_by_ids')
@listing()
def get_answer_list_by_ids(answer_ids):
    """
    首页推荐回答，Feed策略上线之前，数据为Fake
    :param answer_ids:
    :return:
    """
    answer_ids = list(map(int, answer_ids))
    user = get_current_user()
    queryset = Answer.objects.filter(pk__in=answer_ids, question__is_online=True)
    #  v 7.6.35 记录首页推荐回答的浏览量
    pks = [answer.id for answer in queryset]
    add_answer_view.delay(answer_ids=pks)

    queryset = sorted(queryset, key=lambda answer: answer_ids.index(answer.id))
    return [answer.data_for_list(user) for answer in queryset]


@bind('qa/question_answer/list_by_ids')
@listing()
def get_qa_list_by_ids(answer_ids, new_info_control=False):
    """
    首页推荐回答，Feed策略上线之前，数据为Fake
    :param answer_ids:
    :param new_info_control: 新信息控制
    :return:
    """
    if not answer_ids:
        return []

    answer_ids = list(map(int, answer_ids))
    user = get_current_user()
    queryset = Answer.objects.filter(pk__in=answer_ids, question__is_online=True)

    pks, user_ids, question_ids = [], [], []
    for answer in queryset:
        pks.append(answer.id)
        user_ids.append(answer.user_id)
        question_ids.append(answer.question_id)

    user_info_dic = UserService.get_format_user_by_user_ids(user_ids)
    question_tags_dic = QaTool.get_tags_info_by_question_ids(question_ids)
    question_tags_v3_dic = QuestionManager.get_tags_v3_info_by_question_ids(question_ids)

    if new_info_control:
        following_dic = QaTool.get_following_info(user_ids, current_user_id=user and user.id or 0)
    else:
        following_dic = {}

    queryset = sorted(queryset, key=lambda answer: answer_ids.index(answer.id))

    answer_sorted_list = []

    routine = GroupRoutine()
    for answer in queryset:
        routine.submit(answer.id, answer.data_for_qa, user)
    routine.go()

    for answer in queryset:
        _data = routine.results.get(answer.id, {})
        if not _data:
            continue

        header_images = QaTool.get_header_images(answer)
        user = user_info_dic.get(answer.user_id, {})
        _data.update({
            "intact_answer_images": QaTool.get_intact_images(answer, "answer_image"),
            "question_intact_question_images": QaTool.get_intact_images(answer.question, "question_image"),
            "header_images": header_images,
            "user": user if user else {},
            "tags": question_tags_dic.get(answer.question_id, []),
            "tags_v3": question_tags_v3_dic.get(answer.question_id, []),
        })

        if new_info_control:
            _data.update({
                "new_user_info": _data["user"],
                "is_following": following_dic.get(answer.user_id, False),
            })

        answer_sorted_list.append(_data)

    #  v 7.6.35 记录首页推荐回答的浏览量
    add_answer_view.delay(answer_ids=pks)

    return answer_sorted_list


@bind('qa/question_answer/list_by_ids_v3')
def get_qa_list_by_ids_v3(answer_ids, new_info_control=False):
    """
    首页推荐回答，Feed策略上线之前，数据为Fake
    :param answer_ids:
    :param new_info_control: 新信息控制
    :return:
    """

    if not answer_ids:
        return []

    answer_ids = list(map(int, answer_ids))
    user = get_current_user()
    queryset = Answer.objects.filter(pk__in=answer_ids, question__is_online=True)

    user_ids, question_ids = [], []
    for answer in queryset:
        user_ids.append(answer.user_id)
        question_ids.append(answer.question_id)

    question_tags= QuestionManager.get_tags_by_question_ids(question_ids)
    question_v3_tags= QuestionManager.get_tags_v3_by_question_ids(question_ids)
    tasks = Tasks()
    tasks.add("question_tags_dic", QuestionManager.get_tags_info_by_question_tags, question_tags)
    tasks.add("question_tags_v3_dic", QuestionManager.get_tags_v3_info_by_question_tags, question_v3_tags)
    tasks.add("user_info_dic", UserService.get_format_user_by_user_ids, user_ids)

    questions = Question.objects.filter(pk__in=question_ids, is_online=True).in_bulk(question_ids)
    answer_header_image_dict= AnswerManager.get_header_imgs_by_ids(answer_ids)
    answer_text_dic = {item.id: item.content for item in queryset}
    answer_images_dic = answer_media.get_qa_images(answer_text_dic, image_url_sources=[VIDEO_SOURCE_TYPE.ANSWER])
    answer_videos_dic = answer_media.get_qa_videos(answer_text_dic, source_type=VIDEO_SOURCE_TYPE.ANSWER)

    question_text_dic = {question_id: item.content for question_id, item in questions.items()}
    question_images_dic = answer_media.get_qa_images(question_text_dic, image_url_sources=[VIDEO_SOURCE_TYPE.QUESTION])
    questino_videos_dic = question_media.get_qa_videos(question_text_dic, source_type=VIDEO_SOURCE_TYPE.QUESTION)
    voted = AnswerManager.is_voted_by_ids(user.id if user else None, answer_ids)

    tasks.joinall()
    jobs = tasks.jobs
    question_tags_dic = jobs.get("question_tags_dic", {})
    question_tags_v3_dic = jobs.get("question_tags_v3_dic", {})
    user_info_dic = jobs.get("user_info_dic", {})

    if new_info_control:
        following_dic = QaTool.get_following_info(user_ids, current_user_id=user and user.id or 0)
    else:
        following_dic = {}

    queryset = sorted(queryset, key=lambda answer: answer_ids.index(answer.id))

    answer_sorted_list = []
    for answer in queryset:
        _data = AnswerManager.get_base_info(answer)
        user = user_info_dic.get(answer.user_id, {})
        question = questions.get(answer.question_id, None)
        if not question:
            continue
        _data.update({
            "title":  question.title,
            "intact_answer_images": answer_images_dic.get(answer.id, []),
            "question_intact_question_images": question_images_dic.get(answer.id, []),
            "header_images": answer_header_image_dict.get(answer.id, [])[:9],
            "user": user if user else {},
            "tags": question_tags_dic.get(answer.question_id, []),
            "tags_v3": question_tags_v3_dic.get(answer.question_id, []),
            "is_voted": voted.get(answer.id, False),
            "video_cover_list": answer_videos_dic.get(answer.id, []),
            "question_videos": questino_videos_dic.get(answer.question_id, []),
            'comment_num': 0,  # 临时去掉
            'question_answer_num': 0,  # 临时去掉
        })

        if new_info_control:
            _data.update({
                "new_user_info": _data["user"],
                "is_following": following_dic.get(answer.user_id, False),
            })

        answer_sorted_list.append(_data)

    #  v 7.6.35 记录首页推荐回答的浏览量
    add_answer_view.delay(answer_ids=answer_ids)

    return answer_sorted_list


@bind('qa/answer/list_for_index_recommend')
@listing()
def get_answer_for_index_recommend(start_num, count):
    """
    首页推荐回答，Feed策略上线之前，数据为Fake
    :param start_num:
    :param count:
    :return:
    """
    user = get_current_user()
    queryset = Answer.objects.order_by('-like_num')[start_num: start_num + count]
    return [answer.data_for_list(user) for answer in queryset]


@bind('qa/question/list_for_index_recommend')
@listing()
def get_question_for_index_recommend(start_num, count):
    """
    首页推荐问题，Feed策略上线之前，数据为Fake
    :param start_num:
    :param count:
    :return:
    """
    user = get_current_user()
    queryset = Question.objects.order_by('-like_num')[start_num: start_num + count]
    return [question.data_for_list(user) for question in queryset]


@bind('qa/notify/recommended')
def notify_recommended_user(uid, answer_id):
    """
    当运营后台设置回答为推荐时，触发该接口通知用户消息
    """
    push_url = gm_protocol.get_answer_list(answer_id)
    extra = {
        'type': PUSH_INFO_TYPE.GM_PROTOCOL,
        'msgType': 4,
        'pushUrl': push_url,
        'push_url': push_url
    }

    push_task_to_user_multi(user_ids=[uid],
                            platform=[PLATFORM_CHOICES.ANDROID, PLATFORM_CHOICES.IPHONE],
                            extra=extra,
                            alert='恭喜美人，你的优质回答已经被所长推荐了哦，请继续加油，一举成为更美红人吧！',
                            push_type=AUTOMATED_PUSH.ANSWER_RECEIVED_COMMENTS)

    rpc_client['api/notification/create'](uid=uid,
                                          title='您的回答被评为优秀至发现首页',
                                          content='恭喜美人，你的优质回答已经被所长推荐了哦，请继续加油，一举成为更美红人吧！',
                                          url=push_url).unwrap()


@bind("qa/answer/reseived_aggregation")
def answer_message_received_user():
    """
    消息 新加回答 信息展示接口 (与回复逻辑雷同)
    :return: result 字典类型，count 总数，last_user_info 最新用户
    """
    result = {
        "count": 0,
        "last_user_info": {},
    }
    user = get_current_user()
    if not user:
        return result

    result.update(get_unread_answer_info_by_user_id(user.id))
    #result["count"] += get_unread_count_by_user_id(user.id)

    return result


@bind("qa/answer/received")
@listing()
def get_my_answer_received(start_num, count=10):
    """
    消息 - 回答 列表页
    :param start_num:
    :param count:
    :return: data list 类型
    """
    user = get_current_user()
    data = []  # 返回给前端的信息
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    # 我邀请的人对邀请回答的问题，做出了回答
    invite_answers = QuestionInviter.objects.filter(user=user.id, answer_id__isnull=False).values("answer_id", "user_read")
    answer_id_state_dic = {answer["answer_id"]: answer["user_read"] for answer in invite_answers}  # 把数据做一次组装
    invited_answer_ids = list(answer_id_state_dic.keys())  # 被邀请人做出回答的id
    my_question_ids = list(Question.objects.filter(is_online=True, user=user.id).values_list("id", flat=True))
    query = Q(is_online=True, question__is_online=True) & (Q(question_id__in=my_question_ids) | Q(id__in=invited_answer_ids))
    answers = Answer.objects.filter(query).order_by("-create_time").distinct()[start_num: start_num + count]

    if not answers:
        return data

    normal_answer_ids = []  # 我提的问题被回答的id
    invite_answer_ids = []  # 我邀请他人回答问题，已回答的id

    for answer in answers:
        item = answer.data_for_received()
        if answer.id in answer_id_state_dic:
            invite_answer_ids.append(answer.id)
        else:
            normal_answer_ids.append(answer.id)
        data.append(item)

    # 只有存在 出现被邀请人回答的id,才会去查邀请者的查看状态
    if invite_answer_ids:
        for item in data:
            answer_id = int(item["answer"]["id"])
            if answer_id in answer_id_state_dic:
                item["answer"]["is_new"] = not answer_id_state_dic[answer_id]

    Answer.objects.filter(id__in=normal_answer_ids, questioner_read=False).update(questioner_read=True)  # 更新已读状态
    QuestionInviter.objects.filter(answer_id__in=invite_answer_ids, user=user.id, user_read=False).update(user_read=True)
    return data


@bind("qa/answer/received_v1")
@listing()
def get_my_answer_received_v1(answer_ids):
    """
    消息 - 回答 列表页
    :param answer_ids:
    :return: data list 类型
    """
    # 7770 需求，回答、回复合并，先从混排缓存中得到对应的数据，然后根据id进行数据获取
    if not answer_ids:
        return {}

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

    invite_answers = QuestionInviter.objects.filter(user=user.id, answer_id__isnull=False).values("answer_id", "user_read")
    answer_id_state_dic = {answer["answer_id"]: answer["user_read"] for answer in invite_answers}  # 把数据做一次组装
    # 我的问题的回答 + 被邀请的问题回答
    answers = Answer.objects.filter(is_online=True, question__is_online=True, pk__in=answer_ids)
    if not answers:
        return {}

    normal_answer_ids = []  # 我提的问题被回答的id
    invite_answer_ids = []  # 我邀请他人回答问题，已回答的id

    data = []
    for answer in answers:
        item = answer.data_for_received()
        item["id"] = answer.id
        if answer.id in answer_id_state_dic:
            invite_answer_ids.append(answer.id)
        else:
            normal_answer_ids.append(answer.id)
        data.append(item)

    # 只有存在 出现被邀请人回答的id,才会去查邀请者的查看状态
    if invite_answer_ids:
        for item in data:
            answer_id = int(item["answer"]["id"])
            if answer_id in answer_id_state_dic:
                item["answer"]["is_new"] = not answer_id_state_dic[answer_id]

    Answer.objects.filter(id__in=normal_answer_ids, questioner_read=False).update(questioner_read=True)  # 更新已读状态
    QuestionInviter.objects.filter(answer_id__in=invite_answer_ids, user=user.id, user_read=False).update(user_read=True)

    return {
        str(item["id"]): item
        for item in data
    }


@bind("qa/question/recommend_answer_list")
@listing()
def get_question_can_answer(start_num, count=10):
    """
    去回答列表页
    :param start_num:
    :param count:
    :return:
    """
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    result = []
    #  邀请回答的问题，取 今天下午五点到昨天下午五点之间 所有未回答的问题
    today = datetime.datetime.now().replace(hour=17, minute=0, second=0)
    last_day = today - datetime.timedelta(days=1)
    query = Q(inviter=user.id, answer_id__isnull=True, invite_time__range=[last_day, today])
    invited_questions = QuestionInviter.objects.filter(query).order_by("-invite_time")  # 查询满足条件的所有邀请问题表里的数据
    # 用来 筛选 及 计算 邀请问题的个数
    invite_question_ids = invited_questions and list(set(invited_questions.values_list("question_id", flat=True))) or []

    if len(invite_question_ids) > start_num:  # 取邀请的信息

        pre_invite_dic = {}  # 用来预处理的邀请问题信息
        for invite in invited_questions:
            question_id = invite.question_id
            if question_id in pre_invite_dic:
                pre_invite_dic[question_id]["invite_count"] += 1
            else:
                pre_invite_dic[question_id] = {
                    "question_id": str(question_id),
                    'title': invite.question.title,
                    'answer_num': invite.question.answer_num,
                    'user_id': invite.user.id,
                    'nickname': invite.user.nickname,
                    'portrait': invite.user.portrait,
                    'invite_count': 1,
                    "invite_time": invite.invite_time,
                    'type': QUESTION_SOURCE.INVITATION,
                    "is_online": invite.question.is_online,
                }
        invite_list = sorted(list(pre_invite_dic.values()), key=lambda data: data["invite_time"], reverse=True)
        invite_list = list(filter(lambda data: data["is_online"], invite_list))
        for invite in invite_list:
            del invite["invite_time"]

        result += invite_list[start_num: start_num + count]

    if len(result) < count:  # 取推荐的信息
        if start_num < len(invite_question_ids):
            start_num = 0
        else:
            start_num = start_num - len(invite_question_ids)
        new_count = count - len(result)

        questions = Question.objects.filter(is_online=True).order_by("-create_time")[start_num: start_num + new_count]
        #  获取我已经回答的问题的id
        answered_question_ids = list(Answer.objects.filter(user=user.id).values_list("question_id"))
        invite_question_ids += answered_question_ids

        question_list = []
        for question in questions:
            if question.id in invite_question_ids:
                continue

            data = question.data_for_list()
            data.update({
                "type": QUESTION_SOURCE.RECOMMEND
            })
            question_list.append(data)
        result += question_list
    return result


@bind("qa/question/inviter_list")
@listing()
def get_inviter_list(question_id, start_num, count=10):
    """
    邀请人列表页
    :param question_id: 问题id
    :param start_num: 起始
    :param count: 个数
    :return:
    """
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    # 先看当前问题、当前用户下，有没有被邀请的用户
    inviters = list(
        QuestionInviter.objects.filter(question_id=question_id, user=user.id).values_list("inviter", flat=True))
    # 计算出来的，满足邀请条件的user_ids
    question_inviter_pool = question_inviter_cache.get("question_inviter_pool")
    user_pool = question_inviter_pool and json.loads(question_inviter_pool) or []
    user_ids = list(filter(lambda x: x not in inviters, user_pool))  # 过滤掉已经邀请的用户
    # 如果当前用户在邀请人内，过滤掉！
    if user.id in user_ids:
        user_ids.remove(user.id)
    return user_ids[start_num: start_num + count]


@bind("qa/question/invite_status")
def question_invite_status(question_id, inviter):
    """
    点击邀请，写入库的接口
    :param question_id: # 邀请回答的问题id
    :param inviter: # 被邀请人 user_id
    :return: 返回状态
    """
    result = {}

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

    if not inviter or not question_id:
        return gen(CODES.PARAMS_INCOMPLETE)

    data = {"user": int(user.user_id), "inviter": int(inviter), "question_id": int(question_id)}
    create = QuestionInviter.objects.create(**data)

    if create:
        result["status"] = True
    else:
        result["status"] = False

    return result


@bind('qa/answer/tags')
def get_answer_tags(answer_id):
    tags = []
    try:
        question = Answer.objects.get(id=answer_id).question
        tags = question.tags.values_list('tag', flat=True)
    except Answer.DoesNotExist:
        pass

    return list(tags)


@bind('qa/question/tags')
def get_question_tags(question_id=None, answe_id=None):

    if not question_id and not answe_id:
        return []

    if not question_id and answe_id:
        try:
            answer = Answer.objects.get(id=answe_id)
        except Answer.DoesNotExist:
            return []

        question_id = answer.question_id

    tags = list(QuestionTag.objects.filter(question_id=question_id).values_list('tag', flat=True))
    return tags


@bind('qa/mip/answer_list_by_question_id')
def get_answer_list_by_question_id_for_mip(question_id):
    """
    通过 question_id 获取 满足 mip 需求的answer数据
    mip 二期，增加返回问题信息 (问答数据 均不考虑是否在线情况) 去除推荐回答
    :param question_id:
    :return:
    """
    result = {
        "question_info": {},
        "doctor_answers": [],  # 医生回答的数据
        "recommend_answers": [],  # 推荐回答的数据
    }
    doctor_count = 0
    query = Q(question_id=question_id)

    question = Question.objects.filter(pk=question_id).first()
    if question:
        result["question_info"].update({
            'id': question_id,
            'title': question.title,
            'content': question.content,
            'create_time': get_timestamp_epoch(question.create_time),
            'view_num': question.view_amount,
        })

    # 医生回答的不重复数据 (公立医院，有职称，有科室)  等hera后台上线以后，把去重删除
    doctor_answer_query = query & Q(doctor_title__gte=DOCTOR_TITLE_TYPE.RESIDENT, hospital_type=HOSPITAL_TYPE.PUBLIC)

    doctor_answers = BDAnswer.objects.filter(doctor_answer_query).order_by("-is_recommend", "-like_num", "-id")
    doctor_id_list = []
    for answer in doctor_answers:
        if doctor_count < settings.MIP_LIMIT and answer.doctor_department and answer.doctor_id not in doctor_id_list:
            doctor_id_list.append(answer.doctor_id)
            _data = answer.data_for_list()
            result["doctor_answers"].append(_data)
            doctor_count += 1

    return result


def _get_relation_college_tag_question_ids(start_num, count, exclude_ids=None):
    """
    获取关联大学类型标签的问题id
    :param start_num: 起始数
    :param count: 个数
    :param exclude_ids:需要排除的id
    :return: question_id list
    """
    _tags = complains_question_cache.get("tag_ids")
    tags = _tags and json.loads(_tags) or []
    kw = {
        "filters": {
            "raw_tag_ids": tags,
            "answer_num_range": {"gte": settings.ANSWER_NUM_BASELINE},
        },
        "offset": start_num,
        "size": count,
        "sort_type": QUESTION_ORDER_TYPE.MOUTHGUN_RECOMMEND,
    }
    questions = rpc_client["api/question/filter"](**kw).unwrap()
    question_ids = [item.get('question', 0) for item in questions.get("question_ids", {})]

    if exclude_ids:
        question_ids = list(filter(lambda qid: qid not in exclude_ids, question_ids))

    return list(filter(None, question_ids))


@bind("qa/xcx/question_recommends")
def get_relation_college_tag_questions(from_home=False, exclude_ids=None, start_num=0, count=10):
    """
    吐槽小程序，推荐问题，该接口有两个作用：
    1、首页进入的随机推荐回答id
    2、相关推荐的问题列表
    :param exclude_ids: 需要排除的id list结构
    :param from_home: 是否是来自于首页：bool
    :param start_num:
    :param count:
    :return: result {}
    """
    result = {
        "question_id": 0,  # 首页推荐的问题
        "question_list": []  # 问答详情页，相关推荐问题
    }

    if from_home:
        # 先取后台推荐的问题
        recommends = complains_question_cache.get("recommend")
        _recommend_ids = recommends and json.loads(recommends) or []

        # 实时取在线的
        question_ids = _recommend_ids and list(Question.objects.filter(
            pk__in=_recommend_ids, is_online=True).values_list("id", flat=True)) or []

        if not question_ids:  # 取不到按照推荐问题，取第一个
            question_ids = _get_relation_college_tag_question_ids(0, 1)

        result["question_id"] = question_ids and random.choice(question_ids) or 0
        return result

    kw = {
        "start_num": start_num,
        "count": count,
        "exclude_ids": exclude_ids,
    }
    question_ids = _get_relation_college_tag_question_ids(**kw)
    questions = Question.objects.filter(pk__in=question_ids, is_online=True)

    for question in questions:
        if question.answer_num >= settings.ANSWER_NUM_BASELINE:
            result["question_list"].append(question.data_for_list())

    return result


@bind("qa/answer/get_answer_sub_replies")
@listing()
def get_answer_sub_replies(answer_id, reply_id, start_num=0, count=10, new_order=False):
    """
    获取一级评论的子评论
    :param answer_id: 回答id
    :param reply_id: 一级评论的id
    :param start_num: 起始数
    :param count: 每次的个数
    :return:
    """
    try:
        reply = AnswerReply.objects.get(answer_id=answer_id, id=reply_id, is_online=True)
    except AnswerReply.DoesNotExist:
        return gen(CODES.ANSWER_NOT_FOUND)

    data = {
        "sub_replies": reply.get_all_comments(
            need_newest=True, start_num=start_num, count=count, new_order=new_order, return_images=True
        ),
    }

    return data


@bind("qa/answer/replies/receive")
def get_my_answer_replies_receive(replies_ids):
    """获取我收到的回答帖评论。"""

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

    user_ids = []
    answer_ids = []

    topic_replies_dict = AnswerTools.get_answer_replies(replies_ids)
    for _, reply in topic_replies_dict.items():
        # reply.commented_reply 表示被评论的回复，有则表示当前是二级
        answer_ids.append(reply.answer_id)
        user_ids.append(reply.user_id)  # 评论用户id

    answers_dict = {}
    answers = Answer.objects.filter(pk__in=answer_ids, is_online=True)
    for answer in answers:
        user_ids.append(answer.user_id)  # 问题的用户id
        answers_dict[answer.id] = answer

    users_dict = UserService.get_users_by_user_ids(user_ids)

    doctors_dict = {}
    doctors = DoctorService.get_doctor_from_user_ids(user_ids)
    for doctor in doctors:
        doctors_dict[doctor.user_id] = doctor

    result = {}
    for reply_id, reply in topic_replies_dict.items():

        reply_user = users_dict.get(reply.user_id, {})

        doctor = doctors_dict.get(reply.user_id, {})
        doctor_info = {}
        if doctor:
            doctor_info = {
                "user_id": doctor.user_id,
                "doctor_id": doctor.id,
                "name": doctor.name,
            }

        commented_reply = {}
        if  reply.commented_reply:
            commented_reply = {
                "id": reply.commented_reply_id,
                "content": reply.commented_reply.content
            }

        info = {
            "id": reply.id,
            "answer_id": reply.answer_id,

            "is_deleted": reply.is_online,
            "is_new": reply.is_read,
            "reply_date": reply.create_time.strftime("%Y-%m-%d %H:%M:%S"),
            "reply_content": reply.content,
            "commented_reply": commented_reply,
            "my_content": commented_reply["content"] if commented_reply else reply.answer.content,

            "reply_user": {
                "id": reply_user.id,
                "name": reply_user.nickname,
                "portrait": reply_user.portrait,
            },
            "author": {
                "id": user.id,
                "name": user.nick_name,
                "portrait": user.portrait,
            },

            "doctor": doctor_info,
        }
        result[str(reply_id)] = info

    return result


@bind('qa/answer_reply/read')
def qa_answer_reply_read():

    user = get_current_user()
    if not user:
        return

    my_reply_ids = list(AnswerReply.objects.filter(
        user=user.id, is_online=True
    ).values_list("id", flat=True))
    AnswerReply.objects.filter(
        commented_reply_id__in=my_reply_ids,
        is_online=True,
        is_read=False
    ).update(is_read=True)

    answer_ids = list(Answer.objects.filter(
        user=user.id, is_online=True
    ).values_list("id", flat=True))
    AnswerReply.objects.filter(
        answer_id__in=answer_ids,
        is_online=True,
        is_read=False
    ).update(is_read=True)


@bind("qa/answer/get_share_data")
def get_answer_share_data_by_id(answer_id):
    try:
        answer = Answer.objects.get(pk=answer_id, is_online=True)
    except Answer.DoesNotExist:
        return gen(CODES.ANSWER_NOT_FOUND)

    try:
        question = Question.objects.get(pk=answer.question_id, is_online=True)
    except Question.DoesNotExist:
        return gen(CODES.QUESTION_NOT_FOUND)

    _data = {
        "question_id": question.id,
        "answer_id": answer_id,
        "answer_content": answer.content,
        "question_content": question.content,
        "question_tags": [tag_obj.tag for tag_obj in question.tags],
        "question_old_images": question.content_images,
        "answer_old_images": answer.content_images,
    }
    return _data


@bind('mimas/user/follow/answer/list')
def user_follow_answer_list(offset=0, count=10):
    """
    我关注的用户的回答列表
    :param user_id:
    :param offset:
    :param count:
    :return:
    """
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    user_follow_ids = list(UserFollow.objects.filter(
        user_id=user.id, bond=True).order_by('-id').values_list('follow_id', flat=True))
    questions = Question.objects.filter(
        user__in=user_follow_ids, is_online=True).order_by('-id')[offset: offset+count]
    data = []
    question_ids = []
    for question in questions:
        data.append(question.data_for_list())
        question_ids.append(question.id)
    add_question_view.delay(question_ids)
    return data


@bind("qa/question/get_relation_questions_for_detail")
def get_relation_questions_for_detail(question_ids):
    """
    获取问题详情页下推荐的问题数据
    :param question_ids:
    :return:
    """
    questions = Question.objects.filter(is_online=True, pk__in=question_ids)
    question_tags = QuestionTag.objects.filter(question_id__in=question_ids)
    _q_tags_dic = {}
    for q_tag in question_tags:
        _q_id = q_tag.question_id

        if _q_id not in _q_tags_dic:
            _q_tags_dic[_q_id] = []

        _q_tags_dic[_q_id].append(q_tag.tag)

    all_answer_vote_nums_dic = AnswerTools.get_question_all_answer_vote_nums(question_ids)

    result, valid_question_ids = [], []
    for question in questions:
        _id = question.id
        _data = {
            "question_id": _id,
            "title": question.title,
            "content": question.content,
            "tags": _q_tags_dic.get(_id, []),
            "all_answer_vote_nums": all_answer_vote_nums_dic.get(_id, 0),
            "answer_num": question.answer_num,
        }
        result.append(_data)
        valid_question_ids.append(_id)

    add_question_view.delay(valid_question_ids)
    return sorted(result, key=lambda item: question_ids.index(item["question_id"]))


@bind('qa/answer_detail_by_id')
def get_answer_detail_v1(answer_id):
    """
    7210新增回答详情页
    :param answer_id:
    :param header_image: 是否需要头图
    :return:
    """
    if not (answer_id and answer_id.isdigit()):
        return gen(CODES.INVALID_PARAMS)

    user = get_current_user()
    try:
        answer = Answer.objects.get(id=int(answer_id), is_online=True)
    except Answer.DoesNotExist:
        return {}
    current_user_id = user and user.id or 0
    user_ids = [answer.user_id]
    if current_user_id:
        user_ids.append(current_user_id)
    user_info_dic = UserConvertService.get_user_info_by_user_ids(user_ids)
    following_dic = QaTool.get_following_info([answer.user_id], current_user_id)

    if current_user_id:
        is_favored = AnswerFavor.objects.filter(answer_id=answer.id, user_id=current_user_id, is_online=True).exists()
    else:
        is_favored = False

    base_data = answer.data_for_qa(user)

    user = user_info_dic.get(answer.user_id, {})
    base_data.update({
        "user": user,
        "is_following": following_dic.get(answer.user_id, False),
        "is_author": True if current_user_id == answer.user_id else False,
        'current_user': user_info_dic.get(current_user_id, {}),
        'related_tags': [tag.tag for tag in answer.question.tags if tag.tag],
        'tags': [tag.tag for tag in answer.answer_tags.all() if tag.tag],
        'is_favored': is_favored,
    })

    base_data.update({
        'user_name': user.get("user_name", ""),
        'user_portrait': user.get("portrait", ""),
        'membership_level': user.get("membership_level", ""),
        'user_level': user.get("user_level", {}) or base_data["user_level"],
    })

    _images = []
    images = AnswerImage.objects.filter(
        answer=answer, image_url_source=MEDIA_IMAGE_URL_SOURCE.HEAD,
    ).order_by('id')[:9]
    for image in images:
        _images.append(image.image_info_data)
    base_data['header_images'] = _images

    return base_data


@bind('qa/answer/edit')
def answer_edit(answer_id, content, images, question_id, platform=None, head_images=[], tags=[]):
    """
    7210新增回答编辑
    http://wiki.wanmeizhensuo.com/pages/viewpage.action?pageId=32018263
    :param answer_id:
    :param content:
    :param images:
    :param question_id:
    :param platform:
    :return:
    """
    result = {
        'answer_id': answer_id,
    }
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    if not (answer_id and answer_id.isdigit()):
        return result
    try:
        answer = Answer.objects.get(id=int(answer_id), is_online=True)
    except Answer.DoesNotExist:
        return result
    if answer.user_id != user.id:
        return gen(CODES.OPERATION_NOT_SUPPORTED)
    # 敏感词过滤
    content_data = get_qa_content_text(platform, content)
    filterword_by_custom(filter_type=FILTER_WORD_TYPE.TOPIC_CONTENT, content=content_data)

    _images = get_media_extra_info(images)
    data = {'level': 0}     # 编辑之后等级变为为分级
    if platform == GRABBING_PLATFORM.GM and content:
        content = json.loads(content)
        rich_text_dic = refine_qa_content_data(content)
        rich_images_list = get_media_extra_info(rich_text_dic.get("images_list", []))
        for item in rich_images_list:
            item.update({
                "image_url_source": MEDIA_IMAGE_URL_SOURCE.RICH_TEXT,
            })
        _images.extend(rich_images_list)
        data.update(format_qa_content_v2(platform, content))

        for k, v in data.items():
            setattr(answer, k, v)
        answer.save()
        answer.del_cache()  # 删除缓存
        # 删除
        AnswerImage.objects.filter(answer_id=answer.id).delete()
        # 创建
        _head_images = get_head_image_info(head_images)
        _images = _head_images + _images
        image_urls = [image_data['image_url'] for image_data in _images if image_data['image_url']]
        image_dic = handle_image_type(image_urls)
        gif_create_webp.delay(conent_id=answer.id, content_type='answer')

        image_objs = [
            AnswerImage(
                answer=answer,
                image_url=image_data['image_url'],
                width=image_data.get("width", 0),
                height=image_data.get("height", 0),
                image_url_source=image_data.get("image_url_source", MEDIA_IMAGE_URL_SOURCE.CREATE),
                image_type=image_dic.get(image_data['image_url'], IMAGE_TYPE.OTHER) or IMAGE_TYPE.OTHER,
            ) for image_data in _images if image_data.get('image_url')
        ]
        if image_objs:
            AnswerImage.objects.bulk_create(image_objs)

        # 删除标签
        AnswerTag.objects.filter(answer_id=answer.id).delete()

        # 创建标签
        tag_obj_list = list()
        for tag in tags:
            tag_obj_list.append(
                AnswerTag(
                    answer=answer,
                    tag=tag
                )
            )

        if tag_obj_list:
            AnswerTag.objects.bulk_create(tag_obj_list)

        # 编辑完成触发 处理图片等异步任务
        check_spam.delay(answer.id, ['content'], DocType.ANSWER)
        if data.get("content_type", "") == QA_CONTENT_TYPE.VIDEO:
            _, video_list = get_data_from_rich_text(
                data.get("content", ""),
                u'//video[not(@name="new_video")]/@src'
            )
            if video_list:
                get_qiniu_persistent_ids.delay(
                    source_id=answer.id,
                    video_type=VIDEO_SOURCE_TYPE.ANSWER,
                    url_list=video_list
                )

    return result


@bind('qa/question/batch_get_answer_count')
@listing()
def batch_get_answer_count(question_id_list):
    res = Answer.objects.using(settings.SLAVE_DB_NAME).filter(question_id__in=question_id_list, is_online=True).\
        values("question_id").annotate(cnt=Count("id")).values("question_id", "cnt")

    return {str(item["question_id"]): item["cnt"] for item in res}


# --- 待废弃 ---
@bind("qa/question/new_answer_list")
@listing()
def get_question_detail_new_answer_list(question_id, top_answer_id=0, start_num=0, count=10):
    """
    新版获取回答列表
    :param question_id:
    :param top_answer_id:置顶的回答id
    :param start_num:
    :param count:
    :return:
    """
    try:
        _ = Question.objects.get(pk=question_id, is_online=True)
    except Question.DoesNotExist:
        return gen(CODES.QUESTION_NOT_FOUND)

    user = get_current_user()

    if top_answer_id and not start_num:
        top_answer_obj = Answer.objects.filter(question_id=question_id, pk=top_answer_id, is_online=True).first()
    else:
        top_answer_obj = None

    normal_answers_list = Answer.objects.filter(
        question_id=question_id, is_online=True
    ).order_by("-like_num", "-id")[start_num: start_num + count]

    original_list = []
    if top_answer_obj:
        original_list.append(top_answer_obj)
    original_list.extend(list(normal_answers_list))

    user_ids, answer_ids = set(), set()
    answers_list = []
    for a in original_list:
        _answer_id = a.id
        if _answer_id in answer_ids:
            continue

        user_ids.add(a.user_id)
        answer_ids.add(_answer_id)
        answers_list.append(a)

    add_answer_view.delay(answer_ids=list(answer_ids))
    _user_pub_nums_dic = AnswerTools.get_user_publish_answer_nums(user_ids)
    _user_pub_fvr_nums_dic = AnswerTools.get_user_publish_answer_vote_nums(user_ids)
    answers_info_list = AnswerTools.get_questions_data(user, answers_list)
    header_images_dic = AnswerTools.get_header_images_by_answer_ids(answer_ids)     # 增加头图展示
    for answer_info in answers_info_list:
        _user_id = (answer_info.get('author', {})).get("user_id", 0)
        answer_info.update({
            "answer_user_pub_nums": _user_pub_nums_dic.get(_user_id, 0),
            "answer_user_pub_favor_nums": _user_pub_fvr_nums_dic.get(_user_id, 0),
            'header_images': header_images_dic.get(answer_info.get('id'), [])
        })

    return answers_info_list


@bind("qa/question/new_answer_list/v1")
def get_new_answer_list_v1(answer_ids):
    """
    获取回答列表 来自策略推荐
    :param answer_ids:
    :return:
    """
    if not answer_ids or not isinstance(answer_ids, (list, set)):
        return []
    user = get_current_user()
    answers = Answer.objects.filter(
        id__in=answer_ids, is_online=True,
    )
    user_ids = set([answer.user_id for answer in answers])
    answer_ids = set(answer_ids)

    pub_answer_nums_dic = AnswerTools.get_user_publish_answer_nums(user_ids)
    vote_nums_dic = AnswerTools.get_user_publish_answer_vote_nums(user_ids)
    header_images_dic = AnswerTools.get_header_images_by_answer_ids(answer_ids)

    answer_data = AnswerTools.get_questions_data(user, answers)
    for answer in answer_data:
        _user_id = (answer.get('author', {})).get("user_id", 0)
        answer.update({
            "answer_user_pub_nums": pub_answer_nums_dic.get(_user_id, 0),
            "answer_user_pub_favor_nums": vote_nums_dic.get(_user_id, 0),
            'header_images': header_images_dic.get(answer.get('id'), [])
        })
    add_answer_view.delay(answer_ids=list(answer_ids))

    return answer_data

# --- 待废弃 ---


@bind('qa/tags/by_question_id')
def get_question_tags_by_id(question_id):
    """
    根据问题id获取关联标签
    :param question_id:
    :return:
    """
    result = {
        'tags': []
    }
    try:
        question = Question.objects.get(id=question_id)
    except Question.DoesNotExist:
        return result
    tag_serializer = QuestionTagSerializer(question.tags, many=True)
    tags = [tag['tag'] for tag in tag_serializer.data if tag['tag']]
    if not tags:
        return result
    result['tags'] = tags

    return result


@bind('qa/question/user')
def get_user_info_by_question(question_id):
    result = {}
    try:
        question = Question.objects.get(id=question_id)
    except Question.DoesNotExist:
        return result
    user_info_dic = UserConvertService.get_user_info_by_user_ids([question.user_id])
    author = user_info_dic.get(question.user_id, {})

    return author


@bind('mimas/user_favored/answers')
def get_favored_answers(offset=0, count=10):
    """
    收藏的回答列表
    :param offset:
    :param count:
    :return:
    """
    user = get_current_user()
    if not user:
        return []

    answer_ids = list(AnswerFavor.objects.filter(
        user_id=user.id, is_online=True,
    ).values_list('answer_id', flat=True).order_by('-update_time')[offset: offset+count])

    if not answer_ids:
        return []

    quality_questions = QualityUserQuestion.objects.filter(
        user_id=user.id, answer_id__in=answer_ids, is_online=True,
    ).values('quality_question_id', 'answer_id')
    quality_question_ids = [qq['quality_question_id'] for qq in quality_questions]

    author_replied_ids = list(QualityAuthorAnswer.objects.filter(
        quality_question_id__in=quality_question_ids,
    ).values_list('quality_question_id', flat=True))

    replied_dict = {
        item['answer_id']: True for item in quality_questions if item['quality_question_id'] in author_replied_ids
    }
    answers = Answer.objects.filter(id__in=answer_ids, is_online=True)
    data = []
    for answer in answers:
        answer_data = answer.data_for_list(user)
        answer_data['received_author_reply'] = replied_dict.get(answer.id, False)
        data.append(answer_data)

    # 按照收藏时间倒序
    data = sorted(data, key=lambda answer: answer_ids.index(int(answer.get('answer_id'))))

    return data


@bind('qa/question/get_tags')
def get_tags_by_question(question_id):
    """
    通过问题id获取标签信息.
    :param user_id:
    :return:
    """
    tag_v3_id_list = list(QuestionTagV3.objects.filter(question_id=question_id).values_list('tag_v3_id', flat=True))
    if tag_v3_id_list:
        return {'tags_v3_id': tag_v3_id_list}
    else:
        tags_id = list(QuestionTag.objects.filter(question_id=question_id).values_list('tag', flat=True))
        return {'tags_id': tags_id}


@bind('qa/answer/get_tags')
def get_answer_tags_by_id(answer_id):
    """
    通过回答id获取标签信息
    """
    tag_v3_id_list = list(AnswerTagV3.objects.filter(answer_id=answer_id).values_list('tag_v3_id', flat=True))
    if tag_v3_id_list:  ## 获取帖子新标签
        return {'tags_v3_id': tag_v3_id_list}
    else:
        # 获取帖子老标签
        tags_id = list(AnswerTag.objects.filter(answer_id=answer_id).values_list('tag', flat=True))
        return {'tags_id': tags_id}


@bind('qa/question/base_info')
def get_question_base_by_answer_ids(answer_ids):
    """
    通过回答ids获取 问题数据
    :param answer_ids:
    :return:
    """
    result = {}
    if not answer_ids:
        return result
    answer_ids = list(map(int, answer_ids))
    answers_questions = Answer.objects.select_related('question').filter(
        id__in=answer_ids, is_online=True, question__is_online=True,
    )

    for item in answers_questions.iterator():
        result[str(item.id)] = {
            'answer_id': item.id,
            'question_id': item.question.id,
            'question_title': item.question.title,
            'question_content': item.question.content,
        }

    return result


@bind('qa/answer/tags/by_answer_ids')
def get_answer_tags_by_ids(answer_ids):
    """
    通过回答ids获取标签信息
    """
    result = {
        'tag_v3': [],
        'tag_v1': [],
    }
    if not answer_ids:
        return result

    answer_tag3 = list(AnswerTagV3.objects.filter(
        answer_id__in=answer_ids
    ).values_list('tag_v3_id', 'answer_id'))
    tag_v3_info = TagV3Service.get_tags_by_tag_v3_ids(set(item[0] for item in answer_tag3))
    for tag_id, answer_id in answer_tag3:
        tag_v3 = tag_v3_info.get(tag_id, None)
        if tag_v3:
            result['tag_v3'].append(TagV3Service.format_tag_v3(tag_v3))

    else:
        tag_ids = list(AnswerTag.objects.filter(answer_id__in=answer_ids).values_list('tag', 'answer_id'))
        tags_info = TagService.get_tags_by_tag_ids(ids=set(item[0] for item in tag_ids))
        for tag_v1 in tags_info:
            tag_data = {
                'id': tag_v1.id,
                'name': tag_v1.name,
                "tag_id": tag_v1.id,
                "tag_name": tag_v1.name,
                "tag_type": tag_v1.tag_type,
            }
            result['tag_v1'].append(tag_data)

    return result
