# -*- coding: utf8 -*-

from __future__ import unicode_literals, absolute_import, print_function
import datetime
from collections import defaultdict, namedtuple

from django.db.models import Q, Count
from django.utils.html import escape

from gm_types.error import ERROR as CODES
from gm_types.gaia import TOPIC_ORDER_IN_DIARY, DIARY_AUDIT_STATUS
from gm_types.gaia import PROBLEM_FLAG_CHOICES, TAG_TYPE
from gm_rpcd.all import bind

from talos.rpc import bind_context
from talos.statistic import diary_view_increase_num, topic_view_increase_num
from talos.libs.datetime_utils import get_timestamp_or_none
from talos.libs.image_utils import get_full_path
from talos.libs.datetime_utils import get_timestamp
from talos.manager.topic import topic_list_manager
from talos.manager.diary import diary_list_manager
from talos.models.diary import Diary, DiaryFavor, DiaryTag, BDDiary, DiaryCheck
from talos.models.topic import Problem
from talos.services import (
    get_user_from_context,
    UserService,
    GoodsService,
    TagService,
    UserConvertService,
)
from utils.rpc import gen


def _get_topic_pageview(id):
    return Problem.get_view_amount_of_id(id)


def get_diary_card_info_by_ids(ids):
    if len(ids) == 0:
        return []

    diaries = Diary.objects.filter(id__in=ids, is_online=True)
    result = []
    user_ids = [d.user_id for d in diaries]
    diary_users = UserService.get_users_by_user_ids(user_ids)
    for d in diaries:
        diary_user = diary_users[d.user_id] if d.user_id in diary_users else None
        result.append({
            'id': d.id,
            'tags': d.wiki_info,
            "images": d.cover, # (-half)
            "view_count": d.view_num,
            "vote_count": diary_user.vote_count if diary_user else 0,
            "comment_count": d.reply_num,
            "author_name": diary_user.nickname if diary_user else '', # (发布该日记的作者)
            "city": diary_user.city_name if diary_user else '', # (发布该日记的作者的地区)
        })
    return result


