# -*- coding: utf8 -*-

from __future__ import unicode_literals, absolute_import, print_function

import datetime
import re

from django.conf import settings
from django.db.models import Q
from django.utils.html import escape
from gm_types.gaia import (
    FILTER_WORD_TYPE,
    DIARY_OPERATION,
)
from gm_types.user_hierarchy import EventType
from gm_rpcd.all import bind
from gm_types.error import ERROR as CODES
from talos.rpc import bind_context
from utils.rpc import gen, rpc_client, logging_exception, get_current_user
from talos.decorators import list_interface
from talos.cache.takesofacache import take_sofa_cache
from talos.libs.datetime_utils import get_timestamp_or_none

from talos.libs.diaryrank_utils import DiaryRankTool
from talos.libs.reply import is_reply_num_gt_limit

from talos.manager.diary import diary_list_manager
from talos.manager.topic_reply import comment_data_by_reply_ids
from talos.manager.topic_reply import get_reply_details_by_reply_ids
from talos.models.diary.diary import Diary
from talos.models.topic import ReplyHeadline
from talos.models.topic import TopicReply
from talos.services import AntiSpamService
from talos.services import UserService, get_user_from_context

from talos.tools.filterword_tool import filterword_by_custom
from talos.tasks import push_diary_reply_message, applet_diary_replied_push, applet_diary_reply_summary_push
from user_hierarchy.portal import process_task


@bind('diary/reply_create')
def create_diary_reply(diary_id, content, reply_id=None, device_id=None):
    """
    NOTE:
        DESC: 用户对日记本进行评论和回复

        :param ctx:
        :param diary_id:
        :param reply_id:
        :param content:
        :return:
    """
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    result = {
        'comment_data': {},
        'reply_data': {},
        'diary': {},
        'doctor': {},  # if user is a doctor, add doctor.id
    }

    if not diary_id:
        return gen(CODES.DIARY_NOT_FOUND)

    # content not null
    if not content.strip():
        return gen(CODES.REPLY_CAN_NOT_BE_EMPTY)

    replied_reply = None
    if reply_id:
        replied_reply = TopicReply.objects.filter(id=reply_id)
        if replied_reply:
            replied_reply = replied_reply[0]
        else:
            replied_reply = None

    if replied_reply and replied_reply.diary:
        diary = replied_reply.diary

    else:
        try:
            diary = Diary.objects.get(id=diary_id)
        except Diary.DoesNotExist:
            return gen(CODES.DIARY_NOT_FOUND)

    result['tags'] = diary.tags_new_era
    result['diary']['id'] = diary_id

    doctor_id = UserService.get_doctor_id_from_user_id(user.id)
    if doctor_id:
        result['doctor']['id'] = doctor_id

    # 检查用户在一分钟内回复(日记本/帖子)是否大于limit条
    if is_reply_num_gt_limit(user=user):
        return gen(CODES.COMMENT_BUSY)

    # 检查日记贴评论敏感词,以前的代码进行了数字的过滤,本次沿用
    content = content if content else ""
    filterword_by_custom(filter_type=FILTER_WORD_TYPE.TOPIC_REPLY, content=content)

    # 查看最近用户有没有回复内容一样的留言
    try:
        reply = TopicReply.objects.filter(diary_id=diary.id, user_id=user.id)
        last_id = reply.latest('id')
        previous_reply = TopicReply.objects.get(id=last_id.id)
        if previous_reply.content == content:
            return gen(CODES.REPLY_CAN_NOT_BE_REPEATED)

    except TopicReply.DoesNotExist:
        pass

    if diary.user_id == user.id:
        if reply_id:
            d = DiaryRankTool(diary.id)
            t = datetime.datetime.now()
            replied_user_id = replied_reply.user.id
            d.add_heat(DIARY_OPERATION.SELF_REPLY, t, extra_id=replied_user_id)

    elif device_id:
        d = DiaryRankTool(diary.id)
        t = datetime.datetime.now()
        d.add_heat(DIARY_OPERATION.OTHER_REPLY, t, extra_id=device_id)

    diary.reply_num += 1

    commented_reply = (
        replied_reply.commented_reply if replied_reply and
                                         replied_reply.commented_reply else
        replied_reply
    )

    topic_reply = TopicReply()
    topic_reply.diary = diary
    topic_reply.user_id = user.id
    topic_reply.doctor_id = doctor_id
    topic_reply.content = content
    topic_reply.replied_topic = replied_reply
    topic_reply.commented_reply = commented_reply
    topic_reply.save()
    diary.save()

    # 发送小程序推送
    # applet_diary_replied_push(topic_reply.id)
    # applet_diary_reply_summary_push(topic_reply.id)

    # 给客户端返回最近回复的信息
    try:
        reply_data = topic_reply.reply_data_after_create()
    except:
        reply_data = {}
        logging_exception()

    try:
        comment_data = topic_reply.comment_data_after_create()
    except:
        logging_exception()
        comment_data = {}

    # push 异步消息通知
    # replied_reply 被回复的TopicReply对象
    diary_user_id = diary.user_id if diary else None
    replied_reply_user_id = replied_reply.user_id if replied_reply else None
    push_diary_reply_message(user_id=user.id,
                             diary_user_id=diary_user_id,
                             reply_id=reply_id,
                             content=content,
                             replied_reply_user_id=replied_reply_user_id)

    take_sofa_cache.del_diary_cache_data(diary_id)

    # add point,growth
    growth_value, point_value, submit_count = 0, 0, 0
    if not replied_reply:
        event_data = process_task(user_id=user.id, event_type=EventType.COMMENT_OTHER, related_item=topic_reply.id)
        growth_value, point_value, submit_count = event_data['growth_value'], event_data['point_value'], event_data[
            'submit_count']

    result['reply_data'] = reply_data
    result['comment_data'] = comment_data
    result['point_value'] = point_value
    result['growth_value'] = growth_value
    result['submit_count'] = submit_count
    return result


