# coding=utf-8

from __future__ import unicode_literals, absolute_import, print_function

import datetime
import time
import random
from django.db import IntegrityError
from django.utils.html import escape
from django.conf import settings

from gm_types.error import ERROR as CODES
from gm_types.gaia import (
    DIARY_OPERATION,
    CONST_STRINGS,
    VOTEOBJECT,
    NotificationType,
    TOPIC_TYPE,
)
from gm_types.mimas import NOTICATION_LIST_CATEGORY
from gm_types.user_hierarchy import EventType
from gm_types.push import AUTOMATED_PUSH
from gm_protocol import GmProtocol

from talos.cache.base import vote_cache
from talos.models.soft_article import SoftArticleReplyVote
from talos.models.tractate import TractateVote, TractateReplyVote
from talos.services.soft_article.reply import SoftArticleReplyService
from talos.tasks import view_increase
from talos.decorators import list_interface
from talos.libs.datetime_utils import get_timestamp_or_none
from talos.libs.diaryrank_utils import DiaryRankTool
from talos.libs.image_utils import get_full_path
from talos.logger import info_logger
from talos.models.diary.diary import Diary
from talos.models.diary.diaryvote import DiaryVote
from talos.models.topic.topicreply import TopicReply
from talos.rpc import bind, bind_context
from talos.services import (
    UserService,
    UserConvertService,
)
from talos.services import get_user_from_context
from talos.tasks.vote import fake_vote, normal_vote, get_async_args_v2
from talos.tools.vote_tool import (
    VoteTool,
    get_answer_vote_info_by_id,
    get_user_unread_vote_count,
    get_answer_vote_info_by_ids,
    get_unread_vote_num_by_user_id,
)
from qa.libs import get_answer_replies_by_ids
from qa.models import AnswerVoteReply
from talos.services.tractate import TractateService
from talos.services.tractate import TractateReplyService
from user_hierarchy.portal import process_task
from utils.rpc import gen, get_current_user
from utils.push import vote_push
from talos.models.topic import Problem, TopicImage, TopicVote, TopicReplyVote
from utils.stat_log import SocialStatLogForUserAction
from talos.services.other import get_user_lastest_device_app_version_by_user_id
from utils.common import is_version_gray


@bind('topic/vote')
def topic_vote(topic_id):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)
    try:
        topic = Problem.objects.get(pk=topic_id)
    except (Problem.DoesNotExist, ValueError):
        return gen(CODES.TOPIC_NOT_FOUND)

    if user.id != topic.user_id and topic.diary_id is not None:
        diary_id = topic.diary_id
        d = DiaryRankTool(diary_id)
        now = datetime.datetime.now()
        d.add_heat(DIARY_OPERATION.OTHER_VOTE, now)

    try:
        t, created = TopicVote.objects.get_or_create(user_id=user.id, topic_id=topic.id, topic_author_id=topic.user_id)
    except IntegrityError:
        return gen(CODES.TOPIC_NOT_FOUND)

    if created:
        topic.save()
        # 对作者增加点赞数
        topic_author = UserService.get_user_by_user_id(topic.user_id)
        topic_author.incr_vote_count()

        if topic.diary_id:
            alert = u'{user_name}刚刚赞了你的《{diary_title}》，比心~'.format(user_name=user.nick_name, diary_title=topic.diary.title)
            push_url = GmProtocol().get_topic_detail(id=topic.id)
            user_id = topic.user_id
            version = get_user_lastest_device_app_version_by_user_id(user_id)
            # 推送跳转到消息页的赞列表
            if is_version_gray(version, '7.29.0'):
                push_url = GmProtocol().vote_or_favor_list(
                    segment_index=NOTICATION_LIST_CATEGORY.VOTE,
                    new_vote=True,
                )
            vote_push(user_id=user_id, alert=alert, push_url=push_url,
                      push_type=AUTOMATED_PUSH.DIARY_POST_RECEIVED_PRAISE)
    elif not created:
        return gen(CODES.TOPIC_HAS_VOTED)

    # 用户行为埋点，点赞相关
    _topic_type = topic.topic_type
    if _topic_type in [TOPIC_TYPE.ASK, TOPIC_TYPE.SHARE, TOPIC_TYPE.TOPIC]:  # 日记帖
        _stat_log_data = {
            "user_id": user.id,
            "content_id": topic.diary_id,
            "content_type": SocialStatLogForUserAction.CONTENT_TYPE.diary
        }

    elif _topic_type in [TOPIC_TYPE.USER_ARTICLE, TOPIC_TYPE.COLUMN_ARTICLE]:  # 专栏
        _stat_log_data = {
            "user_id": user.id,
            "content_id": topic.id,
            "content_type": SocialStatLogForUserAction.CONTENT_TYPE.article
        }

    else:
        _stat_log_data = {}

    if _stat_log_data:
        SocialStatLogForUserAction.stat_log_for_like(**_stat_log_data)

    # increase directly without delay
    normal_vote(user=user, item=topic)   # TODO CR 看起来 normal_vote 不是一个task...
    growth_value, point_value, submit_count = 0, 0, 0
    if not user.id == topic.user_id:
        event_data = process_task(user_id=user.id, event_type=EventType.VOTE_MULTI_TIMES, related_item=t.id)
        growth_value, point_value = event_data['growth_value'], event_data['point_value']
        submit_count = event_data['submit_count']

    re_data = {'tag_ids':[tag['tag_id'] for tag in topic.get_tags()]}
    re_data['growth_value'] = growth_value
    re_data['point_value'] = point_value
    re_data['submit_count'] = submit_count

    return re_data


