# coding=utf-8

from __future__ import absolute_import
import json
import random
from datetime import timedelta, datetime
from distutils.version import LooseVersion

from django.conf import settings
from django.db.models import Q, Count

from celery import shared_task
from gm_types.antispam import DocType
from gm_types.gaia import AUTHOR_TYPE, TAG_TYPE
from gm_types.mimas import SPAM_LABEL, CONTENT_CLASS, SEND_ANSWER_STATUS, MEDIA_IMAGE_URL_SOURCE
from gm_types.push import PUSH_INFO_TYPE, AUTOMATED_PUSH
from helios.rpc import RPCFaultException
from gm_protocol import GmProtocol

from talos.services.other import get_user_lastest_device_app_version_by_user_id
from utils.protocol import gm_protocol
from utils.rpc import rpc_client, logging_exception
from utils.push import push_task_to_user_multi, limit_get_comment_push, vote_push
from qa.models import (
    Answer,
    AnswerVote,
    Question,
    AnswerReply,
    QuestionInviter,
    QuestionTag,
    SendAnswer,
    QuestionImage,
    AnswerImage,
)
from talos.models.doctor import DoctorMessageList
from talos.logger import qiniu_logger
from qa.tasks.view import view_increase_amount
from qa.utils import const_strings
from qa.cache import (
    high_quality_question_cache,
    question_inviter_cache,
    complains_question_cache,
)
from qa.libs import get_media_extra_info, _get_content_text
from talos.services import UserService, get_user_from_context
from talos.portal import get_doctor_from_user_ids  # 引用的是 talos services doctor 下的方法
from talos.tools.vote_tool import VoteTool
from talos.cache.base import vote_cache
from talos.cache.base import fake_vote_cache


@shared_task
def add_answer_view(answer_ids=[]):
    for answer_id in answer_ids:
        view_increase_amount(const_strings.ANSWER_VIEW, answer_id, 1)


@shared_task
def add_question_view(question_ids=[]):
    for question_id in question_ids:
        view_increase_amount(const_strings.QUESTION_VIEW, question_id, 1)


@shared_task
def add_questions_view(question_ids):
    for question_id in question_ids:
        if question_id['question']:
            view_increase_amount(const_strings.QUESTION_VIEW, question_id['question'], 1)
        if question_id['qa']:
            view_increase_amount(const_strings.ANSWER_VIEW, question_id['qa'], 1)


@shared_task
def fake_vote_to_answer(repeat_times, answer_id, force_push=False):
    user_ids = rpc_client['api/user/get_sleep_user'](number=repeat_times).unwrap()
    add_fake_vote_to_answer(user_ids, answer_id, force_push=force_push)


def add_fake_vote_to_answer(user_ids, answer_id, force_push=False):

    answer_fake_vote_key = "answer_fake_vote_{id}".format(id=answer_id)
    is_set = fake_vote_cache.set(answer_fake_vote_key, 1, nx=True, ex=30)
    if not is_set:
        return

    answer = Answer.objects.get(id=answer_id)

    push_url = gm_protocol.get_answer_list(answer=answer_id)
    content = _get_content_text(answer.content)
    content = (content[:25] + '...') if len(content) > 25 else content
    if not answer.is_online:
        return

    answer_add_vote_num = 0
    answer.like_num += len(user_ids)
    answer.save()
    for user_id in user_ids:
        answer_vote, created = AnswerVote.objects.get_or_create(user=user_id, answer_id=answer.id, is_fake=True)
        if created:
            # 对作者增加点赞数
            rpc_client['api/person/incr_vote'](user_id=user_id).unwrap()
            vt_v1 = VoteTool(vote_cache, answer.user_id, new_version=True)
            vt_v1.receive_answer_vote(answer_vote.id)
            answer_add_vote_num += 1

            vt_v1 = VoteTool(vote_cache, answer.user_id, new_version=True)
            vt_v1.receive_answer_vote(answer_vote.id)

        if force_push:
            try:
                user_name = UserService.get_user_by_user_id(user_id).nickname
            except:
                logging_exception()
                return

            vt_v1 = VoteTool(vote_cache, answer.user_id, new_version=True)
            vt_v1.receive_answer_vote(answer_vote.id)

            vote_push(
                user_id=answer.user_id,
                push_url=push_url,
                alert=u'{}赞了你的回复{}'.format(user_name, content),
                push_type=AUTOMATED_PUSH.ANSWER_GET_VOTE
            )
    answer.like_num += answer_add_vote_num
    answer.save()


def bayes_antispam(doc_id, doc_type, content):
    """
    is spam.
    :param doc_id: document id.
    :param doc_type: document type.
    :param content: content
    :return:
    """
    try:
        return rpc_client['antispam/classify'](doc_id=doc_id, doc_type=doc_type, content=content).unwrap()['spam']
    except RPCFaultException:
        return False