def get_diary_replies_info(user, replies, start_num=0, count=10):
    return TopicReply.get_replies_info(user, replies, start_num, count)


@bind_context('diary/replies')
@list_interface(offset_name='start_num', limit_name='count')
def get_diary_replies(ctx, diary_id, start_num=0, count=10):
    """
    NOTE:
        DESC: 获取日记本的评论列表
        :param ctx:
        :param diary_id:
        :param start_num:
        :param count:
        :return:

        ```
        排序的规则: 先热门按照点赞数排序(热门定义为 点赞数大于5)
                     然后非热门的按照回复时间排序
        重点说明:
            其中start_num计算的地方

            假设某一个diary 中热门评论15条,普通评论40条
            1 取第一分页,则直接可以从热门评论中直接取到(start_num=0, count=10)
            2 取第二分页(start_num=11, count=10), 返回的数据中既有热门, 也有非热门
              所以需要进行start_num的重新点位,
              start_num = 0 if (start_num < hot_reply_num + count) else (start_num - hot_reply_num)
            3 取第三页 start_num = start_num - hot_reply_num
        ```
    """
    user = get_user_from_context(ctx)

    if not diary_id:
        return gen(CODES.DIARY_NOT_FOUND)

    try:
        diary = Diary.objects.get(id=diary_id)

        if not diary.is_online:
            return gen(CODES.DIARY_NOT_FOUND)

    except Diary.DoesNotExist:
        return gen(CODES.DIARY_NOT_FOUND)

    query = Q(diary_id=diary_id)
    query &= Q(commented_reply__isnull=True)
    query &= Q(is_online=True)

    result = TopicReply.get_replies_list_info(user, query, start_num=start_num, count=count)
    return result


@bind_context('diary/top_replies')
@list_interface(offset_name='start_num', limit_name='count')
def get_elite_reply_list(ctx, diary_id, start_num=0, count=3):
    """
    get elite replies or recently created replies.
    changelog: 20160603
    老逻辑:有热门评论 按照赞数倒序返回热门评论(不管几条) 没有热门评论 按照时间倒序返回非热门(3条)
    新逻辑:总共返回四条评论 如果有热门评论 按照时间倒序返回热门评论 不足4条 按照时间倒序返回非热门评论
    """
    user = get_user_from_context(ctx)

    if not diary_id:
        return gen(CODES.DIARY_NOT_FOUND)

    try:
        diary = Diary.objects.get(id=diary_id)
    except Diary.DoesNotExist:
        return gen(CODES.DIARY_NOT_FOUND)

    if diary.is_online:
        hot_query = Q(diary=diary)
        hot_query &= Q(commented_reply__isnull=True)
        hot_query &= Q(is_online=True)
        hot_query &= (Q(replyheadline__is_online=True) | Q(like_num__gte=settings.ELITE_LIKE_NUM))

        # 新逻辑
        def build_comments(replies):
            local_comments = get_reply_details_by_reply_ids(replies, user.id)
            return local_comments

        comments = []

        elite_replies = TopicReply.objects.filter(hot_query).values_list('id', flat=True).order_by(
            '-replyheadline__is_online', '-like_num', '-reply_date'
        )
        if elite_replies:
            comments.extend(build_comments(list(elite_replies)[:4]))
        is_elite = 1 if elite_replies else 0

        if elite_replies.count() < count:
            query = Q(diary=diary)
            query &= Q(commented_reply__isnull=True)
            query &= Q(is_online=True)
            query &= Q(like_num__lt=settings.ELITE_LIKE_NUM)

            replies = TopicReply.objects.filter(query).values_list('id', flat=True).order_by('-id').exclude(hot_query)
            comments.extend(build_comments(list(replies)[:count - elite_replies.count()]))

        total_count = TopicReply.objects.filter(diary=diary, is_online=True).count()

        result = {
            'comments': comments,
            'is_elite': is_elite,
            'total_count': total_count
        }
        return result

    return gen(CODES.DIARY_NOT_FOUND)


