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

import datetime

from itertools import chain
from urllib.parse import urljoin
from django.db import transaction
from django.db.models import F
from django.conf import settings

from gm_dataquery.dict_mixin import to_dict

from gm_types.mimas import (
    TRACTATE_STATUS,
    TRACTATE_PLATFORM,
    TRACTATE_CONTENT_LEVEL,
    TRACTATE_DATA_TYPE,
    VOTE_TASK_TYPE,
    COMMUNITY_CONTENT_TYPE,
    MEDIA_IMAGE_URL_SOURCE,
    IMAGE_TYPE,
    TRACATE_VIDEO_URL_SOURCE,
)
from gm_rpcd.all import (
    RPCDFaultException,
    bind,
)
from gm_dataquery.dict_mixin import to_dict
from gm_types.push import AUTOMATED_PUSH, PUSH_INFO_TYPE
from gm_upload.utils.image_utils import Picture
from talos.cache.base import reply_cache
from talos.models.soft_article import SoftArticleReply, SoftArticleExtra
from talos.tools.replies_tool import ReplyTool
from utils.common import get_data_from_rich_text
from utils.protocol import gm_protocol

from talos.libs.image_utils import fetch_picture_and_save_to_qiniu_v2
from talos.models.tractate import (
    Tractate,
    TractateTag,
    TractateImages,
    TractateVideo,
    TractateMessage,
    TractateCheck,
    TractateExtra,
    TractateNewTag,
    TractateRelationService,
    TractateTagV3,
)
from communal.tasks import create_fake_task
from talos.models.tractate.reply import TractateReply
from talos.rpc import (
    bind_context,
)
from talos.services import (
    UserService,
    get_user_from_context,
    AgileTagService,
    UserConvertService,
)
from talos.tasks.tractate import (
    set_tractate_video_water_mark_url,
    set_tractate_video_clipping_to_video,
)
from talos.services.tractate import TractateReplyService, TractateService
from talos.tasks.tractate import reply_push, applet_replied_push
from talos.cache.base import tractate_reply_count_cache
from utils.push import push_task_to_user_multi
from lxml import html
from hera.queries.qa import get_media_list_from_content
from qa.utils.image import handle_image_type
from qa.tasks.view import gif_create_webp


uri_pre = 'mimas/hera_tractate'


def tractate_tag_handle(tag_ids, old_tag_ids, tractate_id, model):
    """
    帖子标签逻辑处理
    :param tag_ids:
    :param old_tag_ids:
    :param tractate_id:
    :param model:
    :return:
    """
    need_add_tags = set(tag_ids) - set(old_tag_ids)
    need_del_tags = set(old_tag_ids) - set(tag_ids)
    for tag_id in need_add_tags:
        _ = model.objects.get_or_create(tractate_id=tractate_id, tag_id=tag_id)
    model.objects.filter(tractate_id=tractate_id, tag_id__in=need_del_tags).delete()


def tractate_tag_v3_handle(tag_ids, old_tag_ids, tractate_id, model):
    """
    帖子 标签3.0逻辑处理
    :param tag_ids:
    :param old_tag_ids:
    :param tractate_id:
    :param model:
    :return:
    """
    need_add_tags = set(tag_ids) - set(old_tag_ids)
    need_del_tags = set(old_tag_ids) - set(tag_ids)
    for tag_id in need_add_tags:
        _ = model.objects.get_or_create(tractate_id=tractate_id, tag_v3_id=tag_id)
    model.objects.filter(tractate_id=tractate_id, tag_v3_id__in=need_del_tags).delete()