def yidun_antispam(text):
    """
    易盾反垃圾
    http://support.dun.163.com/antispam/api/#_1
    :param text:
    :return:
    """
    try:
        data = rpc_client['antispam/yidun'](text=text).unwrap()
    except RPCFaultException:
        return False
    if data['result']['action'] == 2:
        label = data['result']['labels'][0]['label']
        if label == 100:
            return SPAM_LABEL.EROTICISM
        elif label == 200:
            return SPAM_LABEL.ADVERTISE
        elif label == 400:
            return SPAM_LABEL.PROHIBIT
        elif label == 600:
            return SPAM_LABEL.ABUSE
        elif label == 700:
            return SPAM_LABEL.WATERING
        return False


@shared_task
def check_spam(pk, fields, cls_type):
    """
    check spam.
    :param pk:
    :param fields:
    :param cls:
    :return:
    """
    clsMap = {
        DocType.ANSWER: Answer,
        DocType.QUESTION: Question,
        DocType.COMMENT: AnswerReply,
    }

    cls = clsMap.get(cls_type)
    if not cls:
        return False

    try:
        instance = cls.objects.get(pk=pk)
    except cls.DoesNotExist:
        return False

    content = u""
    for field in fields:
        content += getattr(instance, field, u"")
    instance.spam_label = bayes_antispam(doc_id=pk, doc_type=cls_type, content=content)
    yidun_label = yidun_antispam(content)
    if yidun_label:
        instance.spam_label = yidun_label
    instance.save()


@shared_task
def build_high_quality_cache():
    """

    :return:
    """
    start_time = datetime.now() - timedelta(days=90)
    condition = Q(is_online=True) & Q(is_recommend=True) & Q(create_time__gt=start_time)
    total = Question.objects.filter(condition).count()
    offset = random.randint(0, total)
    ids = list(Question.objects.filter(condition).values_list('id', flat=True)[offset:offset + 100])
    random.shuffle(ids)
    high_quality_question_cache.set('high_quality', json.dumps(ids))


@shared_task
def build_inviter_pool():
    """
    邀请人列表池
    :return:
    """
    start_time = datetime.now() - timedelta(days=150)
    query = Q(is_online=True, level__gte=CONTENT_CLASS.FINE, create_time__gt=start_time)
    users = Answer.objects.filter(query).values("user", "create_time")

    user_dic = {}
    # 用于构建用来比较的数据结构 {user_id:{"id":user_id, "cnt": count, "create_time": datetime类型}}
    for user in users:
        uid = user["user"]
        if uid not in user_dic:
            user_dic[uid] = {"id": uid, "cnt": 1, "create_time": user["create_time"]}
        else:
            user_dic[uid]['cnt'] += 1
            if user["create_time"] > user_dic[uid]["create_time"]:
                user_dic[uid]['create_time'] = user["create_time"]

    user_list = list(filter(lambda x: x["cnt"] > settings.INVITER_LIMIT_FORM_ANSWER, user_dic.values()))
    user_list.sort(key=lambda user: (user["cnt"], user["create_time"]), reverse=True)
    user_ids = [user["id"] for user in user_list]  # 筛选完毕，满足需求的所有user_id
    #  剔除 医生 or 医院用户
    doctors = get_doctor_from_user_ids(user_ids)
    doctor_user_ids = [int(doctor.user_id) for doctor in doctors]
    user_ids = list(filter(lambda y: y not in doctor_user_ids, user_ids))

    #  通过请求 gaia 的方法，过滤掉马甲账号,需要先做一次预处理，user_is_puppet_dic:{"user_id": "is_puppet"}
    user_infos = rpc_client["api/user/get_fundamental_info_by_user_ids"](user_ids=user_ids).unwrap()
    user_is_puppet_dic = {user["id"]: user["is_puppet"] for user in user_infos}
    user_ids = list(filter(lambda user_id: not user_is_puppet_dic[user_id], user_ids))

    question_inviter_cache.set("question_inviter_pool", json.dumps(user_ids))


@shared_task
def time_push_to_invited_user():
    """
    定时推送给被邀请者
    :return:
    """
    #  数据池 每天下午五点到上一天下午五点，被邀请的用户未回答的问题。
    today = datetime.now().replace(hour=17, minute=0, second=0)
    last_day = today - timedelta(days=1)
    need_push_info = QuestionInviter.objects.filter(
        answer_id__isnull=True, invite_time__range=[last_day, today]).values("inviter").annotate(nums=Count("inviter"))

    for info in need_push_info:
        push_url = GmProtocol().get_webview(settings.BACKEND_API_HOST + settings.INVITE_QUESTION_LIST_PAGE)
        kwargs = {
            "user_ids": [info["inviter"]],
            "platform": ['android', 'iPhone'],
            "alert": u'{count}位用户邀请你来回答医美问题，赶紧去为他们解答哦~'.format(count=info["nums"]),
            "extra": {
                'type': PUSH_INFO_TYPE.GM_PROTOCOL,
                'msgType': 4,
                'pushUrl': push_url,
                'push_url': push_url,
            },
            'push_type': AUTOMATED_PUSH.INVITATION_TO_ANSWER,
            # "labels": {},  # 不知道是否需要埋点，先预留
        }
        push_task_to_user_multi(**kwargs)