@bind('topic/cancel_vote')
def topic_cancel_vote(topic_id):
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)
    try:
        topic = Problem.objects.get(pk=topic_id)
    except Problem.DoesNotExist:
        return gen(CODES.TOPIC_NOT_FOUND)

    try:
        rel = TopicVote.objects.get(user_id=user.id, topic_id=topic.id)
        rel.delete()
        topic.save()

        view_increase(Problem.get_redis_vote_key(), topic.id, reverse=True)     # TODO CR 异步任务

        topic_author = UserService.get_user_by_user_id(user_id=topic.user_id)
        topic_author.decr_vote_count()

        return gen(CODES.SUCCESS)

    except TopicVote.DoesNotExist:
        return gen(CODES.OPERATION_NOT_SUPPORTED)


@bind('topic/vote_list')
@list_interface(offset_name='start_num', limit_name='count', element_model=TopicVote)
def votes_for_my_topic(start_num=0, count=10):
    """
    get unread votes for my topics order by vote_time
    """
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)
    my_topics = Problem.objects.filter(user_id=user.id, is_online=True)
    if not my_topics:
        return []

    votes = TopicVote.objects.filter(topic_id__in=my_topics).order_by('-id')[start_num:start_num + count]
    result = []
    user_ids = [v.user_id for v in votes]
    users = UserService.get_users_by_user_ids(user_ids=user_ids)

    for vote in votes:
        images = TopicImage.objects.filter(topic=vote.topic).order_by('id')
        user_id = int(vote.user_id)
        user = users.get(user_id)

        if not user:
            continue

        content = vote.topic.get_title()
        info = {
            'topic_id': vote.topic.id,
            'nickname': user.nickname,
            'user_id': vote.user.id,
            'portrait': user.portrait,
            'vote_time': get_timestamp_or_none(vote.vote_time),
            'image': images and get_full_path(images[0].image_url, '-thumb') or '',
            'content': escape(content),
            'membership_level': user.membership_level,
        }
        result.append(info)

    return result


@bind_context('topic/read_votes')
def read_votes(ctx):
    """ mark as read
    """
    start_time = time.time()
    user = get_user_from_context(ctx)

    votes = []

    _votes = TopicVote.objects.filter(topic_author_id=user.id)
    votes.extend(_votes.filter(unread=True).distinct())

    my_diaries = Diary.objects.filter(user_id=user.id)
    if my_diaries:
        _votes = DiaryVote.objects.filter(diary_id__in=my_diaries)
        votes.extend(_votes.filter(unread=True).distinct())

    _topic_reply_votes = TopicReplyVote.objects.filter(topic_reply__user_id=user.id, unread=True).distinct()
    votes.extend(_topic_reply_votes)

    # force to user optimized sql
    count = 0
    for vote in votes:
        vote.unread = False
        vote.save()
        count += 1

    duration = time.time() - start_time
    info_logger.info(
        'PROFILER: mark all topic vote as read, votes: %s, duration: %s s',
        count,
        duration
    )

    return gen(CODES.SUCCESS)


@bind_context('topic/unread_votes_count')
def unread_vote_count(ctx):
    """ get unread vote num
    """
    user = get_user_from_context(ctx)
    if not user:
        return 0

    count = get_unread_vote_num_by_user_id(user.id)
    return count