@bind_context('diary/detail')
def diary_detail(ctx, id, need_order=False, simple_data=False):
    group_diary_factor = 3.7
    from talos.models.topic import TopicImage

    try:
        diary = Diary.objects.get(pk=id, is_online=True)
    except Diary.DoesNotExist:
        return None

    user = get_user_from_context(ctx)
    id = diary.id
    author_id = diary.user_id
    service_id = diary.service_id
    view_num = diary.view_num
    images = diary.cover
    topic_num = diary.topic_num
    tags = diary.tags_new_era
    pre_operation_image_num = diary.pre_operation_images.count()

    post_operation_image_query = {
        'topic__diary_id': id,
        'topic__is_online': True,
    }
    post_operation_image_num = TopicImage.objects.filter(**post_operation_image_query).count()

    operation_info = {}
    online_service_id = second_tag_id = None
    if not simple_data:
        operation_info = diary.operation_info()
        # 如果关联美购下线, 则取关联订单的医生名下同项目tag的美购（在线美购）
        online_service_id = GoodsService.get_diary_detail_related_service_id(diary.service_id, diary.order_id)
        second_tag_id = TagService.get_second_tag_id_by_diary_tags(tags)

    #  7715改版   筛选出日记本关联tag---只有一个
    tag_data = GoodsService.get_tag_data(diary.all_tags)  #日记本取出来的一级tag
    diary_count = Problem.objects.filter(user_id=diary.user_id, is_online=True).count()
    group_diary_num = DiaryTag.objects.filter(tag_id=tag_data.get('tag_id')).count() if tag_data.get('tag_id') else 0

    recent_topic = diary.latest_topic
    if recent_topic is not None:
        recent_topic_content = escape(recent_topic.answer)
    else:
        recent_topic_content = None

    social_info = None
    ownership = False
    is_author = False
    is_login = False
    if user:
        from social.models import SocialInfo
        social_info = SocialInfo(uid=user.id)
        if user.id == diary.user_id:
            ownership = True
        is_author = True if user.id == author_id else False
        is_login = True

    is_following = social_info and social_info.is_following_user(uid=author_id) or False
    topic_reply_count = Problem.objects.filter(diary_id=id).all().aggregate(reply_num=Count('reply_num'))
    reply_num_for_diary = diary.topicreply_set.filter(is_online=True).count()    # 日记本评论

    vote_num = diary.vote_num
    is_voted = diary.is_voted_by(user)
    try:
        favord_count = diary.favor_diary_diary.filter(is_deleted=False).count()
    except DiaryFavor.DoesNotExist:
        favord_count = 0

    is_favored = diary.favor_diary_diary.filter(user_id=user.id, is_deleted=False).exists() if user else False

    if diary.is_fake_operation_time or diary.operation_time is None:
        is_fake_operation_time = True
    else:
        is_fake_operation_time = False

    oldest_topic_create_time = diary.diary_first_topic and diary.diary_first_topic.created_time
    if oldest_topic_create_time and diary.operation_time:
        """
        需要使用 unix 基准时间来比较
        防止出现边界问题
        example:
        d1 = datetime.datetime(2017, 1, 2, 11, 11)
        d2 = datetime.datetime(2017, 1, 3, 10, 11)
        (d2 - d1).days = 0  会出现24小时跨天时间边界问题
        """
        unix_1970_datetime = datetime.datetime(1970, 1, 1)
        op_day = (diary.operation_time - unix_1970_datetime).days
        topic_day = (oldest_topic_create_time - unix_1970_datetime).days
        if op_day > topic_day:
            show_operation_day_info = False
        else:
            show_operation_day_info = True
    else:
        show_operation_day_info = True
    raw_hospital = diary.raw_hospital
    raw_doctor = diary.raw_doctor

    diary_user = UserService.get_user_by_user_id(diary.user_id)
    new_diary_convert_user = UserConvertService.get_user_info_by_user_id(diary.user_id)  # 新的日记本用户信息处理逻辑
    _diary_all_tags = diary.all_tags
    area_tags = diary.get_tags_from_tags_by_types(_diary_all_tags, [TAG_TYPE.CITY])
    city_tag_id = None
    if area_tags:
        city_tag_id = area_tags[0].id

    # 日记本项目类型的标签
    diary_item_tags = diary.get_tags_from_tags_by_types(
        _diary_all_tags,
        [TAG_TYPE.BODY_PART, TAG_TYPE.BODY_PART_SUB_ITEM, TAG_TYPE.ITEM_WIKI]
    )
    diary_item_tags = [tag.to_dict() for tag in diary_item_tags]
    diary_data = {
        'id': id,
        'author_id': author_id,
        'service_id': service_id,
        'view_num': view_num,
        'images': images,
        'topic_num': topic_num,
        'tags': tags,
        'city_tag_id': city_tag_id,
        'operation_info': operation_info,
        'pre_operation_image_num': pre_operation_image_num,
        'post_operation_image_num': post_operation_image_num,
        'online_service_id': online_service_id,
        'recent_topic_content': recent_topic_content,
        'cover_post': get_full_path(diary.post_operation_image) if diary.post_operation_image else '',
        'ownership': ownership,
        'is_following': is_following,
        'is_favored': is_favored,
        'is_voted': is_voted,
        'reply_num_for_diary': reply_num_for_diary,
        'vote_num': vote_num,
        'is_fake_operation_time': is_fake_operation_time,
        'second_tag_id': second_tag_id,
        'show_operation_day_info': show_operation_day_info,
        'raw_hospital': raw_hospital,
        'raw_doctor': raw_doctor,
        'city': diary_user.city_name,
        'user_nickname': diary_user.nickname,
        'user_portrait': diary_user.portrait,
        'user_id': diary.user_id,
        'membership_level': diary_user.membership_level,
        'level_icon': diary_user.level_icon,
        'membership_icon': diary_user.membership_icon,
        'pre_operation_images': diary.pre_operation_images_info,
        'title': diary.title or '',
        'created_time': get_timestamp_or_none(diary.created_time),
        'doctor_id': diary.doctor_id or '',
        'hospital_id': diary.hospital_id or '',
        'sticky_post': diary.sticky_post,
        'favord_count': favord_count,
        'tag_info': tag_data,
        'is_author': is_author,
        'count': diary_count,
        'order_id': diary.order_id,
        'polymer_tag': tag_data,
        'all_comments_num': topic_reply_count.get('reply_num', 0) + reply_num_for_diary,
        'group_diary_num': int(group_diary_num * group_diary_factor),
        'is_login': is_login,
        'share_count': diary.share_num,
        'diary_item_tags': diary_item_tags,
        'new_diary_convert_user': new_diary_convert_user,  # 新增字段，用户信息整合
    }

    if need_order:
        diary_data['order_id'] = diary.order_id

    return diary_data