def save_tractate_video_url(tractate_id, video_url, video_url_source=TRACATE_VIDEO_URL_SOURCE.RICH_TEXT):
    """
    保存用户帖的视频
    :param tractate_id: 用户帖id
    :param video_url: 视频地址
    :param video_url_source: 视频来源位置
    :return:
    """
    _video_cover_url = "{url}{params}".format(url=urljoin(settings.VIDEO_HOST, video_url), params=settings.VIDEO_PIC_URL)

    new_video_caver_url = fetch_picture_and_save_to_qiniu_v2(_video_cover_url)

    _kw = {
        "tractate_id": tractate_id,
        "raw_video_url": video_url,
        "video_url_source": video_url_source,
    }
    _base_data = Picture.get_image_base_info(new_video_caver_url)
    if not all(_base_data.values()):
        _base_data = {
            "width": 0,
            "height": 0,
        }

    defaults={
        "video_cover_url": new_video_caver_url,
        "is_online": True,
    }
    defaults.update(_base_data)

    video_obj, _ = TractateVideo.objects.update_or_create(defaults=defaults, **_kw)
    # 视频水印处理
    set_tractate_video_water_mark_url.delay(video_obj.id)


def update_tractate_rich_text_video(tractate_id, rich_text_content):
    _, video_list = get_data_from_rich_text(rich_text_content, u'//video[not(@name="new_video")]/@src')
    new_video_urls = list(map(TractateVideo.cleaned_video_url, video_list))

    old_video_urls = set(
        TractateVideo.objects.filter(
            tractate_id=tractate_id,
            video_url_source=TRACATE_VIDEO_URL_SOURCE.RICH_TEXT,
            is_online=True,
        ).values_list('raw_video_url', flat=True)
    )

    need_create_video_urls = set(new_video_urls) - old_video_urls
    for video_url in new_video_urls:
        if video_url in need_create_video_urls:
            save_tractate_video_url(tractate_id, video_url)

    need_delte_video_urls = old_video_urls - set(new_video_urls)
    TractateVideo.objects.filter(
        tractate_id=tractate_id,
        raw_video_url__in=list(need_delte_video_urls),
        video_url_source=TRACATE_VIDEO_URL_SOURCE.RICH_TEXT,
    ).update(is_online=False)


def update_tractate_rich_text_image(tractate_id, rich_text_content):
    """
    处理用户帖， 富文本中的图片
    :param tractate_id: 用户帖id
    :param rich_text_content: 富文本内容
    :return:
    """
    _images_list = get_media_list_from_content(rich_text_content, u'//img/@src')

    new_images = {image_item["image_url"] for image_item in _images_list if image_item.get("image_url", "")}
    old_images = set(TractateImages.objects.filter(
        tractate_id=tractate_id, image_url_source=MEDIA_IMAGE_URL_SOURCE.RICH_TEXT
    ).values_list("image_url", flat=True))

    _update_images = new_images - old_images
    images_list = [item.get("image_url", "") for item in _images_list]
    gevent_dic = handle_image_type(images_list)

    need_create_tractate_images = []
    for image_dic in _images_list:
        _image_url = image_dic.get("image_url", "")
        if _image_url and _image_url in _update_images:
            need_create_tractate_images.append(
                TractateImages(
                    tractate_id=tractate_id,
                    image_url=_image_url,
                    width=image_dic.get('width', 0),
                    height=image_dic.get('height', 0),
                    image_url_source=MEDIA_IMAGE_URL_SOURCE.RICH_TEXT,
                    image_type=gevent_dic.get(_image_url, IMAGE_TYPE.OTHER) or IMAGE_TYPE.OTHER,
                )
            )
    if need_create_tractate_images:
        TractateImages.objects.bulk_create(need_create_tractate_images)

    _delete_images = old_images - new_images
    TractateImages.objects.filter(tractate_id=tractate_id, image_url__in=_delete_images) \
        .exclude(image_url_source=MEDIA_IMAGE_URL_SOURCE.HEAD).delete()
    gif_create_webp.delay(conent_id=tractate_id, content_type='tractate')


def update_tractate_rich_text_image_video(tractate_id, rich_text_content):
    # 处理帖子视频
    update_tractate_rich_text_video(tractate_id, rich_text_content)

    # 处理帖子图片
    update_tractate_rich_text_image(tractate_id, rich_text_content)


