# coding=utf-8
from __future__ import unicode_literals, absolute_import, print_function

from datetime import datetime, timedelta

from django.conf import settings
from django.utils.html import strip_tags
from django.views.decorators.cache import cache_page
from gm_types.gaia import (
    TAG_TYPE,
    ZONE_TYPE_V2,
    OPTIMIZE_WIKI_TYPE,
    ZONE_DETAIL_SORT_TYPE,
    ZONE_DETAIL_TAB_TYPE,
    DIARY_ORDER_TYPE,
    SERVICE_ORDER_TYPE,
    TOPIC_TYPE,
    QUESTION_ORDER_TYPE, PROBLEM_ORDER_TYPE
)

from api.manager.tag_manager import get_tag_by_id
from api.models import Zone, UserTagRelation, TagWiki, Service
from api.tool.user_tool import get_user_from_context
from rpc.context import get_rpc_remote_invoker
from rpc.decorators import bind, bind_context
from rpc.tool.error_code import CODES, gen
from rpc.tool.protocol import gm_protocol
from search.utils.diary import filter_diary
from search.utils.question import filter_qustion_v1, filter_question
from search.utils.service import filter_service
from search.utils.topic import filter_topic
from statistic.tasks import add_diary_pv, add_problem_pv
# from talos.manager.topic import topic_list_manager


@cache_page(120)
@bind('api/zone/community_activity')
def get_community_activity():
    """
    获取社区精选活动
    """
    zones = Zone.objects.select_related('tag__id', 'tag__name').filter(
        is_online=True,
        is_communityhome=True,
        tag__tag_type=TAG_TYPE.FREE
    ).order_by("home_rank")

    result = []
    for zone in zones:
        result.append({
            "icon": zone.icon_image,
            "tag_id": zone.tag.id,
            "tag_name": zone.tag.name,
        })

    return result


@cache_page(120)
@bind('api/zone/community_hot')
def get_community_hot():
    """
    获取社区热门圈子
    """

    zones = Zone.objects.select_related('tag__id', 'tag__name').filter(
        is_online=True,
        is_communityhome=True,
        tag__tag_type__in=[
            TAG_TYPE.BODY_PART,
            TAG_TYPE.BODY_PART_SUB_ITEM,
            TAG_TYPE.ITEM_WIKI
        ]
    ).order_by("home_rank")

    result = []
    for zone in zones:
        result.append({
            "icon": zone.icon_image,
            "tag_id": zone.tag.id,
            "tag_name": zone.tag.name,
        })

    return result


def _get_tabs_by_zone(tag, zone, tab_type):
    """NOTE: maybe move to backend

    weight as the tab sort reference.
        discuss 1
        diary   2
        service 3
    """
    topic_tab = {
        'name': u'讨论',
        'tab_type': ZONE_DETAIL_TAB_TYPE.DISCUSS,
        'selected': ZONE_DETAIL_TAB_TYPE.DISCUSS == tab_type,
        # only show create topic button when discuss tab selected
        'show_discuss_btn': True and tag.free_to_add,
        'show_question_btn': False,
        'weight': zone.topic_tab_position if zone else 1,
    }

    diary_tab = {
        'name': u'日记本',
        'tab_type': ZONE_DETAIL_TAB_TYPE.DIARY,
        'selected': ZONE_DETAIL_TAB_TYPE.DIARY == tab_type,
        'show_discuss_btn': False,
        'show_question_btn': False,
        'weight': zone.diary_tab_position if zone else 2,
    }

    service_tab = {
        'name': u'美购',
        'tab_type': ZONE_DETAIL_TAB_TYPE.SERVICE,
        'selected': ZONE_DETAIL_TAB_TYPE.SERVICE == tab_type,
        'show_discuss_btn': False,
        'show_question_btn': False,
        'weight': zone.service_tab_position if zone else 3,
    }

    question_tab = {
        'name': u'问答',
        'tab_type': ZONE_DETAIL_TAB_TYPE.QUESTIONS,
        'selected': ZONE_DETAIL_TAB_TYPE.QUESTIONS == tab_type,
        'show_discuss_btn': False,
        'show_question_btn': True,
        'weight': zone.qa_tab_position if zone else 4,
    }

    tabs = [topic_tab, diary_tab, service_tab, question_tab]

    if zone and not zone.show_topic_tab:
        tabs.remove(topic_tab)

    if zone and not zone.show_diary_tab:
        tabs.remove(diary_tab)

    if zone and not zone.show_service_tab:
        tabs.remove(service_tab)

    if zone and not zone.show_qa_tab:
        tabs.remove(question_tab)

    # at least one tab show be displayed, if not raise exception
    if not tabs:
        return gen(CODES.DATA_SOURCE_NOT_CORRECT)

    tabs = sorted(tabs, key=lambda x: x['weight'])

    # mark tab as selected if only one tab exist
    if len(tabs) == 1 or not filter(lambda x: x['selected'], tabs):
        tabs[0]['selected'] = True

    return {'tabs': tabs}


