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

import os
import time
import ffmpy
import hashlib
import json
import requests
import random
import datetime

from django.conf import settings
from django.db.models import F
from celery import shared_task
# from celery_once import QueueOnce
from urllib.parse import urljoin

from gm_types.push import PUSH_INFO_TYPE, AUTOMATED_PUSH
from gm_types.mimas import TRACTATE_PLATFORM, TRACTATE_DATA_TYPE, TRACTATE_CONTENT_LEVEL
from gm_types.gaia import (
    VIDEO_CODE_STATUS,
    QINIU_VIDEO_HANDLE_STATUS_CODE,
    LIST_TRACTATE_FROM,
)

from gm_upload import (
    set_video_watermark,
    fetch_picture_and_save_to_qiniu,
    video_clipping,
    upload_file,
    video_delete,
)
from gm_upload.utils.qiniu_tool import QiniuTool
from gm_upload.utils.image_utils import Picture

from talos.libs.image_utils import fetch_picture_and_save_to_qiniu_v2
from talos.logger import info_logger
from talos.models.soft_article.soft_article import SoftArticleVideo
from talos.models.soft_article.reply import SoftArticleReply
from talos.models.tractate import TractateVideo, TractateExtra, TractateVote, Tractate, TractateReply
from talos.rpc import logging_exception
from utils.common import get_new_video_name
from utils.push import push_task_to_user_multi, vote_push
from utils.protocol import gm_protocol

from talos.cache.base import tractate_pv_cache, tractate_vote_count_cache, vote_cache
from talos.services import UserService
from talos.tasks import get_sleep_user
from talos.tools.vote_tool import VoteTool
from talos.cache.base import fake_vote_cache


def fake_view_num(start, end):
    """
    帖子的浏览量灌水
    :param start:
    :param end:
    :return:
    """
    return random.randint(start, end)


def upload_new_image_and_get_base_info(video_url):

    result = {
        "video_cover_url": "",
        "width": 0,
        "height": 0,
    }

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

    try:
        new_video_caver_url = fetch_picture_and_save_to_qiniu_v2(_video_cover_url)
        _base_data = Picture.get_image_base_info(new_video_caver_url)
        if not all(_base_data.values()):
            _base_data = {
                "width": 0,
                "height": 0,
            }

        result["video_cover_url"] = new_video_caver_url
        result.update(_base_data)

        return result
    except:  # 上传失败则返回默认值
        return result


@shared_task
def set_tractate_video_water_mark_url(video_id):
    video = TractateVideo.objects.get(id=video_id)

    raw_video_url = video.raw_video_url
    pid = set_video_watermark(
        raw_video_url,
        get_new_video_name(raw_video_url),
        water_mark_url=settings.WATER_MARK_URL_FOR_VIDEO
    )

    video.persistent_id = pid
    video.persistent_status = VIDEO_CODE_STATUS.WAITING
    video.save(update_fields=["persistent_id", "persistent_status"])


@shared_task
def check_tractate_video_water_mark_url_is_finish():
    videos = TractateVideo.objects.filter(persistent_status=VIDEO_CODE_STATUS.WAITING)
    for video in videos:
        _pid = video.persistent_id
        try:
            if _pid:
                result = json.loads(requests.get('http://api.qiniu.com/status/get/prefop?id=' + _pid).text)
                # 状态码0成功，1等待处理，2正在处理，3处理失败，4通知提交失败。
                code = result['code']
                if code != QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                    continue
                else:
                    if result['items'][0]['code'] == QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                        water_video_url = result['items'][0]['key']
                        # 刷新cdn缓存
                        QiniuTool.refresh_qiniu_resource_cache([urljoin(settings.VIDEO_HOST, water_video_url)])
                        # 上传一下有水印的视频第一帧图片
                        others = upload_new_image_and_get_base_info(water_video_url)
                        if others.get("video_cover_url", ""):
                            for k, v in others.items():
                                setattr(video, k, v)

                        video.water_video_url = water_video_url
                        video.persistent_status = VIDEO_CODE_STATUS.SUCCESS
                        video.save()
                    else:
                        continue
        except:
            logging_exception()