@bind_context(uri_pre + '/get')
def get_tractate_data(ctx, tractate_id):
    if not tractate_id:
        return None
    try:
        tractate_obj = Tractate.objects.get(id=tractate_id)
        video = TractateVideo.objects.filter(
            tractate_id=tractate_id,
            video_url_source=TRACATE_VIDEO_URL_SOURCE.HEAD,
            is_online=True,
        ).first()
        tag_ids = list(TractateTag.objects.filter(tractate_id=tractate_id).values_list("tag_id", flat=True))
        new_tag_ids = list(TractateNewTag.objects.filter(tractate_id=tractate_id).values_list("tag_id", flat=True))
        tag_v3_ids = list(TractateTagV3.objects.filter(tractate_id=tractate_id).values_list("tag_v3_id", flat=True))
        images = list(TractateImages.objects.filter(
            tractate_id=tractate_id,
            image_url_source=MEDIA_IMAGE_URL_SOURCE.HEAD,
        ).values("image_url", 'is_cover'))
        service_ids = list(TractateRelationService.objects.filter(tractate_id=tractate_id).values_list("service_id", flat=True))
        return {
            "user_id": tractate_obj.user_id,
            "is_online": tractate_obj.is_online,
            "is_excellent": tractate_obj.is_excellent,
            "title": tractate_obj.title,
            "content": tractate_obj.content,
            "pgc_type": tractate_obj.pgc_type,
            "images": images,
            "tag_ids": tag_ids,
            "new_tag_ids": new_tag_ids,
            "tag_v3_ids": tag_v3_ids,
            "video_url": urljoin(settings.VIDEO_HOST, video.raw_video_url) if video else "",
            "status": tractate_obj.status,
            "user_del": tractate_obj.user_del,
            "low_quality": tractate_obj.low_quality,
            "low_quality_deal": tractate_obj.low_quality_deal,
            "service_ids": service_ids,
            "real_vote_amount": tractate_obj.real_vote_amount,
            "base_vote_num": tractate_obj.base_vote_num,
        }
    except Tractate.DoesNotExist:
        raise RPCDFaultException(code=1601, message="Integrity Error")