def _get_sort_type_by_zone(zone):
    """return sort types.

    NOTE: maybe move to backend
    """
    sort_types = [
        {
            'name': u'按热度排序',
            'sort_type': ZONE_DETAIL_SORT_TYPE.HOT,
        },
        {
            'name': u'按时间排序',
            'sort_type': ZONE_DETAIL_SORT_TYPE.TIME,
        },
    ]

    # NOTE: only for two kinds of sort types
    if zone and zone.default_sort_type != sort_types[0]['sort_type']:
        sort_types = sort_types[::-1]

    return {'sort_types': sort_types}


def _get_desc_and_showmore_btn(tag_obj):
    # try to get wiki abstract
    # tagwiki -> itemwiki -> optimizewiki -> type=abstract
    tagwikis = TagWiki.objects.filter(tag=tag_obj)
    if not tagwikis:
        return

    tagwiki = tagwikis.first()
    if not tagwiki:
        return

    # validate data
    wiki = tagwiki.itemwiki
    if not wiki:
        return

    if not hasattr(wiki, 'optimizewiki'):
        return

    abstract = wiki.optimizewiki.filter(
        type=OPTIMIZE_WIKI_TYPE.ABSTRACT,
        is_delete=False
    )
    if not abstract:
        return

    abstract = abstract.first()
    content = strip_tags(abstract.content)
    if content:
        show_content = len(content) > 50 and content[:50] + '...' or content
        return {
            'showmore_url': gm_protocol.get_wiki_detail(wiki.id, wiki.item_name),
            # only show up to 50 chars
            'desc': show_content + '<ems>【查看百科】</ems>',
            # NOTE: original_desc和url_info为冗余字段 用于返回原始数据
            'original_desc': content,
            'url_info': {
                'type': 'wiki',
                'args': {'wiki_id': wiki.id, 'wiki_name': wiki.item_name},
            },
        }

    # hmmm...
    return


def _get_tag_obj(tag_id):
    tag_obj = get_tag_by_id(tag_id)
    if not tag_obj or not tag_obj.is_online:
        return gen(CODES.TAG_NOT_FOUND)

    return tag_obj


@cache_page(120)
@bind('api/zone/activity_detail')
def get_zone_activity_detail(zone_id):
    try:
        z = Zone.objects.get(id=zone_id)
    except Zone.DoesNotExist:
        return gen(CODES.ZONE_NOT_FOUND)

    return {
        # safe guard fuck.
        'title': z.title or z.tag.name,
        'activity_detail': z.campaign_descr or z.campaign_descr_richtext or z.sentence_descr,
    }


