#!/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)
from gm_rpcd.all import (
    RPCDFaultException,
    bind,
)
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.fake_vote_tool import is_special_user, fake_vote_v1
from talos.tools.replies_tool import ReplyTool
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,
)
from talos.models.tractate.reply import TractateReply
from talos.rpc import (
    bind_context,
)
from talos.services import (
    UserService,
    get_user_from_context,
    AgileTagService,
)
from talos.tasks.tractate import (
    set_tractate_video_water_mark_url,
    set_tractate_video_clipping_to_video,
)
from talos.services.tractate import TractateReplyService
from talos.tasks.tractate import reply_push
from talos.cache.base import tractate_reply_count_cache
from utils.push import push_task_to_user_multi


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()


@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, 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))
        images = list(TractateImages.objects.filter(tractate_id=tractate_id).values_list("image_url", flat=True))
        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,
            "content": tractate_obj.content,
            "images": images,
            "tag_ids": tag_ids,
            "new_tag_ids": new_tag_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,
        }
    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', [])))

    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)
    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()

    _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))

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

    # 图片
    new_images = set(images)
    old_images = set(TractateImages.objects.filter(tractate_id=_tractate_id).values_list('image_url', flat=True))
    tractate_images_edit = bool(new_images - old_images)  # 图片是否改变

    #美购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 _image_url in sorted(new_images - old_images, key=images.index):
        _kw = {
            "tractate_id": _tractate_id,
            "image_url": _image_url,
        }
        _base_data = Picture.get_image_base_info(_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)
    TractateImages.objects.filter(tractate_id=_tractate_id, image_url__in=list(old_images - new_images)).delete()

    # 视频
    video_url = TractateVideo.cleaned_video_url(video_url)
    video_url_list = list(TractateVideo.objects.filter(
        tractate_id=_tractate_id, is_online=True).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).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_images_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):
    """hera后台回复"""

    if not reply_id:
        return "回复失败"

    try:
        tractate_reply = TractateReply.objects.get(pk=reply_id)
    except TractateReply.DoesNotExist:
        return "回复不存在"
    try:
        tractate = Tractate.objects.get(id=tractate_reply.tractate_id)
    except TractateReply.DoesNotExist:
        return "帖子不存在"

    #更新评论数
    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'])

    reply = TractateReply()

    reply.content = content
    reply.user_id = tractate.user_id
    reply.top_id = tractate_reply.top_id or tractate_reply.id
    reply.tractate_id = tractate.id

    reply.source_id = TRACTATE_PLATFORM.HERA
    reply.replied_id = tractate_reply.id

    reply.save()

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

    return "回复成功"


@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)

        # 审核完成 等级大于等于3且是第一次审核 需要点赞
        if int(content_level) >= int(TRACTATE_CONTENT_LEVEL.GENERAL) \
                and not tractate_obj.audit_time and int(status) != int(TRACTATE_STATUS.AUDIT_REJECT):
            special_user = is_special_user(user_id=tractate_obj.user_id)
            fake_vote_v1(
                task_type=VOTE_TASK_TYPE.TRACTATE, business_id=tractate_id, special_user=special_user,
                content_level=content_level
            )
        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)

        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['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 {}