@shared_task
def get_relation_college_tag_questions():
    """
    获取关联大学标签的所有问题id及，后台设置的小程序推荐问题id
    :return:
    """
    tag_info = rpc_client["api/tag/hera_search"](query="", tag_type_in=[TAG_TYPE.COLLEGE, ], is_online=True).unwrap()
    tag_ids = [int(tag.get("id")) for tag in tag_info]
    question_ids = QuestionTag.objects.filter(tag__in=tag_ids).values_list("question_id", flat=True).distinct()
    recommends = Question.objects.filter(id__in=question_ids, is_online=True, recommend_xiaochengxu=True).values_list(
        "id", flat=True)
    complains_question_cache.set("tag_ids", json.dumps(tag_ids))
    complains_question_cache.set("recommend", json.dumps(list(recommends)))


def push_to_question_provider(user_id, answer_id, eta=None, question_title=None, user_name=None):
    """
    push 给提出问题的用户
    :param user_id: 用户ID
    :param question_id: 问题ID
    :param eta:
    :param question_title:
    :param user_name:
    :return:
    """
    push_url = gm_protocol.get_answer_list(answer=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=[user_id], push_type=AUTOMATED_PUSH.QUESTION_POSTED_ANSWER,
        extra=extra,
        labels={'event_type': 'push', 'event': 'question_received_answer'},
        alert=u'{user_name} 刚刚回答了你的问题＂{question_title}＂'.format(
            user_name=user_name, question_title=question_title),
        eta=eta,
    )


@shared_task
def send_answer_on_time(sa_id, question_user_id, question_title, nick_name, push_time):
    send_answer = SendAnswer.objects.get(id=sa_id)
    if send_answer.status != SEND_ANSWER_STATUS.SUCCESS:
        Answer.objects.update_or_create(**send_answer.data)
        send_answer.status = SEND_ANSWER_STATUS.SUCCESS
        send_answer.save()
        push_to_question_provider(user_id=question_user_id,
                                  eta=push_time,
                                  question_title=question_title,
                                  user_name=nick_name)


@shared_task
def comment_push(answer_id, comment_id=None, user_id=None, author_name=None, content=None, is_commented_reply=None):
    if not user_id:
        return {}
    user_ids = [user_id]
    # 7715 评论都跳转到通知页面  产品: 连鑫
    # 7745 评论跳到 评论页
    content = (content[:25] + '...') if len(content) > 25 else content
    if not is_commented_reply:
        push_url = gm_protocol.get_answer_list(answer_id, comment_id, is_push=1)
    else:
        push_url = gm_protocol.get_answer_reply_list(
            answer=answer_id,
            comment_id=comment_id or ''
        )

    extra = {
        'type': PUSH_INFO_TYPE.GM_PROTOCOL,
        'msgType': 4,
        'push_url': push_url,
        'pushUrl': push_url,
    }
    alert = u'{author_name}评论了你：{content}'.format(author_name=author_name, content=content)
    push_type = AUTOMATED_PUSH.COMMENT_RECEIVED_REPLY if is_commented_reply \
        else AUTOMATED_PUSH.ANSWER_RECEIVED_COMMENTS
    labels = {'event_type': 'push', 'event': 'received_qa_comments'}
    push_task_to_user_multi(
        user_ids=user_ids,
        alert=alert,
        extra=extra,
        labels=labels,
        push_type=push_type
    )


@shared_task
def add_doctor_question(question_id):
    """添加医生关联的问题"""

    try:
        question = Question.objects.get(id=question_id, is_online=True)
    except Question.DoesNotExist:
        return

    if question.answer_num:
        return

    user = question.user
    if not user.current_city_id:
        return

    try:
        doctors = rpc_client["doctor/getdoctorinfo_bycityid"](city_id=user.current_city_id).unwrap()
        doctor_ids = [doctor.get("doctor_id") for doctor in doctors]
    except:
        logging_exception()
        return

    messages = []
    for doctor_id in doctor_ids:
        messages.append(DoctorMessageList(
            user_id=user.id,
            doctor_id=doctor_id,
            question=question
        ))
    DoctorMessageList.objects.bulk_create(messages)


@shared_task
def qa_image_add_base_info(images_list, params_info):
    """
    更新问答图片的基础信息
    :param images_list: []   不带后缀的完整图片地址链接
    :param params_info:  {"model": answer_image, "param_name": "answer_id", "id": 111}
    :return:
    """

    mapping_dic = {
        "question_image": (QuestionImage, "question_id"),
        "answer_image": (AnswerImage, "answer_id"),
    }

    model, param_name = mapping_dic.get(params_info.get("model", ""), (None, ""))
    _id = params_info.get("id", 0)

    if model:
        _image_list = get_media_extra_info(images_list)
        for image_dic in _image_list:
            if param_name:
                _kw = {
                    "image_url_source": MEDIA_IMAGE_URL_SOURCE.RICH_TEXT,
                    "image_url": image_dic.get("image_url", ""),
                    param_name: _id,
                }

                _default = {
                    "width": image_dic.get("width", 0),
                    "height": image_dic.get("height", 0),
                }

                model.objects.get_or_create(**_kw, defaults=_default)
            else:
                continue