@bind_context(uri_pre + '/edit')
@transaction.atomic
def tractate_edit(ctx, tractate_id=None, tractate_info=None):
    tag_ids = list(map(int, tractate_info.pop('tag_ids', [])))
    new_tag_ids = list(map(int, tractate_info.pop('new_tag_ids', [])))
    tag_v3_ids = list(map(int, tractate_info.pop('tag_v3_ids', [])))
    _ = tractate_info.pop('real_vote_amount', 0)

    images = tractate_info.pop('images', [])
    video_url = tractate_info.pop("video_url", "")
    tractate_content_edit = False
    service_ids = tractate_info.pop("service_ids", [])

    if tractate_id is None:
        tractate_info['platform'] = TRACTATE_PLATFORM.HERA
        is_online = tractate_info.get("is_online", False)  # 后台不勾选上线，则至为暂存
        if is_online:
            tractate_info["status"] = TRACTATE_STATUS.UNAUDITED
        else:
            tractate_info["status"] = TRACTATE_STATUS.TEMPORARY_STORAGE
        tractate_obj = Tractate.objects.create(**tractate_info)

        # 处理富文本中的图片和视频变更
        update_tractate_rich_text_image_video(tractate_obj.id, tractate_obj.content)
    else:
        try:
            tractate_obj = Tractate.objects.get(id=tractate_id)
        except Tractate.DoesNotExist:
            raise RPCDFaultException(1404, message="Not Found")

        # 暂存状态的数据勾选了上线，则变为待审核
        pre_tractate_online = tractate_obj.is_online
        tractate_audit_status = tractate_obj.status
        pre_edit_tractate_is_online_status = tractate_info.get("is_online", False)

        if pre_edit_tractate_is_online_status and tractate_audit_status == TRACTATE_STATUS.TEMPORARY_STORAGE and \
                not pre_tractate_online:
            tractate_info["status"] = TRACTATE_STATUS.UNAUDITED

        # 预处理变更 审核拒绝 的状态
        tractate_content = tractate_info.get("content", "")
        pre_tractate_content = tractate_obj.content
        tractate_content_edit = tractate_content != pre_tractate_content

        for k, v in tractate_info.items():
            setattr(tractate_obj, k, v)
        tractate_obj.save()

        # 内容变更， 处理富文本中的图片和视频变更
        if tractate_content_edit:
            update_tractate_rich_text_image_video(tractate_obj.id, tractate_content)

    _tractate_id = tractate_obj.id

    # 标签 新老标签同时维护
    mapping_tag_dic = AgileTagService.get_relation_old_tags_by_agile_tag_ids(new_tag_ids)
    # 新标签对应的老标签
    mapping_old_tag_ids = set(map(int, chain(*mapping_tag_dic.values())))
    # 原有的老标签数据
    old_tag_ids = list(TractateTag.objects.filter(tractate_id=_tractate_id).values_list("tag_id", flat=True))
    # 原有的新标签数据
    agile_tag_ids = list(TractateNewTag.objects.filter(tractate_id=_tractate_id).values_list("tag_id", flat=True))
    # 标签3.0的数据
    tag_v3_old_ids = list(TractateTagV3.objects.filter(tractate_id=_tractate_id).values_list("tag_v3_id", flat=True))

    # 老标签逻辑维护
    tractate_tag_handle(list(mapping_old_tag_ids) + tag_ids, old_tag_ids, _tractate_id, TractateTag)
    # 新标签逻辑维护
    tractate_tag_handle(new_tag_ids, agile_tag_ids, _tractate_id, TractateNewTag)

    # 标签3.0逻辑维护
    tractate_tag_v3_handle(tag_v3_ids, tag_v3_old_ids, _tractate_id, TractateTagV3)

    # 图片
    TractateImages.objects.filter(
        tractate_id=_tractate_id,
        image_url_source=MEDIA_IMAGE_URL_SOURCE.HEAD,
    ).delete()

    #美购ID
    relation_objs = []
    old_service_ids = list(TractateRelationService.objects.filter(tractate_id=_tractate_id).values_list("service_id", flat=True))
    for item in service_ids:
        if not item:
            continue

        if int(item) in old_service_ids:
            old_service_ids.remove(int(item))
            continue

        relation_objs.append(TractateRelationService(tractate_id=_tractate_id, service_id=int(item)))

    TractateRelationService.objects.bulk_create(relation_objs)
    TractateRelationService.objects.filter(tractate_id=_tractate_id, service_id__in=old_service_ids).delete()

    _bulk_create_list = []
    for item in images:
        _kw = {
            "tractate_id": _tractate_id,
            "image_url": item['image_url'],
            'is_cover': item['is_cover'],
        }
        _base_data = Picture.get_image_base_info(item['image_url'])
        if not all(_base_data.values()):
            _base_data = {
                "width": 0,
                "height": 0,
            }
        _kw.update(_base_data)
        _bulk_create_list.append(TractateImages(**_kw))

    if _bulk_create_list:
        TractateImages.objects.bulk_create(_bulk_create_list)

    # 视频
    video_url = TractateVideo.cleaned_video_url(video_url)
    video_url_list = list(TractateVideo.objects.filter(
        tractate_id=_tractate_id,
        is_online=True,
        video_url_source=TRACATE_VIDEO_URL_SOURCE.HEAD,
    ).exclude(raw_video_url="").values_list("raw_video_url", flat=True))

    need_update_video_url = False
    need_create_video_url = False

    if not video_url and video_url_list:  # 把视频删了
        need_update_video_url = True

    elif video_url and video_url not in video_url_list:  # 把视频更新了
        need_update_video_url = True
        need_create_video_url = True

    elif video_url and not video_url_list:  # 本没有后来增加了
        need_create_video_url = True

    if need_update_video_url:
        TractateVideo.objects.filter(
            tractate_id=_tractate_id,
            video_url_source=TRACATE_VIDEO_URL_SOURCE.HEAD,
        ).update(is_online=False)

    if need_create_video_url:
        _video_cover_url = "{url}{params}".format(url=urljoin(settings.VIDEO_HOST, video_url), params=settings.VIDEO_PIC_URL)

        new_video_caver_url = fetch_picture_and_save_to_qiniu_v2(_video_cover_url)

        _kw = {
            "tractate_id": _tractate_id,
            "raw_video_url": video_url,
            "video_cover_url": new_video_caver_url,
            "is_online": True,
        }
        _base_data = Picture.get_image_base_info(new_video_caver_url)
        if not all(_base_data.values()):
            _base_data = {
                "width": 0,
                "height": 0,
            }
        _kw.update(_base_data)

        video_obj = TractateVideo.objects.create(**_kw)
        # 视频水印处理
        set_tractate_video_water_mark_url.delay(video_obj.id)
        # set_tractate_video_clipping_to_video.delay(video_obj.id)

    tractate_video_edit = need_update_video_url or need_create_video_url

    # 重新编辑帖子只要不是暂存状态，(图片，视频，内容 任意一个变动) 则需要把审核状态重置为待审核
    pre_audit_status = tractate_obj.status
    if tractate_id and pre_audit_status != TRACTATE_STATUS.TEMPORARY_STORAGE and any(
            [tractate_content_edit, tractate_video_edit]):
        tractate_obj.status = TRACTATE_STATUS.UNAUDITED
        tractate_obj.save(update_fields=["status"])

    return {
        'id': _tractate_id,
    }