@shared_task
def tractate_fake_vote(
        repeat_times, tractate_id, need_view_increase=True, incr_range=[1, 1], alert='', force_push=False
):
    """
    异步增加虚拟点赞量、浏览量
    :param repeat_times:
    :param tractate_id:
    :param incr_range:
    :param need_view_increase:
    :param alert:
    :return:
    """
    now_date = datetime.datetime.now()
    if not 9 <= now_date.hour < 22:  # 超过晚十点灌水任务不执行
        return
    tractate_fake_vote_key = "tractate_fake_vote_{id}".format(id=tractate_id)
    is_set = fake_vote_cache.set(tractate_fake_vote_key, 1, nx=True, ex=30)
    if not is_set:
        return

    tractate = Tractate.objects.get(id=tractate_id)
    if tractate.user_del or not tractate.is_online or tractate.content_level == TRACTATE_CONTENT_LEVEL.BAD:
        info_logger.info('帖子:{}已下线, 不执行点赞灌水.'.format(tractate.id))
        return

    users = get_sleep_user(repeat_times)
    cnt = 0
    for u in users:
        u_nick_name = ''
        tractate_vote, created = TractateVote.objects.get_or_create(user_id=u, tractate_id=tractate.id, is_fake=True)
        if created:
            # 对作者增加点赞数
            author = UserService.get_user_by_user_id(tractate.user_id)
            author.incr_vote_count()
            cnt += 1

        if not u_nick_name:
            vote_user = UserService.get_user_by_user_id(u)
            u_nick_name = vote_user.nickname or u'更美用户'

        vote_tool = VoteTool(redis_c=vote_cache, user_id=tractate.user_id, new_version=True)
        vote_tool.receive_tractate_vote(tractate_vote.id)

        # 点赞推送
        push_url = gm_protocol.get_tractate_detail(
            tractate_id=tractate.id,
            tractate_detail_from=LIST_TRACTATE_FROM.NOTICE_VOTE
        )

        if not alert:
            alert = u'{user_name}赞了你的帖子{content}'.format(
                user_name=str(u_nick_name), content=tractate.content[:10])

        if force_push:
            vote_push(user_id=tractate.user_id, alert=alert, push_type=AUTOMATED_PUSH.TRACTATE_GET_VOTED, push_url=push_url)

    if not need_view_increase:
        return

    # 增加可视点赞数
    te, _ = TractateExtra.objects.get_or_create(tractate_id=tractate.id)
    te.favor_count = F('vote_count') + cnt
    te.save(update_fields=['vote_count'])
    tractate_vote_count_cache.incrby(str(tractate.id), cnt)

    # 每个点赞增加随机浏览量
    view_num = 0
    for i in range(cnt):
        view_num += random.randint(*incr_range)

    # 增加可视浏览数
    tractate_pv_cache.incrby(str(tractate.id), view_num)


@shared_task
def reply_push(user_id, tractate_id, reply_id):
    """

    :param user_id: 回复人的user_id
    :param tractate_id:
    :param reply_id: 发出的那条回复ID
    :return:
    """

    if not tractate_id and not reply_id:
        return

    push_msg, push_user_id, push_type = "", None, ""
    try:
        reply_info = TractateReply.objects.get(id=reply_id)
    except TractateReply.DoesNotExist:
        return
    if reply_info.replied_id:
        try:
            replied_info = TractateReply.objects.get(id=reply_info.replied_id)
        except TractateReply.DoesNotExist:
            return
        user = UserService.get_user_by_user_id(user_id=reply_info.user_id)
        push_user_id = replied_info.user_id
        # content = reply_info.content if len(reply_info.content) >= 10 else reply_info.content[:10]
        push_msg = "@{user_name}对于你的评论发表了新的回应，快来点击看看~".format(user_name=user.nickname)
        push_type = AUTOMATED_PUSH.TRACTATE_REPLY_GET_REPLY

    else:
        try:
            tractate = Tractate.objects.get(id=tractate_id)
        except Tractate.DoesNotExist:
            return
        push_user_id = tractate.user_id
        user = UserService.get_user_by_user_id(user_id=user_id)
        content = tractate.content if len(tractate.content) <= 30 else tractate.content[:30] + "..."
        content = content.replace("\n", "")
        push_msg = "@{user_name}评论了你的帖子：{content}，快来点击看看~".format(user_name=user.nickname, content=content)
        push_type = AUTOMATED_PUSH.TRACTATE_GET_REPLY

    push_url = gm_protocol.get_tractate_detail(
        comment_id=reply_id,
        tractate_id=tractate_id,
        tractate_detail_from=LIST_TRACTATE_FROM.NOTICE_REPLY
    ) if reply_id else gm_protocol.get_tractate_detail(tractate_id=tractate_id)

    extra = {
        'type': PUSH_INFO_TYPE.GM_PROTOCOL,
        'msgType': 4,
        'pushUrl': push_url,
        'push_url': push_url,
    }

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