@bind_context('diary/diary_info', deprecated=True)
# @bind_context("topic/diary/get", deprecated=True)
def get_diary_info(ctx, id, admin=False, without_activity_topics=False,
                   simple_data=False, topic_sort=TOPIC_ORDER_IN_DIARY.LATEST, add_view=True):
    """get diary infomation.

    .. changelog. add param without_activity_topics at 5.5
                  add param simple_data at 5.6 return diary simple data

    TODO: delete without_activity_topics when mweb support activities

    NOTE: deprecated @7.0.0

    """
    user = get_user_from_context(ctx)
    try:
        diary = Diary.objects.get(pk=id, is_online=True)
    except Diary.DoesNotExist:
        return None

    if simple_data:
        return {
            'result': diary.simple_data()
        }

    diary_user = UserService.get_user_by_user_id(diary.user_id)

    diary_data = diary.get_diary_info(user=user)
    diary_data.update({
        'topics': [],
        'pre_operation_images': diary.pre_operation_images_info,
        'cover_pre': get_full_path(diary.pre_operation_image) if diary.pre_operation_image else '',
        'cover_post': get_full_path(diary.post_operation_image) if diary.post_operation_image else '',
        'user_id': diary.user_id,
        'user_portrait': diary_user.portrait,
        'user_nickname': diary_user.nickname,
        'city': diary_user.city_name,
        'wiki_list': diary.wiki_info,
        'view_num': diary.view_num,
        'tags': diary.tags_new_era,
        'membership_level': diary_user.membership_level,
        'zone_list': [],
    })

    if admin:
        topic_filter = Q()
    else:
        topic_filter = Q(flag=PROBLEM_FLAG_CHOICES.NORMAL, is_online=True)

    if without_activity_topics:
        topic_filter &= Q(activity__isnull=True)

    if topic_sort == TOPIC_ORDER_IN_DIARY.OLDEST:
        order_by = 'created_time'
    else:
        order_by = '-created_time'

    topics = diary.topics.filter(topic_filter).order_by(order_by)
    topics_id = list(diary.topics.filter(topic_filter).order_by('created_time').values_list('id', flat=True))

    topic_ids = []

    topics_data = topic_list_manager.get_list_data_by_topic_objs(topics, viewer_user_id=user.id)
    for t_data in topics_data:
        problem = t_data['problem']

        if diary.operation_time:
            interval = datetime.datetime.fromtimestamp(int(problem['created_time'])) - diary.operation_time
            if interval.days >= 0:
                interval = interval.days + 1
            else:
                interval = ''
        else:
            interval = ''

        user = t_data['user']
        topic_data = {
            'interval': interval,
            'user_nickname': user['user_name'],
            'user_portrait': user['portrait'],
            'membership_level': user['membership_level'],
        }

        topic_id = problem['id']

        try:
            # topic.id不在topics_id中或者topics_id为空的情况
            topic_data['diary_num'] = topics_id.index(topic_id) + 1
        except:
            topic_data['diary_num'] = 1

        topic_data.update(problem)

        topic_ids.append(topic_id)

        diary_data['topics'].append(topic_data)

    if add_view:
        topic_view_increase_num(topic_ids)

        # keep original logic
        diary_view_increase_num([id])
        diary_view_increase_num([id])

    return diary_data


@bind('diary/get_num_and_cover_by_ids')
def diary_num_and_cover(ids):
    """
    :param ids: diary_id list
    :return:
    [
        {
            'id': 1,
            'data': {
                'diary_amount': 2,
                'view_num': 3,
                'vote_num': 4,
                'cover': xxx
            }
        }
    ]
    """
    if len(ids) == 0:
        return []

    diaries = Diary.objects.filter(id__in=ids)
    result = []
    for diary in diaries:
        diary_amount = diary.topic_num
        view_num = diary.view_num
        vote_num = diary.vote_num
        cover = diary.cover
        data = {
            'diary_amount': diary_amount,
            'view_num': view_num,
            'vote_num': vote_num,
            'cover': cover,
        }
        d = {
            'id': diary.id,
            'data': data,
        }
        result.append(d)
    return result


