# -*- coding: UTF-8 -*-
from __future__ import absolute_import
import re
import datetime
from django.db import IntegrityError
from django.db.models import Q
from django.db import transaction
from django.conf import settings

from api.models import Tag
from api.models.types import RECOMMEND_TYPE
from talos.tools.reply_tool import topic_reply_common
from api.tool.user_tool import get_user_by_id
from gm_types.gaia import USER_CLASSIFY
from gm_types.gaia import TOPIC_TYPE, INDEX_CARD_TYPE
from rpc.all import get_rpc_remote_invoker
from rpc.decorators import bind_context
from rpc.exceptions import RPCIntegrityError, RPCNotFoundException, GaiaRPCFaultException
from rpc.tool.dict_mixin import to_dict
from rpc.tool.log_tool import info_logger, logging_exception
from services.talos_service import (
    topic_get_topic_info_by_id,
    topic_update_reply_num_by_id,
    topic_reply_get_reply_info_by_ids,
    topic_reply_update_reply_num_by_ids,
)
from hera.queries.choice_query import choice_query
from hera.queries.talos.topic import TopicDQ
from hera.queries.talos.topicreply import TopicReplyDQ
from hera.utils import model_edit
from statistic.models import ClassifyUser
from services.talos_service import diary_update_doctor_messages_status
from talos.models.topic.column import ColumnTab, ColumnTabTag, Columnist, ArticleCard, Article
from talos.models.diary import Diary
from talos.models.topic import (Problem, TopicReply, TopicImage, Video, ProblemTagV3)
from talos.tools.topic_tool import modify_diary_video_tag_by_topic_ids


uri_pre = 'hera/topic'