@shared_task
def set_softarticle_video_water_mark_url(video_id):
    # 医生后台帖子封面图修改
    video = SoftArticleVideo.objects.get(id=video_id)

    raw_video_url = video.raw_video_url
    pid = set_video_watermark(
        raw_video_url,
        get_new_video_name(raw_video_url),
        water_mark_url=settings.WATER_MARK_URL_FOR_VIDEO
    )

    video.persistent_id = pid
    video.persistent_status = VIDEO_CODE_STATUS.WAITING
    video.save(update_fields=["persistent_id", "persistent_status"])


@shared_task
def check_soft_article_video_water_mark_url_is_finish():
    videos = SoftArticleVideo.objects.filter(persistent_status=VIDEO_CODE_STATUS.WAITING)
    for video in videos:
        _pid = video.persistent_id
        try:
            if _pid:
                result = json.loads(requests.get('http://api.qiniu.com/status/get/prefop?id=' + _pid).text)
                # 状态码0成功，1等待处理，2正在处理，3处理失败，4通知提交失败。
                code = result['code']
                if code != QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                    continue
                else:
                    if result['items'][0]['code'] == QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                        water_video_url = result['items'][0]['key']
                        # 刷新cdn缓存
                        QiniuTool.refresh_qiniu_resource_cache([urljoin(settings.VIDEO_HOST, water_video_url)])
                        # 上传一下有水印的视频第一帧图片
                        others = upload_new_image_and_get_base_info(water_video_url)
                        if others.get("video_cover_url", ""):
                            for k, v in others.items():
                                setattr(video, k, v)

                        video.water_video_url = water_video_url
                        video.persistent_status = VIDEO_CODE_STATUS.SUCCESS
                        video.save()
                    else:
                        continue
        except:
            logging_exception()


@shared_task
def doctor_reply_push(soft_article_id, reply_id):
    """

    :param soft_article_id:
    :param reply_id: 发出的那条回复ID
    :return:
    """

    if not soft_article_id and not reply_id:
        return

    try:
        reply_info = SoftArticleReply.objects.get(id=reply_id)
    except SoftArticleReply.DoesNotExist:
        return
    if reply_info.replied_id:
        try:
            replied_info = SoftArticleReply.objects.get(id=reply_info.replied_id, source_id=TRACTATE_PLATFORM.GM)
        except SoftArticleReply.DoesNotExist:
            return
        user = UserService.get_user_by_user_id(user_id=reply_info.user_id)
        if not user:
            return
        push_user_id = replied_info.user_id
        content = reply_info.content if len(reply_info.content) >= 10 else reply_info.content[:10]
        push_msg = "{user_name}回复了你的回复{content}".format(user_name=user.nickname, content=content)
        push_type = AUTOMATED_PUSH.TRACTATE_REPLY_GET_REPLY
    else:
        return

    push_url = gm_protocol.get_tractate_detail(
        comment_id=reply_id,
        tractate_id=replied_info.softarticle_id,
        data_type=TRACTATE_DATA_TYPE.DOCTOR,
        tractate_detail_from=LIST_TRACTATE_FROM.NOTICE_REPLY
    ) if reply_id else gm_protocol.get_comment_detail(tractate_id=replied_info.softarticle_id)

    extra = {
        'type': PUSH_INFO_TYPE.GM_PROTOCOL,
        'msgType': 4,
        'pushUrl': push_url,
        'push_url': push_url,
    }

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


@shared_task
def tractate_video_fsm_runner():
    """
    状态机
    考虑封装成类，根据状态给对应worker添加任务
    :return:
    """

    # 截取视频前两秒
    clipping_id_list = list(TractateVideo.objects.filter(
        persistent_clip_status=VIDEO_CODE_STATUS.NOSTART
    ).values_list("id", flat=True))
    for video_id in clipping_id_list:
        set_tractate_video_clipping_to_video.delay(video_id)

    # 检查七牛云截取状态
    check_id_list = list(TractateVideo.objects.filter(
        persistent_clip_status=VIDEO_CODE_STATUS.WAITING
    ).values_list("id", flat=True))
    for video_id in check_id_list:
        check_tractate_video_clipping_is_finish.delay(video_id)

    # 将七牛云截取成功的 mp4 转换为 webP 动图并上传
    set_id_list = list(TractateVideo.objects.filter(
        persistent_clip_status=VIDEO_CODE_STATUS.OPERATING_LOCAL
    ).values_list("id", flat=True))
    for video_id in set_id_list:
        set_tractate_video_webp_pic.delay(video_id)