@bind_context('api/zone/info')
def zone_info(ctx, tag_id, tab_type=None):
    """get zone basic info.

    added at 6.6.0
    pm: yang long ying
    wiki: http://wiki.gengmei.cc/pages/viewpage.action?pageId=3358920
    """
    user = get_user_from_context(ctx)
    tag_obj = _get_tag_obj(tag_id)

    data = {
        # always be the tag's name
        'title': tag_obj.name,

        # default as tag's icon or default zone icon
        # TODO: ask PM for default icon
        'icon': tag_obj.icon_url or settings.DEFAULT_ZONE_ICON,

        'followed': user and UserTagRelation.user_follow_tag(user.id, tag_obj.id) or False,

        # if configured as activity zone, get fron zone.sentence_descr
        # else if tag connect with wiki, get wiki's abstract
        'desc': '',

        'zone_type': ZONE_TYPE_V2.NORMAL_ZONE,

        'tag': {
            'tag_id': tag_obj.id,
            'name': tag_obj.name,
        },
    }

    zone = None

    def _get_wiki_info(tag_obj, data):
        # NOTE: data has side effect
        _extra_data = _get_desc_and_showmore_btn(tag_obj)
        if _extra_data:
            data.update(_extra_data)

    if hasattr(tag_obj, 'zone') and tag_obj.zone.is_online:
        # for activity zone
        zone = z = tag_obj.zone

        # if configured as zone get icon, desc from zone
        data['icon'] = z.icon_image

        if z.zone_type == ZONE_TYPE_V2.ACTIVITY_ZONE:
            # only activity zone show sentence_descr
            data['desc'] = z.sentence_descr
            data['sentence_descr'] = z.sentence_descr

            # populate zone_type banner and show more button for activity zone
            data['zone_type'] = z.zone_type

            # get banner from campaign_banner
            data['banner'] = z.campaign_banner

            if z.campaign_descr or z.campaign_descr_richtext:
                # NOTE: original_desc和url_info为冗余字段，用于返回原始数据
                data['original_desc'] = data['desc']
                data['url_info'] = {'type': 'activity_detail', 'args': {'zone_id': z.id}}
                # only show more with campaign_descr not none or ''
                desc = data['desc']
                desc = len(desc) > 50 and desc[:50] + '...' or desc
                data['desc'] = desc + u'<ems>【查看详情】</ems>'
                data['showmore_url'] = gm_protocol.get_zone_activity_detail(z.id)

        else:
            # add desc and showmore_btn for wiki
            _get_wiki_info(tag_obj, data)

    elif hasattr(tag_obj, 'zone'):
        # zone not online
        return gen(CODES.ZONE_NOT_FOUND)

    else:
        # add desc and showmore_btn for wiki
        _get_wiki_info(tag_obj, data)

    data.update(_get_sort_type_by_zone(zone))
    data.update(_get_tabs_by_zone(tag_obj, zone, tab_type))

    return data


def _get_start_num_and_ids(model_objs, fk_n, start_num):
    # for get_tab_data
    hot_up_limit = 3
    hot_ids = model_objs.filter(
        deleted=False
    ).order_by('rank').values_list(fk_n, flat=True)[:hot_up_limit]

    # check if start_num != 0
    if start_num != 0:
        # adjust start up to -3
        delta = start_num - len(hot_ids)
        if delta < 0:
            start_num = 0
            hot_ids = hot_ids[start_num:]
        else:
            start_num = delta
            hot_ids = []

    return start_num, list(hot_ids)