@bind('diary/comment_info')
def get_comment_info(id):
    """get reply detail.

    args:
        id: topicreply.id

    NOTE:
        获取评论的详细信息
    """
    try:
        topicreply = TopicReply.objects.get(pk=id)
    except TopicReply.DoesNotExist:
        return gen(CODES.TOPIC_REPLY_NOT_FOUND)

    comment_info = {}
    comment_info['content'] = escape(topicreply.content)
    comment_info['comment_time'] = get_timestamp_or_none(
        topicreply.reply_date
    )

    user = UserService.get_user_by_user_id(topicreply.user_id)
    comment_info['user_name'] = user.nickname
    comment_info['user_portrait'] = user.portrait
    comment_info['user_id'] = topicreply.user_id
    comment_info['membership_level'] = user.membership_level
    comment_info['comment_id'] = topicreply.id
    comment_info['is_hot'] = topicreply.is_elite()
    comment_info['is_recommend'] = ReplyHeadline.is_recommend(topicreply.id)

    user_bz_info = UserService.get_doctor_hospital_id_by_user_id(topicreply.user_id)
    if user_bz_info:
        comment_info.update(user_bz_info)

    return comment_info


# @bind_context('talos/diary/reply')
@bind_context('diary/get_reply')
def get_diary_reply_by_id(ctx, reply_id):
    """

    :param ctx:
    :param reply_id:
    :return:
    """
    user = get_user_from_context(ctx)

    try:
        reply = TopicReply.objects.get(pk=reply_id)
    except TopicReply.DoesNotExist:
        return gen(CODES.TOPIC_REPLY_NOT_FOUND)

    replies_data = reply.get_reply_detail(user.id)
    reply_info = replies_data['reply']
    if reply.replyheadline_set.count() == 0:
        replies_data['reply']['is_recommand'] = False
    else:
        replies_data['reply']['is_recommand'] = True

    replies_data['reply']['is_elite'] = (1 if reply_info['favor_amount'] >= settings.ELITE_LIKE_NUM else 0)
    comments = reply.comments.filter(is_online=True)
    comments_ids = list(comments.values_list('id', flat=True))
    comments_data = comment_data_by_reply_ids(comments_ids)
    replies_data[u'comments'] = comments_data
    return replies_data


@bind('diary/diary_smart_index')
def get_diary_smart_index(id, comment_id):
    """
    NOTE:
        定位评论在那个页面, 定位锚点
    :param id: diary id
    :param comment_id: topicreply id
    :return:
    """
    try:
        diary = Diary.objects.get(id=id)
    except Diary.DoesNotExist:
        return gen(CODES.DIARY_NOT_FOUND)

    try:
        specific_reply = TopicReply.objects.get(pk=comment_id, diary=diary)

    except TopicReply.DoesNotExist:
        return gen(CODES.TOPIC_REPLY_NOT_FOUND)

    except ValueError:
        # http://sentry.gengmei.cc/sentry/prod-gaia/issues/592/
        return gen(CODES.UNKNOWN_ERROR)

    query = Q(commented_reply__isnull=True)
    query &= Q(is_online=True)
    query &= Q(diary=diary)

    if specific_reply.like_num >= settings.ELITE_LIKE_NUM:
        query &= Q(like_num__gte=specific_reply.like_num)
        replies = TopicReply.objects.filter(query)

        query = Q(reply_date__gte=specific_reply.reply_date)
        query |= Q(like_num__gte=specific_reply.like_num)
        count = replies.filter(query).count()
    else:
        query &= (Q(like_num__gte=settings.ELITE_LIKE_NUM) | Q(reply_date__gte=specific_reply.reply_date))
        count = TopicReply.objects.filter(query).count()

    return count