@shared_task
def set_tractate_video_clipping_to_video(video_id):
    video = TractateVideo.objects.get(id=video_id)
    if video.persistent_clip_status != VIDEO_CODE_STATUS.NOSTART:
        return

    hash_key = hashlib.md5(str(time.time()).encode("utf8")).hexdigest()
    pid = video_clipping(
        video.raw_video_url,
        new_filename='{}_clip.mp4'.format(hash_key),
        video_type='mp4',
        water_mark_url=None
        # water_mark_url=settings.WATER_MARK_URL_FOR_VIDEO
    )

    video.persistent_clip_status = VIDEO_CODE_STATUS.WAITING
    video.persistent_clip_id = pid
    video.save(update_fields=['persistent_clip_id', 'persistent_clip_status'])


@shared_task
def check_tractate_video_clipping_is_finish(video_id):
    try:
        video = TractateVideo.objects.get(id=video_id)
        if video.persistent_clip_status != VIDEO_CODE_STATUS.WAITING:
            return

        if video.persistent_clip_id:
            result = json.loads(requests.get('{}{}'.format(
                settings.QINIU_VIDEO_INQUIRE_HOST,
                video.persistent_clip_id
            )).text)
            # 状态码0成功，1等待处理，2正在处理，3处理失败，4通知提交失败。
            code = result['code']
            if code not in (
                    QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS,
                    QINIU_VIDEO_HANDLE_STATUS_CODE.PROCESSING_FAIL
            ):
                return
            elif code == QINIU_VIDEO_HANDLE_STATUS_CODE.PROCESSING_FAIL:
                video.persistent_clip_status = VIDEO_CODE_STATUS.FAIL
                video.save(update_fields=['persistent_clip_status'])
            else:
                if result['items'][0]['code'] == QINIU_VIDEO_HANDLE_STATUS_CODE.SUCCESS:
                    mp4_key = result['items'][0]['key']

                    # 刷新cdn缓存
                    QiniuTool.refresh_qiniu_resource_cache([urljoin(settings.VIDEO_HOST, mp4_key)])

                    video.intercept_video_url = mp4_key
                    video.persistent_clip_status = VIDEO_CODE_STATUS.OPERATING_LOCAL
                    video.save(update_fields=['persistent_clip_status', 'intercept_video_url'])
                    # set_tractate_video_webp_pic(video.id)
                else:
                    video.persistent_status = result['items'][0]['code']
                    video.save(update_fields=['persistent_clip_status'])

    except:
        logging_exception()


@shared_task
def set_tractate_video_webp_pic(video_id):
    video = TractateVideo.objects.get(id=video_id)
    if video.persistent_clip_status != VIDEO_CODE_STATUS.OPERATING_LOCAL:
        return
    mp4_key = video.intercept_video_url

    hash_key = hashlib.md5(str(time.time()).encode("utf8")).hexdigest()
    input_path = os.path.join(settings.VIDEO_CONVERT_PATH, "{}.mp4".format(hash_key))
    output_path = os.path.join(settings.VIDEO_CONVERT_PATH, '{}.webp'.format(hash_key))

    try:
        clipping_video_url = settings.VIDEO_HOST + mp4_key
        res = requests.get(clipping_video_url)

        # 文件存储到本地
        with open(input_path, 'wb') as f:
            f.write(res.content)

        # 使用底层 FFmpeg 库进行转码
        ff = ffmpy.FFmpeg(
            inputs={input_path: None},
            outputs={output_path: settings.VIDEO_FF_ARGS}
        )
        ff.run()

        # 上传webP图片
        video.webp_url = upload_file(output_path)
        video.persistent_clip_status = VIDEO_CODE_STATUS.SUCCESS
        video.save(update_fields=['webp_url', 'persistent_clip_status'])

        # 删除中间数据　
        video_delete(mp4_key)

        # 刷新缓存
        QiniuTool.refresh_qiniu_resource_cache(
            [urljoin(settings.VIDEO_HOST, mp4_key)]
        )

    except:
        logging_exception()
        video.persistent_clip_status = VIDEO_CODE_STATUS.FAIL
        video.save(update_fields=['persistent_clip_status'])

    finally:
        if os.path.exists(input_path):
            os.remove(input_path)
        if os.path.exists(output_path):
            os.remove(output_path)