@bind(uri_pre + '/reply_by_reply_id')
def reply_by_reply_id(reply_id, content, user_id='', tractate_id=0):
    """hera后台创建评论和回复"""
    if not reply_id and not tractate_id:
        return "参数错误"

    # 评论
    if not reply_id:
        tractate = TractateService.healthy(tractate_id)

    # 回复
    else:
        tractate_reply = TractateReplyService.healthy(reply_id)
        tractate = TractateService.healthy(tractate_reply.tractate_id)

        #更新评论数
        TractateReplyService.update_reply_count(top_reply_id=tractate_reply.top_id or tractate_reply.id)

    te, _ = TractateExtra.objects.get_or_create(tractate_id=tractate.id)
    te.reply_count = te.reply_count + 1
    te.save(update_fields=['reply_count'])

    user_id = user_id or tractate.user_id

    if reply_id:
        top_id = tractate_reply.top_id or tractate_reply.id
        replied_id = tractate_reply.id
    else:
        top_id = 0
        replied_id = 0

    reply = TractateReplyService.create(
        content,
        int(user_id),
        tractate.id,
        extra={
            "top_id": top_id,
            "replied_id": replied_id,
            "source_id": TRACTATE_PLATFORM.HERA,
            "is_fake": True,
        },
    )

    reply_push.delay(user_id=tractate.user_id, tractate_id=tractate.id, reply_id=reply.id, content=content)

    # 小程序推送
    applet_replied_push.delay(reply.id)

    return "回复成功" if reply_id else "评论成功"


@bind(uri_pre + '/show_reply')
def show_reply(reply_id):
    """Hera后台， 获取回复内容"""

    if not reply_id:
        return "回复失败"

    tractate_reply = TractateReply.objects.filter(replied_id=int(reply_id), source_id=TRACTATE_PLATFORM.HERA,
                                                  is_online=True).first()

    if not tractate_reply:
        return ''

    return tractate_reply.content


@bind(uri_pre + '/get_tractate_message_list')
def get_tractate_message():
    """
    获取预设话述列表
    """
    result = {}
    for key, value in TRACTATE_CONTENT_LEVEL:
        messages = TractateMessage.objects.filter(is_delete=False, content_level=key)
        result[key] = [{'id': item.id,
                        'description': item.description,
                        'title': item.title,
                        'message': item.message} for item in messages]
    return result