@bind('diary/get_diary_info_by_ids')
def get_diary_info_by_ids(ids, simple=False):
    """
    :param ids: diary_id list
    simple:
    :return:
    [
        {
            'id': 1,
            'data': diary_info 一个大字典
        }
    ]
    """
    if len(ids) == 0:
        return []

    diaries = Diary.objects.filter(id__in=ids)
    result = []
    for diary in diaries:
        d = diary.get_diary_info(simple=simple)
        data = {
            'id': diary.id,
            'data': d,
        }
        result.append(data)
    return result


@bind_context('diary/get_index_banner_diary_by_ids')
def get_index_banner_diary_by_ids(ctx, diary_ids):
    user = get_user_from_context(ctx)
    diary_infos = diary_list_manager.get_diary_list_by_diary_ids(diary_ids, user.id)
    _info = {}

    for diary_info in diary_infos:
        _info.update({str(diary_info['diary_id']): {
            'vote_num': diary_info['vote_num'],
            'reply_num': diary_info['reply_num'],
            'view_num': diary_info['view_num'],
            'user_portrait': diary_info['user_portrait'],
            'user_id': diary_info['user_id'],
            'user_nickname': diary_info['user_nickname'],
            'is_voted': diary_info['is_voted'],
        }})

    return _info


@bind('diary/tags')
def get_diary_tags(diary_id):
    tags = DiaryTag.objects.filter(diary_id=diary_id).values_list('tag_id', flat=True)
    return list(tags)


@bind('diary/tags/v1')
def get_diary_tags_v1(diary_ids):
    mp = defaultdict(list)
    for obj in DiaryTag.objects.filter(diary_id__in=diary_ids).iterator():
        mp[obj.diary_id].append(obj.tag_id)

    rs = []
    for k, v in mp.items():
        rs.append({
            'id': k,
            'tags': v
        })

    return rs


@bind("diary/mip/detail")
def get_diary_info_for_mip(diary_id):
    """
    mip 百度合作，日记本详情页信息
    :param diary_id:
    :return:
    """
    try:
        _ = BDDiary.objects.get(diary_id=diary_id, is_submit=True)  # 防止接口使用非推荐的id获取数据，校验 diary_id 需在 bd_diary 表中。
        diary = Diary.objects.get(pk=diary_id, is_online=True)
    except (BDDiary.DoesNotExist, Diary.DoesNotExist):
        return gen(CODES.DIARY_NOT_FOUND)

    operation_info = diary.operation_info()
    diary_user = UserService.get_user_by_user_id(diary.user_id)
    latest_topic = diary.latest_topic
    result = {
        "id": diary.id,
        "created_time": get_timestamp_or_none(diary.created_time),
        "update_time": get_timestamp_or_none(diary.last_topic_add_time),
        "operation_info": operation_info,
        "tags": diary.tags_new_era,
        "topic_num": diary.topic_num,
        "view_num": diary.view_num,
        "reply_num": diary.reply_num,
        "vote_num": diary.vote_num,
        'pre_operation_images': diary.pre_operation_images_info,
        "first_topic_id": latest_topic and latest_topic.id or 0,
        "first_topic_content": latest_topic and escape(latest_topic.answer) or "",
        "cover": diary.cover,
        "user": {
            "user_id": diary.user_id,
            "nickname": diary_user.nickname,
            "portrait": diary_user.portrait,
        }
    }
    return result


@bind('diary/simple_data')
def get_diary_simple_data(diary_id, for_doctor=False):
    """
    获取diary的一些基础信息
    """

    query = Q(pk=diary_id)
    if not for_doctor:
        query &= Q(is_online=True)
    try:
        diary = Diary.objects.get(query)
    except Diary.DoesNotExist:
        return None

    if diary.is_fake_operation_time or diary.operation_time is None:
        is_fake_operation_time = True
    else:
        is_fake_operation_time = False
    tag_data = GoodsService.get_tag_data(diary.all_tags)
    diary_count = Problem.objects.filter(user_id=diary.user_id, is_online=True).count()
    info = {'operation_timestamp': get_timestamp(diary.operation_time)}

    return {
        'title': diary.title,
        'diary_id': diary.id,
        'author_id': diary.user_id,
        'service_id': diary.service_id,
        'order_id': diary.order_id,
        'view_num': diary.view_num,
        'images': diary.cover,
        'topic_num': diary.topic_num,
        'tag_id': tag_data.get('tag_id', None),
        'operation_info': info,
        'is_fake_operation_time': is_fake_operation_time,
        'tags': diary.tags_new_era,
        'diary_total': diary_count,
    }