@bind_context(uri_pre + '/choices')
def topic_choices(ctx, q='', page=1, num=30, initial=None):
    page = int(page)
    num = int(num)

    default_using = getattr(settings, 'MIMAS_SLAVE_DB_NAME', '')
    query = choice_query(Problem, ['id', 'ask', 'answer'], 'id', q, initial, using=default_using)
    total_count = 0
    start_pos = (page - 1) * num
    start_pos = start_pos if start_pos >= 0 else 0
    results = [
        {
            'id': obj.id,
            'text': u'{}:{}'.format(obj.id, obj.ask if obj.ask else obj.answer[:90]),
        } for obj in query[start_pos: start_pos + num]
        ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


@bind_context(uri_pre + '/query')
def topic_query(ctx, options):
    dqobj = TopicDQ()
    return dqobj.process(**options)


@bind_context(uri_pre + '/choices_ask_suozhang')
def topic_choices_ask_suozhang(ctx, q='', page=1, num=30, initial=None):
    """
    获取讨论帖和所长发帖
    """
    page = int(page)
    num = int(num)

    if initial is not None:
        if isinstance(initial, (list, tuple)):
            qry = Q(id__in=initial)
        else:
            qry = Q(id=initial)
    elif q:
        qry = (Q(id__contains=q) | Q(ask__contains=q) | Q(answer__contains=q)) & (
            Q(topic_type=TOPIC_TYPE.TOPIC) | Q(user_id=22))
    else:
        qry = Q(topic_type=TOPIC_TYPE.TOPIC) | Q(user_id=22)
    query = Problem.objects.using(settings.MIMAS_SLAVE_DB_NAME).filter(qry)
    total_count = 0
    start_pos = (page - 1) * num
    start_pos = start_pos if start_pos >= 0 else 0
    results = [
        {
            'id': obj.id,
            'text': u'{}:{}'.format(obj.id, obj.ask if obj.ask else obj.answer[:90]),
        } for obj in query[start_pos: start_pos + num]]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


# @bind_context(uri_pre + '/list')
# def topic_datatable(ctx, req_data):
#     dtobj = TopicDT(Problem)
#     return dtobj.process(req_data, ['id', 'title', 'user__last_name', 'ask', 'is_online'])

@bind_context(uri_pre + '/get')
def topic_detail(ctx, topic_id, options=None):
    try:
        topic = Problem.objects.get(id=topic_id)
    except:
        raise RPCNotFoundException
    if options is None:
        options = {
            'fields': None,
            'excludes': None,
            'expands': None,
        }
    topic_data = to_dict(topic, **options)
    topic_data['tags'] = [t.id for t in topic.tags]
    topic_data["tag_v3_ids"] = list(ProblemTagV3.objects.filter(problem_id=topic_id).values_list("tag_v3_id", flat=True))
    if ClassifyUser.objects.filter(user_id=topic.user_id, classify=USER_CLASSIFY.MODEL).exists():
        topic_data['is_real'] = u'否'
    else:
        topic_data['is_real'] = u'是'
    return topic_data


@bind_context(uri_pre + '/edit')
def topic_edit(ctx, topic_id=None, topic_info=None):
    if topic_info is None:
        return None
    tags = topic_info.pop('tags', [])
    tag_v3_ids = topic_info.pop("tag_v3_ids", [])
    topic_info.pop('is_real')
    if topic_id is None:
        try:
            topic = Problem.objects.create(**topic_info)
        except IntegrityError:
            raise RPCIntegrityError
    else:
        try:
            topic = Problem.objects.get(id=topic_id)
        except:
            info_logger.info(__import__('traceback').format_exc())
            raise RPCNotFoundException
        # 修改日记本帖子上下线
        if (topic.is_online != topic_info['is_online']) and topic.topic_type == TOPIC_TYPE.SHARE:
            # topic.update_reply_num(topic_info['is_online'])
            topic_update_reply_num_by_id(id=topic.id, is_online=topic_info['is_online'])
        for k, v in topic_info.iteritems():
            setattr(topic, k, v)

        diary_update_doctor_messages_status(ids=[topic_id], data_type=INDEX_CARD_TYPE.DIARY_TOPIC,
                                            status=topic_info['is_online'])

    if tags:
        topic.upd_tags(tags)

    tag_v3_new_ids = set(map(int, tag_v3_ids))
    tag_v3_old_ids = set(ProblemTagV3.objects.filter(problem_id=topic_id).values_list("tag_v3_id", flat=True))
    ProblemTagV3.objects.filter(problem_id=topic_id, tag_v3_id__in=tag_v3_old_ids - tag_v3_new_ids).delete()
    for tag_id in (tag_v3_new_ids - tag_v3_old_ids):
        ProblemTagV3.objects.get_or_create(problem_id=topic_id, tag_v3_id=tag_id)

    topic.save()

    try:
        get_rpc_remote_invoker()['diary/cache_cover_by_ids_or_topic_ids'](topic_ids=[topic_id]).unwrap()
    except:
        pass

    if 'is_online' in topic_info:
        modify_diary_video_tag_by_topic_ids([topic.id, ])

    return topic.id


@bind_context('hera/topic/create', login_required=True)
def topic_create(ctx, diary_id, content, tags=[]):
    problem = Problem()
    diary = Diary.objects.get(pk=diary_id)
    problem.diary = diary
    problem.answer = content
    problem.save()
    problem.add_tags(tags)
    return {'problem': problem.id}


@bind_context(uri_pre + '/info')
def topic_info(ctx, topic_id, options=None):
    try:
        topic = Problem.objects.get(id=topic_id)
    except:
        raise RPCNotFoundException
    # topic_info = topic.get_topic_info()
    topic_info = topic_get_topic_info_by_id(id=topic.id)
    return topic_info


@bind_context(uri_pre + '/image_update')
def topic_image_update(ctx, topic_id, image_new):
    try:
        TopicImage.objects.filter(topic_id=topic_id).delete()
    except:
        raise RPCNotFoundException
    for image in image_new:
        TopicImage.objects.create(topic_id=topic_id, image_url=image)
    try:
        get_rpc_remote_invoker()['diary/cache_cover_by_ids_or_topic_ids'](topic_ids=[topic_id]).unwrap()
    except:
        pass
    return None


@bind_context(uri_pre + '/reply')
def topic_reply(ctx, topic_id, options=None):
    try:
        topic = Problem.objects.get(id=topic_id)
        topic_replys = TopicReply.objects.filter(problem_id=topic.id)
    except:
        raise RPCNotFoundException
    ids = []
    for topic_reply in topic_replys:
        if not topic_reply.replied_topic:  # 不是子回复
            # data.append(topic_reply.get_reply_info())
            # for comment in topic_reply.comments.all():
            #     data.append(comment.get_reply_info())
            ids.append(topic_reply.id)
            ids.extend(list(topic_reply.comments.values_list('id', flat=True)))
    data = topic_reply_get_reply_info_by_ids(ids=ids)
    return data


@bind_context(uri_pre + '/reply_update')
def topic_reply_update(ctx, content='', is_delete='', reply_ids=None):
    """话题回复更新
    reply_ids: 话题回复ID, 必选
    content: 回复的内容,
    is_delete: 删除
    """
    online_ids = []
    not_online_ids = []
    for reply_id in reply_ids:
        reply = TopicReply.objects.get(id=reply_id)
        if reply:
            # if reply
            if reply.is_online == is_delete:
                # reply.update_reply_num(is_online=not is_delete)
                if is_delete:
                    not_online_ids.append(reply.id)
                else:
                    online_ids.append(reply.id)
            reply.is_online = not is_delete
            if content:
                reply.content = content
            reply.save()
    topic_reply_update_reply_num_by_ids(ids=online_ids, is_online=True)
    topic_reply_update_reply_num_by_ids(ids=not_online_ids, is_online=False)
    return None


@bind_context(uri_pre + '/reply_query')
def topicreply_query(ctx, options):
    dqobj = TopicReplyDQ()
    return dqobj.process(**options)


# @bind_context(uri_pre + '/reply_list')
# def reply_datatable(ctx, req_data):
#     dtobj = TopicReplyDT(TopicReply)
#     return dtobj.process(req_data, ['ask', 'user__last_name', 'answer'])


@bind_context(uri_pre + '/reply_choices')
def reply_choices(ctx, q='', page=1, num=30, initial=None):
    page = int(page)
    num = int(num)

    default_using = getattr(settings, 'MIMAS_SLAVE_DB_NAME', '')
    query = choice_query(TopicReply, ['id', 'content'], 'id', q, initial, using=default_using)
    total_count = 0
    start_pos = (page - 1) * num
    start_pos = start_pos if start_pos >= 0 else 0
    results = [
        {
            'id': obj.id,
            'text': u'{}:{}'.format(obj.id, obj.content),
        } for obj in query[start_pos: start_pos + num]
        ]
    return {'total_count': total_count, 'results': results, 'page': page, 'num': num}


@bind_context(uri_pre + "/add_reply")
def add_reply(ctx, topic_id, user, reply_id=None, content='', is_fake=False):
    """话题回复.
        :param content: 回复的内容,
        :param reply_id: 评论ID，可选. 回复帖子时, 该字段为None; 回复其他评论时, 不可为空
        :param topic_id: 话题ID, 必选
        :param user: 用户
    """
    user = get_user_by_id(user)
    # TODO 下期重构时处理此函数
    return topic_reply_common(user, topic_id, reply_id, content, is_fake=is_fake)


def problem_has_hot_tag(problem):
    tags = Tag.objects.filter(recommend_type=RECOMMEND_TYPE.RECOMMENDED_HOT)
    return tags.filter(id__in=problem.tag_ids).exists()


@bind_context(uri_pre + '/listupdate')
def topic_listupdate(ctx, items):
    info = []
    status = False
    # 被设为热门话题推荐的帖子或回复不受上下线和下沉操作的限制
    # 批量上线帖子即上线帖子和其回复。被举报处理的帖子除外。
    for obj in items:
        status = obj['is_online']
        problem = Problem.objects.get(pk=obj['key'])
        problem.is_online = obj['is_online']
        problem.save()
        TopicReply.objects.filter(problem=problem, report_reply=None).update(is_online=obj['is_online'])
        info.append(obj['key'])

    diary_update_doctor_messages_status(ids=info, data_type=INDEX_CARD_TYPE.DIARY_TOPIC, status=status)
    return info


@bind_context(uri_pre + '/sinkupdate')
def topic_sinkupdate(ctx, items):
    info = [obj['key'] for obj in items]
    # 被设为热门话题推荐的帖子或回复不受下沉操作的限制
    # 批量下沉帖子即下沉帖子及其回复。
    for obj in items:
        problem = Problem.objects.get(pk=obj['key'])
        problem.is_sink = True
        problem.save()
        info.append(obj['key'])
    return info


@bind_context(uri_pre + '/columntab/edit')
@transaction.atomic
def columntab_edit(ctx, columntab_id=None, columntab_info=None):
    tag_ids = columntab_info.pop('tags')
    columntabs = ColumnTab.objects.exclude(id=columntab_id).filter(is_online=True)
    if columntabs.filter(order=columntab_info['order']).exists():
        message = u'该位置已存在生效tab！'
        raise GaiaRPCFaultException(error=1, message=message, data=None)
    if columntabs.filter(name=columntab_info['name']).exists():
        message = u'该tab名已存在生效tab！'
        raise GaiaRPCFaultException(error=1, message=message, data=None)
    if columntab_id is None:
        columntab = ColumnTab.objects.create(**columntab_info)
    else:
        try:
            columntab = ColumnTab.objects.get(id=columntab_id)
        except:
            raise RPCNotFoundException
        for k, v in columntab_info.iteritems():
            setattr(columntab, k, v)
        columntab.save()

    old_tags = set(columntab.columntabtag_set.values_list('tag_id', flat=True))
    new_tags = set(map(int, tag_ids))
    for tag in new_tags - old_tags:
        ColumnTabTag.objects.get_or_create(column_tab=columntab, tag_id=tag)
    ColumnTabTag.objects.filter(column_tab=columntab,
                                tag_id__in=(old_tags - new_tags)).delete()
    return {
        'id': columntab.id
    }


@bind_context(uri_pre + '/article/edit')
@transaction.atomic
def article_edit(ctx, article_id=None, article_info=None):
    tags = article_info.pop('tags_id')
    tag_v3_ids = article_info.pop("tag_v3_ids", [])
    image = article_info.pop('images')
    is_star = article_info.pop('is_star', None)
    answer_richtext = article_info['answer_richtext']
    article_info['topic_type'] = TOPIC_TYPE.COLUMN_ARTICLE if not is_star else TOPIC_TYPE.USER_ARTICLE
    article_info['last_modified'] = datetime.datetime.now()

    article_modul_info = {}
    article_modul_info['article_type'] = article_info['topic_type']
    article_modul_info['is_online'] = article_info['is_online']

    if article_id is None:
        topic = Problem.objects.create(**article_info)
        article_modul_info['article_id'] = topic.id
        Article.objects.create(**article_modul_info)
    else:
        try:
            topic = Problem.objects.get(id=article_id)
            article = Article.objects.get(article_id=article_id)
        except:
            raise RPCNotFoundException
        for k, v in article_info.iteritems():
            setattr(topic, k, v)
        topic.save()
        for k, v in article_modul_info.iteritems():
            setattr(article, k, v)
        article.save()

    card_regex_map = {
        'service': re.compile(r'\[ref:service:(\d+)\]'),
        'hospital': re.compile(r'\[ref:hospital:(\w+)\]'),
        'doctor': re.compile(r'\[ref:doctor:(\w+)\]'),
        'answer': re.compile(r'\[ref:answer:(\d+)\]'),
        'rankboard': re.compile(r'\[ref:rankboard:(\d+)\]'),
        'diary': re.compile(r'\[ref:diary:(\d+)\]'),
    }
    cards = ArticleCard.objects.filter(article_id=topic.id)

    for card_type in card_regex_map.keys():
        m_id = '{}_id'.format(card_type)
        old_val = set(map(str, cards.filter(**{'{}__isnull'.format(m_id): False}).values_list(m_id, flat=True)))
        new_val = set(re.findall(card_regex_map[card_type], answer_richtext))
        for obj in (new_val - old_val):
            ArticleCard.objects.get_or_create(**{'article_id': topic.id, m_id: obj})
        ArticleCard.objects.filter(**{'article_id': topic.id, '{}__in'.format(m_id): old_val-new_val}).delete()

    if tags:
        topic.upd_tags(tags)

    # 标签 3.0
    tag_v3_new_ids = set(map(int, tag_v3_ids))
    tag_v3_old_ids = set(ProblemTagV3.objects.filter(problem_id=topic.id).values_list("tag_v3_id", flat=True))
    ProblemTagV3.objects.filter(problem_id=topic.id, tag_v3_id__in=tag_v3_old_ids - tag_v3_new_ids).delete()
    for tag_id in (tag_v3_new_ids - tag_v3_old_ids):
        ProblemTagV3.objects.get_or_create(problem_id=topic.id, tag_v3_id=tag_id)

    new_image = set(image)
    old_image = set(topic.images.values_list('image_url', flat=True))
    for obj in new_image - old_image:
        TopicImage.objects.get_or_create(topic_id=topic.id, image_url=obj)
    TopicImage.objects.filter(topic_id=topic.id, image_url__in=(old_image-new_image)).delete()
    TopicImage.objects.filter(topic_id=topic.id, image_url=image[0]).update(is_cover=True)

    # 通知 mimas problem 检查富文本数据，更新视频。如果rpc请求失败，则记录日志不再通知
    try:
        _ = get_rpc_remote_invoker()['topic/article/add_video_water_url'](
            source_id=topic.id
        ).unwrap()
    except:
        logging_exception()
        pass

    return {
        'id': topic.id,
    }


@bind_context(uri_pre + '/article/author/edit')
def article_author_edit(ctx, columnist_id=None, columnist_info=None):
    columnist = model_edit(Columnist, columnist_id, columnist_info)
    return {
        'id': columnist.id
    }