@bind("topic/topicvote/latest")
@list_interface(limit_name='count', element_model=TopicReply)
def latest_topic_votes(id=None, count=10):
    if not id:
        return

    rels = TopicVote.objects.filter(topic_id=id).order_by('-vote_time')[:count]

    user_ids = [r.user_id for r in rels]
    users = UserService.get_users_by_user_ids(user_ids=user_ids)

    images = []
    for rel in rels:
        user_id = int(rel.user_id)
        user = users.get(user_id)
        if not user:
            continue

        images.append({
            'portrait': user.portrait,
            'uid': rel.user_id,
            'membership_level': user.membership_level,
        })

    return images


@bind('topic/user/voted')
def get_user_voted_topic_of_topic_ids(user_id, topic_ids):
    if not topic_ids or not user_id:
        return []
    res = TopicVote.objects.filter(user_id=user_id, topic_id__in=topic_ids).values_list('topic_id', flat=True)
    return list(res)


@bind('topic/votes/received')
# @bind_context('api/votes/received', login_required=True)
@list_interface(offset_name='start_num', limit_name='count')
def votes_received(start_num=0, count=10, new_version=False):
    """
    get all votes for my topics/diaries. diary votes first
    """
    user = get_current_user()
    if not user:
        return gen(CODES.LOGIN_REQUIRED)

    result = []
    _result = []
    vote_tool = VoteTool(vote_cache, user.id, new_version=new_version)

    tractate_ids = []
    tractate_reply_ids = []
    users_ids = []
    answer_vote_ids = []
    answer_vote_infos = {}
    answer_reply_ids = []
    soft_article_reply_ids = []
    for r in vote_tool.get_votes_received(start_num, count):
        _id, _type = r

        if _type == VOTEOBJECT.DIARY:
            try:
                vote = DiaryVote.objects.get(pk=_id)
                data = vote.to_dict()
                users_ids.append(vote.user_id)
                result.append(data)
            except DiaryVote.DoesNotExist:
                continue

        if _type == VOTEOBJECT.TOPIC:
            try:
                vote = TopicVote.objects.get(pk=_id)
                data = vote.to_dict()
                users_ids.append(vote.user_id)
                result.append(data)
            except TopicVote.DoesNotExist:
                continue

        if _type == VOTEOBJECT.TOPIC_REPLY:
            try:
                vote = TopicReplyVote.objects.get(pk=_id)
                data = vote.to_dict()
                users_ids.append(vote.user_id)
                result.append(data)
            except TopicReplyVote.DoesNotExist:
                continue

        if _type == VOTEOBJECT.TRACTATE:   # 新帖子点赞
            try:
                vote = TractateVote.objects.get(pk=_id)
                tractate_ids.append(vote.tractate_id)
                users_ids.append(vote.user_id)
                result.append(vote.to_dict())
            except TractateVote.DoesNotExist:
                continue

        if _type == VOTEOBJECT.TRACTATE_REPLY:
            try:
                vote = TractateReplyVote.objects.get(pk=_id)
                tractate_reply_ids.append(vote.reply_id)
                users_ids.append(vote.user_id)
                result.append(vote.to_dict())
            except TractateReplyVote.DoesNotExist:
                continue

        if _type == VOTEOBJECT.ANSWER_REPLY:
            try:
                vote = AnswerVoteReply.objects.get(pk=_id)
                answer_reply_ids.append(vote.answerreply_id)
                users_ids.append(vote.user_id)
                result.append(vote.to_dict())
            except AnswerVoteReply.DoesNotExist:
                continue

        if _type == VOTEOBJECT.SOFT_ARTICLE_REPLY:
            try:
                vote = SoftArticleReplyVote.objects.get(pk=_id)
                soft_article_reply_ids.append(vote.reply_id)
                users_ids.append(vote.user_id)
                result.append(vote.to_dict())
            except TractateReplyVote.DoesNotExist:
                continue

        if _type == VOTEOBJECT.ANSWER:
            answer_vote_ids.append(int(_id))
            result.append(int(_id))

    if answer_vote_ids:
        answer_vote_infos = get_answer_vote_info_by_ids(answer_vote_ids)
    for i, _r in enumerate(result):
        if isinstance(_r, int):
            vote_info = answer_vote_infos.get(str(_r), None)
            if vote_info:
                users_ids.append(vote_info.get("user_id", 0))
                _result.append(vote_info)
        else:
            _result.append(_r)

    users = UserConvertService.get_user_info_by_user_ids(users_ids)
    tractates = TractateService.list_tractate_by_ids(tractate_ids)
    tractate_replies = TractateReplyService.list_objs_by_ids(tractate_reply_ids)
    answer_repies = get_answer_replies_by_ids(answer_reply_ids)
    soft_article_replies = SoftArticleReplyService.list_objs_by_ids(soft_article_reply_ids)

    for item in _result:

        user_info = users.get(item["user_id"])
        if user_info:
            item.update({
                "user_info": user_info,
                # 以下是冗余字段
                "nickname": user_info.get("user_name", ""),
                "portrait": user_info.get("portrait", ""),
                "membership_level": user_info.get("membership_level", "0"),
            })

        if item.get("type", None) in (
                VOTEOBJECT.TRACTATE, VOTEOBJECT.TRACTATE_REPLY, VOTEOBJECT.ANSWER_REPLY, VOTEOBJECT.SOFT_ARTICLE_REPLY
        ):

            if item.get("type", None) == VOTEOBJECT.TRACTATE:
                tractate = tractates.get(item["tractate_id"])
                item["content"] = tractate.content if tractate else ""

            elif item.get("type", None) == VOTEOBJECT.TRACTATE_REPLY:
                if item["reply_id"]:
                    reply = tractate_replies.get(item["reply_id"])
                else:
                    reply = tractates.get(item["tractate_id"])
                item["content"] = reply.content if reply else ""

            elif item.get("type", None) == VOTEOBJECT.ANSWER_REPLY:
                reply = answer_repies.get(str(item["reply_id"]))
                item["answer_id"] = reply.answer_id if reply else ""
                item["content"] = reply.content if reply else ""

            elif item.get('type', '') == VOTEOBJECT.SOFT_ARTICLE_REPLY:
                reply = {}
                if item['reply_id']:
                    reply = soft_article_replies.get(item['reply_id'])
                item['content'] = '' if not reply else reply.content

    return _result