@bind(uri_pre + '/delete_tractate_message')
def delete_tractate_message(m_id):
    """
    删除预设话述
    """
    message = TractateMessage.objects.get(pk=m_id)
    message.is_delete = True
    message.save(update_fields=['is_delete'])


@bind(uri_pre + '/add_tractate_message')
def add_tractate_message(content_level, message, title, description):
    """
    添加预设话述
    """
    data = TractateMessage.objects.create(
        content_level=content_level, message=message, title=title, description=description)
    message_data = {
        'id': data.id,
        'message': data.message,
        'description': data.description,
        'level': data.content_level,
        'title': data.title
    }
    return message_data


@bind(uri_pre + '/get_tractate_message')
def get_message(level):
    message_data = []
    tractate_message = TractateMessage.objects.filter(content_level=level).filter(is_delete=False)
    for message in tractate_message:
        data = {
            'id': message.id,
            'title': message.title,
            'description': message.description,
            'message': message.message,
        }
        message_data.append(data)

    return message_data


@bind_context(uri_pre + '/audit_tractate')
@transaction.atomic
def aduit_tractate(ctx, tractate_check):
    user = get_user_from_context(ctx)

    _check_id = None

    if tractate_check:
        content_level = tractate_check['content_level']
        status = tractate_check['status']
        tractate_id = tractate_check['tractate_id']
        tractate_check_obj = TractateCheck(
            tractate_id=tractate_id,
            user_id=user.id,
            content_level=content_level,
            check_content=tractate_check['check_content'],
            send_message=tractate_check['send_message'],
            title=tractate_check['title'],
            status=status
        )
        tractate_check_obj.save()
        # 审核状态，内容等级，最终审核时间同步
        tractate_obj = Tractate.objects.get(id=tractate_id)

        # 审核完成 等级大于等于2 灌水
        if int(content_level) >= int(TRACTATE_CONTENT_LEVEL.BAD) \
                and not tractate_obj.audit_time and int(status) != int(TRACTATE_STATUS.AUDIT_REJECT):
            create_fake_task.delay(
                business_id=tractate_id, business_type=COMMUNITY_CONTENT_TYPE.TRACTATE,
                business_level=content_level, author_id=tractate_obj.user_id,
            )
        tractate_obj.content_level = content_level
        tractate_obj.status = status
        tractate_obj.audit_time = datetime.datetime.now()
        tractate_obj.save(update_fields=["status", "content_level", "audit_time"])

        _check_id = tractate_check_obj.id

    return {
        "id": _check_id
    }


@bind(uri_pre + '/get_aduit_one')
def get_aduit_one(tractate_id):
    """
    获取最新一条的审核信息
    :param tractate_id:
    :return:
    """
    tractate_check = TractateCheck.objects.filter(tractate_id=tractate_id).order_by('-id').first()
    if tractate_check:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
        user = UserService.get_user_by_user_id(tractate_check.user_id)

        content_level_num = tractate_check.content_level
        tractate_check = to_dict(tractate_check, **options)
        tractate_check['nick_name'] = user.nickname
        tractate_check['content_level'] = TRACTATE_CONTENT_LEVEL.getDesc(tractate_check["content_level"])
        tractate_check['content_level_num'] = content_level_num
        tractate_check['status'] = TRACTATE_STATUS.getDesc(tractate_check["status"])
        tractate_check['check_time'] = str(tractate_check['check_time']).split('.')[0]
    else:
        tractate_check = {}
    return tractate_check