@bind_context('api/zone/get_tab_data')
def get_zone_tab_data(ctx, tag_id, start_num, sort_type, tab_type):
    user = get_user_from_context(ctx)
    rpc_client = get_rpc_remote_invoker()

    hot_topic_ids = []
    hot_diary_ids = []
    hot_question = []

    tag_obj = _get_tag_obj(tag_id)
    if (
                        sort_type == ZONE_DETAIL_SORT_TYPE.HOT and
                    hasattr(tag_obj, 'zone') and tag_obj.zone.is_online
    ):
        zone = tag_obj.zone

        if tab_type == ZONE_DETAIL_TAB_TYPE.DIARY:
            fk_n = 'diary_id'
            start_num, hot_diary_ids = _get_start_num_and_ids(zone.overheaddiary_set, fk_n, start_num)

        if tab_type == ZONE_DETAIL_TAB_TYPE.DISCUSS:
            fk_n = 'topic_id'
            start_num, hot_topic_ids = _get_start_num_and_ids(zone.overheadtopic_set, fk_n, start_num)

        if tab_type == ZONE_DETAIL_TAB_TYPE.QUESTIONS:
            hot_question = rpc_client['qa/question/zone_recommend'](zone_id=zone.id).unwrap()

    tab_type_filter = {
        (ZONE_DETAIL_TAB_TYPE.DIARY, ZONE_DETAIL_SORT_TYPE.HOT): (
            filter_diary,
            {
                'sort_type': DIARY_ORDER_TYPE.HOT,
                'offset': start_num,
                'size': 10,
                'filters': {'tag_ids': [tag_id]},
            },
            'diary_ids',
        ),

        (ZONE_DETAIL_TAB_TYPE.DISCUSS, ZONE_DETAIL_SORT_TYPE.HOT): (
            filter_topic,
            {
                'filters': {
                    'tag_ids': [tag_id],
                    'topic_types': (TOPIC_TYPE.ASK, TOPIC_TYPE.TOPIC),
                },
                'offset': start_num,
                'size': 10,
                'sort_type': PROBLEM_ORDER_TYPE.RANKING_POPULARITY,
            },
            'topic_ids',
        ),

        (ZONE_DETAIL_TAB_TYPE.SERVICE, ZONE_DETAIL_SORT_TYPE.HOT): (
            filter_service,
            {
                'sort_type': SERVICE_ORDER_TYPE.ORDER_HIGHEST_SALES,
                'offset': start_num,
                'size': 10,
                'filters': {'tag_ids': [tag_id]},
            },
            'service_ids',
        ),

        (ZONE_DETAIL_TAB_TYPE.DIARY, ZONE_DETAIL_SORT_TYPE.TIME): (
            filter_diary,
            {
                'sort_type': DIARY_ORDER_TYPE.LAST_UPDATE_TIME,
                'offset': start_num,
                'size': 10,
                'filters': {'tag_ids': [tag_id]},
            },
            'diary_ids',
        ),

        (ZONE_DETAIL_TAB_TYPE.DISCUSS, ZONE_DETAIL_SORT_TYPE.TIME): (
            filter_topic,
            {
                'sort_type': PROBLEM_ORDER_TYPE.LATEST_CREATED,
                'offset': start_num,
                'size': 10,
                'filters': {
                    'tag_ids': [tag_id],
                    'topic_types': (TOPIC_TYPE.ASK, TOPIC_TYPE.TOPIC),
                },
            },
            'topic_ids',
        ),

        (ZONE_DETAIL_TAB_TYPE.SERVICE, ZONE_DETAIL_SORT_TYPE.TIME): (
            filter_service,
            {
                'sort_type': SERVICE_ORDER_TYPE.DISTANCE,
                'offset': start_num,
                'size': 10,
                'filters': {'tag_ids': [tag_id]},
            },
            'service_ids',
        ),

        (ZONE_DETAIL_TAB_TYPE.QUESTIONS, ZONE_DETAIL_SORT_TYPE.HOT): (
            filter_qustion_v1,
            {
                'sort_type': QUESTION_ORDER_TYPE.HOT,
                'offset': start_num,
                'size': 10,
                'tag_id': tag_id,
            },
            "questions",
        ),

        (ZONE_DETAIL_TAB_TYPE.QUESTIONS, ZONE_DETAIL_SORT_TYPE.TIME): (
            filter_qustion_v1,
            {
                'sort_type': QUESTION_ORDER_TYPE.DOCTOR_LASTEST,
                'offset': start_num,
                'size': 10,
                'tag_id': tag_id,
            },
            "questions",
        )
    }

    tup = (tab_type, sort_type)
    if tup not in tab_type_filter:
        return gen(CODES.PARAMS_INVALID)

    result = []

    (filter_func, params, id_name) = tab_type_filter.get(tup)
    ids = filter_func(**params)[id_name]

    if tab_type == ZONE_DETAIL_TAB_TYPE.DIARY:
        ids = hot_diary_ids + ids
        res = ctx.rpc_remote['diary/list_data_get_by_ids'](ids=ids).unwrap()
        result = res['diaries']
        add_diary_pv.delay(ids)

    elif tab_type == ZONE_DETAIL_TAB_TYPE.DISCUSS:
        ids = hot_topic_ids + ids
        res = rpc_client['topic/list_data_get_by_ids'](ids=ids).unwrap()
        result = res['topics']
        add_problem_pv(ids)

    elif tab_type == ZONE_DETAIL_TAB_TYPE.SERVICE:
        objs_dict = {o.id: o for o in Service.objects.filter(id__in=ids)}
        for i in ids:
            if i in objs_dict:
                # TODO 这里的对外接口已经被废弃了，本次改版不需要处理这里的代码

                obj = objs_dict[i]
                info = obj.get_data_for_list_with_sell_info(user=user)
                info['operate_tag'] = obj.operate_tag()
                from api.manager import coupon_manager
                info["coupon_gifts"] = coupon_manager.get_top_coupon_gift_infos(service_id=obj.id)
                result.append(info)

    elif tab_type == ZONE_DETAIL_TAB_TYPE.QUESTIONS:
        result = rpc_client['qa/question/tab_choice'](question_pks=ids).unwrap()
        result = hot_question + result
    return result