@bind('topic/votes/received_aggregation')
# @bind_context('api/votes/received_aggregation')
def get_reseived_votes_aggregation(new_version=False):
    No_new_vote = {
        'is_about_me': False,
        'toptitle': '',
        'type': NotificationType.VOTE_ME,
        'unread_count': 0,
        "title": u'赞',
        "url": "",
        "content": "",
        "create_time": "",
        "user": "",
        "is_view": False,
        "imgheader": settings.NOTIFICATION_VOTE_IMG,
        "pic": "",
        "id": '',
    }
    user = get_current_user()
    if not user:
        return No_new_vote

    count = get_user_unread_vote_count(user.id)
    if count == 0:
        return No_new_vote

    # get latest vote user info: prevent cache id can not find vote item, try 3 times
    vote = None

    vote_tool = VoteTool(vote_cache, user.id, new_version=True)
    for r in vote_tool.get_votes_received(0, 3):
        _id, _type = r

        if _type == VOTEOBJECT.DIARY:
            try:
                vote = DiaryVote.objects.get(pk=_id)
                user = UserService.get_user_by_user_id(vote.user_id)
                return format_result(user, count, create_time=get_timestamp_or_none(vote.vote_time))

            except DiaryVote.DoesNotExist:
                continue

        if _type == VOTEOBJECT.TOPIC:
            try:
                vote = TopicVote.objects.get(pk=_id)
                user = UserService.get_user_by_user_id(vote.user_id)
                return format_result(user, count, create_time=get_timestamp_or_none(vote.vote_time))

            except TopicVote.DoesNotExist:
                continue

        if _type == VOTEOBJECT.TOPIC_REPLY:
            try:
                vote = TopicReplyVote.objects.get(pk=_id)
                user = UserService.get_user_by_user_id(vote.user_id)
                return format_result(user, count, create_time=get_timestamp_or_none(vote.vote_time))

            except TopicReplyVote.DoesNotExist:
                continue

        if _type == VOTEOBJECT.ANSWER:
            vote = get_answer_vote_info_by_id(int(_id))
            if not vote:
                continue

            return format_result(vote, count, is_answer_vote=True)

    if not vote:
        No_new_vote['unread_count'] = count
        return No_new_vote


def format_result(latest_user_info, count, is_answer_vote=False, create_time=""):
    if is_answer_vote:
        nickname = latest_user_info['nickname']
        portrait = latest_user_info['portrait']
        create_time = latest_user_info["vote_time"]
    else:
        nickname = latest_user_info.nickname
        portrait = latest_user_info.portrait

    title = '{}等人给你点了赞'.format(nickname) if count > 1 else '{}赞了你'.format(nickname)
    return {
        'is_about_me': False,
        'toptitle': '',
        'type': NotificationType.VOTE_ME,
        'unread_count': count,
        "title": title,
        "url": "",
        "content": "",
        "create_time": create_time,
        "user": "",
        "is_view": False,
        "imgheader": portrait,
        "pic": "",
        "id": '',
    }