@bind('mimas/tractate_fake_reply/create')
def fake_reply_create(tractate_id, data=None):
    """
    帖子评论灌水
    :param tractate_id:
    :param data:
    :return:
    """
    result = {
        'error': 0,
        'message': '创建成功',
        'data': ''
    }
    if not all([tractate_id, data]):
        result['message'] = '参数不完整'
        return result

    tractate = Tractate.objects.get(id=tractate_id)

    user_ids = [item['user_id'] for item in data]
    user_ids = set(filter(None, user_ids))
    user_dic = UserService.get_users_by_user_ids(user_ids)

    reply_list = []
    for reply_data in data:
        obj = {
            'tractate_id': tractate.id,
            'user_id': reply_data['user_id'],
            'content': reply_data['content'],
            'is_fake': True,
            'source_id': TRACTATE_PLATFORM.HERA,
            'top_id': 0,
            'replied_id': 0,
        }

        user = user_dic.get(int(reply_data['user_id']))
        nickname = user.nickname if user else '更美用户'
        push_dic = format_push_content(user_id=reply_data['user_id'], tractate=tractate, nickname=nickname)
        push_dic.update({
            'reply_obj': obj,
        })
        reply_list.append(push_dic)

    return {'reply_list': reply_list}


def format_push_content(user_id, tractate, nickname):

    push_url = gm_protocol.get_tractate_detail(tractate_id=tractate.id)
    push_type = AUTOMATED_PUSH.TRACTATE_GET_REPLY
    extra = {
        'type': PUSH_INFO_TYPE.GM_PROTOCOL,
        'msgType': 4,
        'pushUrl': push_url,
        'push_url': push_url,
    }
    push_msg = "{user}回复了#你的帖子#{content}".format(
        user=nickname,
        content=tractate.content[:6])

    _item = {
        'author_id': tractate.user_id,
        'reply_user_id': user_id,
        'nickname': nickname,
        'push_msg': push_msg,
        'push_type': push_type,
        'push_url': push_url,
        'extra': extra,
        '_type': TRACTATE_DATA_TYPE.USER,
    }
    return _item


@bind('mimas/reply_fake/push')
def reply_fake_push(user_id, push_type, reply_obj, extra=None, push_msg=None, _type=''):
    """
    评论灌水的消息页通知 和 push
    :param user_id:
    :param push_type:
    :param reply_obj:
    :param extra:
    :param push_msg:
    :param _type:
    :return:
    """

    if _type == TRACTATE_DATA_TYPE.USER:  # 目前医生帖评论在消息通知页不展示，所以不入缓存
        try:
            reply = TractateReply.objects.create(**reply_obj)
            # 增加帖子评论数
            te, _ = TractateExtra.objects.get_or_create(tractate_id=reply_obj.get('tractate_id'))
            te.reply_count = te.reply_count + 1
            te.save(update_fields=['reply_count'])
        except:
            return
        rt = ReplyTool(reply_cache, user_id)
        rt.receive_tractate_reply(reply.id)
    else:
        try:
            SoftArticleReply.objects.create(**reply_obj)
            # 增加帖子评论数
            te, _ = SoftArticleExtra.objects.get_or_create(softarticle_id=reply_obj.get('softarticle_id'))
            te.reply_count = te.reply_count + 1
            te.save(update_fields=['reply_count'])
        except:
            return

    push_task_to_user_multi(
        user_ids=[user_id], push_type=push_type,
        extra=extra,
        labels={'event_type': 'push', 'event': 'reply_fake_push'},
        alert=push_msg,
    )


@bind("mimas/tractate_reply/offline")
def offline_reply(reply_ids=[], action=0):
    if not reply_ids:
        return

    reply_cache_key = '{top_id}_reply_num'

    if not action:
        reply_infos = TractateReply.objects.filter(id__in=reply_ids)
        for reply in reply_infos:
            if not reply.is_online:
                continue
            if reply.top_id:
                tractate_reply_count_cache.decr(reply_cache_key.format(top_id=reply.top_id))
            reply.is_online = False
            reply.update_time = datetime.datetime.now()
            reply.save()
            tractate_extra = TractateExtra.objects.filter(tractate_id=reply.tractate_id).first()
            if not tractate_extra:
                continue
            tractate_extra.reply_count -= 1
            tractate_extra.save()
        return {}

    reply_infos = TractateReply.objects.filter(id__in=reply_ids)
    for reply in reply_infos:
        if reply.is_online:
            continue
        if reply.top_id:
            tractate_reply_count_cache.incr(reply_cache_key.format(top_id=reply.top_id))
        reply.is_online = True
        reply.update_time = datetime.datetime.now()
        reply.save()
        tractate_extra = TractateExtra.objects.get(tractate_id=reply.tractate_id).first()
        if not tractate_extra:
            continue
        tractate_extra.reply_count += 1
        tractate_extra.save()

    return {}