@bind("diary/share")
def diary_share(diary_id):
    try:
        diary = Diary.objects.get(pk=diary_id, is_online=True)
    except Diary.DoesNotExist:
        return False

    diary.share_num += 1
    diary.save()

    return True


@bind("diary/new_share_data")
def get_diary_new_share_data_by_id(diary_id):
    try:
        diary = Diary.objects.get(pk=diary_id, is_online=True)
    except Diary.DoesNotExist:
        return gen(CODES.DIARY_NOT_FOUND)

    # 用户相关数据
    user = UserService.get_user_by_user_id(diary.user_id)
    # 美购相关数据
    service_id = diary.service_id
    service_info = (service_id and GoodsService.get_diary_show_info_by_service_ids([service_id]) or {}).get(service_id, {})

    lastest_topic = diary.latest_topic
    diary_operation_time = diary.operation_time or diary.created_time
    lastest_topic_operate_time = lastest_topic and lastest_topic.operation_date or None

    _data = {
        "diary_id": diary.id,
        "content": lastest_topic and lastest_topic.content or "",
        "cover": diary.cover,
        "diary_operation_timestamp": int(diary_operation_time.strftime("%s")),
        "topic_operation_timestamp": lastest_topic_operate_time and int(lastest_topic_operate_time.strftime("%s")) or 0,
        "city_name": service_info.get("city_name", "") or user.city_name or "",
        "vote_num": diary.vote_num,
        "tags": diary.tags_new_era,
        "user_name": user.nickname,
    }
    return _data


@bind("diary/get_diary_id_by_audit_status")
def get_diary_id_by_audit_status(diary_id, near_status):
    """
    通过日记id，审核状态获取相近的日记本id

    if status == done:
        if au_cnt == 1:
            待初审

        elif au_cnt > 1:
            待再审

    elif status == 初审:
      待初审

    elif status == 再审:
      待再审

    :param diary_id:
    :param near_status:
    :return:
    """
    _nt = namedtuple("DIARY_NEAR_STATUS", ['PREV', "AFTER"])
    DIARY_NEAR_STATUS = _nt(PREV=0, AFTER=1)
    base_query = Q(is_online=True)

    diary_obj = Diary.objects.filter(pk=diary_id).first()
    if diary_obj:
        audit_status = diary_obj.audit_status
        content_level = diary_obj.content_level

        if audit_status == DIARY_AUDIT_STATUS.AUDITED:
            _diary_check_count = DiaryCheck.objects.filter(diary_id=diary_id).count()
            if _diary_check_count == 1:
                base_query &= Q(audit_status=DIARY_AUDIT_STATUS.UNAUDITED)
            elif _diary_check_count > 1:
                # 待再审 + 当前日记本审核的内容等级
                base_query &= Q(audit_status=DIARY_AUDIT_STATUS.REAUDIT, content_level=content_level)

        elif audit_status == DIARY_AUDIT_STATUS.UNAUDITED:
            base_query &= Q(audit_status=DIARY_AUDIT_STATUS.UNAUDITED)

        elif audit_status == DIARY_AUDIT_STATUS.REAUDIT:
            base_query &= Q(audit_status=DIARY_AUDIT_STATUS.REAUDIT)

        if near_status == DIARY_NEAR_STATUS.PREV:  # 前一条
            filter_query = base_query & Q(pk__lt=diary_id)
            jump_diary_id = Diary.objects.filter(filter_query).order_by("-id").values_list("id", flat=True).first()

        elif near_status == DIARY_NEAR_STATUS.AFTER:  # 后一条
            filter_query = base_query & Q(pk__gt=diary_id)
            jump_diary_id = Diary.objects.filter(filter_query).values_list("id", flat=True).first()

        else:
            jump_diary_id = 0
    else:
        jump_diary_id = 0

    _diary_id = jump_diary_id and jump_diary_id or 0

    return _diary_id