def get_question_and_diary(zone_ids, offset=0, count=10, user=None):
    """
    获取圈子中人气最高的问题，日记本；
    按照问题的回答数，日记的评论数排序；
    :param user:
    :param zone_ids:
    :param offset:
    :param count:
    :return:
    """

    r = get_rpc_remote_invoker()
    user_id = user.id if user else None
    seven_days_ago = datetime.now() - timedelta(days=7)

    # notice: tag_ids is instance of django.db.models.query.ValuesListQuerySet.
    tag_ids = Zone.objects.filter(id__in=zone_ids, is_online=True, tag__is_online=True).values_list('tag_id', flat=True)
    tag_ids = list(tag_ids)

    if not len(list(tag_ids)):
        return []

    filters = {
        'raw_tag_ids': tag_ids,
        'created_time': seven_days_ago
    }

    topic_ids = filter_topic(offset=0, size=offset + count, sort_type=PROBLEM_ORDER_TYPE.ZONE_SUGGEST,
                             filters=filters)['topic_ids']

    # topics = topic_list_manager.get_list_data_by_topic_ids(topic_ids=topic_ids, viewer_user_id=user_id)
    topics = r['topic/list_data_get_by_ids'](ids=topic_ids).unwrap()
    topics = topics.get('topics')
    questions = filter_question(offset=0, size=offset + count, sort_type=QUESTION_ORDER_TYPE.ANSWER_NUM,
                                filters=filters)['question_ids']

    question_ids = [question['question'] for question in questions]
    questions = r['qa/question/get_data_list_by_ids'](ids=question_ids).unwrap()

    questions = questions.values()

    def comparator(obj1, obj2):
        """
        :param obj1:
        :param obj2:
        :return:
        """

        def get_comment_number(obj):
            return int(obj.get('problem', {}).get('reply_num', 0)) or int(obj.get('answer_num', 0))

        def get_timestamp(obj):
            return obj.get("timestamp") or obj.get("problem", {}).get("created_time", 0)

        c1, c2 = get_comment_number(obj1), get_comment_number(obj2)
        if c1 == c2:
            return get_timestamp(obj1) > get_timestamp(obj2)

        return c1 - c2

    return sorted(topics + questions, cmp=comparator, reverse=True)[offset: offset + count]


@bind_context('api/choice/recommend_zone')
def get_choice_recommend_zone(ctx, offset=0, count=10):
    """
    首页Feed流，推荐圈子
    :param ctx:
    :param offset:
    :param count:
    :return:
    """
    user = get_user_from_context(ctx)
    zone_ids = ctx.rpc_remote['talos/recommend/get']()
    zone_ids = zone_ids.unwrap()['zone_ids']
    return get_question_and_diary(zone_ids, offset, count, user)


@bind_context('api/choice/personal_zone')
def get_choice_personal_zone(ctx, offset=0, count=10):
    """
    首页Feed流，个性化圈子
    :param ctx:
    :param offset:
    :param count:
    :return:
    """
    user = get_user_from_context(ctx)
    if not user:
        return []

    tag_ids = UserTagRelation.objects.filter(user=user).values_list('related_tag_id', flat=True)

    zone_ids = Zone.objects.filter(tag_id__in=tag_ids, is_online=True).values_list('id', flat=True)
    return get_question_and_diary(list(zone_ids), offset, count)