@bind("mimas/hera_tractate/reply_list")
def reply_list(tractate_id, size=10, top_id=0, last_id=0):
    """
    获取帖子评论，一级评论按照时间先后排序，二级评论跟着一级评论后
    :param tractate_id: 帖子id
    :param size:
    :param top_id: 一级评论id
    :param last_id: 最后一条评论的id
    :return:
    """
    reply_infos = []
    # 第一次查询
    if not last_id:
        # 获取size条一级和次级评论
        reply_infos = TractateReplyService.get_replies(tractate_id, last_id, size)
    else:
        # 获取当前一级评论剩余的次级评论 size 条
        sub_reply_info_list = list(TractateReply.objects.filter(
            tractate_id=tractate_id,
            top_id=top_id or last_id,
            is_online=True,
            id__gt=last_id,
        )[0: size])

        reply_infos.extend(sub_reply_info_list)

        # 如果总数小于size 往后查 size-len(reply_infos)条 一级和次级评论
        last_id = top_id or last_id
        if len(reply_infos) < size:
            _reply_infos = TractateReplyService.get_replies(tractate_id, last_id, size - len(reply_infos))
            reply_infos.extend(_reply_infos)

    if not reply_infos:
        return []

    reply_list = []
    user_id_set = set()

    for reply in reply_infos:
        reply_info =  to_dict(reply, fields=['id', 'tractate_id', 'user_id', 'top_id', 'content', 'create_time'])
        reply_list.append(reply_info)
        user_id_set.add(reply_info['user_id'])

    all_user_infos = UserConvertService.get_user_info_by_user_ids(list(user_id_set))

    for item in reply_list:
        item['user_name'] = all_user_infos.get(item['user_id'], {}).get('user_name', '')

    return reply_list


@bind("mimas/hera_tractate/delete_reply")
def delete_reply(reply_id):
    """
    删除评论
    :param reply_id: 评论id
    :return:
    """
    reply = TractateReplyService.healthy(reply_id)

    reply.is_online = False
    reply.save()

    # 如果是一级评论 则清空当前评论的评论缓存 、将top_id为当前评论id的评论下线，帖子额外数据，减(1+下线评论数)
    top_id = reply.top_id
    if not top_id:
        TractateReplyService.set_reply_count(reply_id, 0)
        effect_num = TractateReply.objects.filter(top_id=reply_id).update(is_online=False)

    # 如果是次级评论，则将replied_id为当前评论id的评论下线， 帖子额外数据，减(1+下线评论数)  将当前评论的一级评论的缓存减(1+下线评论数)
    else:
        sub_reply_id_list = TractateReplyService.get_sub_reply_ids(reply_id)
        effect_num = 0
        if sub_reply_id_list:
            effect_num = TractateReply.objects.filter(id__in=sub_reply_id_list).update(is_online=False)

        reply_num = int(TractateReplyService.top_reply_count(top_id))
        new_reply_num = reply_num - 1 - effect_num
        if new_reply_num < 0:
            new_reply_num = 0
        TractateReplyService.set_reply_count(top_id, new_reply_num)


    t_extra = TractateExtra.objects.get(tractate_id=reply.tractate_id)
    new_reply_count =  t_extra.reply_count - effect_num - 1
    if new_reply_count < 0:
        new_reply_count = 0
    t_extra.reply_count = new_reply_count
    t_extra.save(update_fields=['reply_count'])

    return "删除成功"