from .recall import *
from .realtime_filter import *
from .rerank import *
import json
import logging
import traceback
from collections import Counter
from libs.cache import redis_client2
from libs.es import es_index_adapt, es_msearch
from libs.error import logging_exception
from django.conf import settings


class PersonalizedFeed(object):
    FLOW = ('recaller', 'realtime_filter', 'reranker')

    def __init__(self, device_id, feed_type, offset=0, size=10):
        self.device_id = device_id
        self.feed_type = feed_type
        self.offset = offset
        self.size = size
        self.recall_ = None

    def get_feed(self):
        for flow in self.FLOW:
            getattr(self, flow)

        return self.recall_


class DiaryPersonalizedFeed(PersonalizedFeed):
    recaller = DiaryRecaller()
    realtime_filter = DiaryRealtimeFilter()
    reranker = DiaryReranker()


class QuestionPersonalizedFeed(PersonalizedFeed):
    FLOW = ('recaller',)
    recaller = QuestionRecaller()


class ArticlePersonalizedFeed(PersonalizedFeed):
    FLOW = ('recaller',)
    recaller = ArticleRecaller()


class AnswerPersonalizedFeed(PersonalizedFeed):
    FLOW = ('recaller',)
    recaller = AnswerRecaller()


class FeedDispatch(object):
    FEED = {
        'diary': DiaryPersonalizedFeed,
        'question': QuestionPersonalizedFeed,
        'answer': AnswerPersonalizedFeed,
        'article': ArticlePersonalizedFeed
    }

    @classmethod
    def dispatch(cls, feed_type):
        return cls.FEED[feed_type]


def get_feed(device_id, feed_type, offset, size):
    feed = FeedDispatch.dispatch(feed_type)(device_id, feed_type, offset, size)
    return feed.get_feed()


def get_user_service_portrait2_redis(cl_id, n=4):
    """
    :param cl_id: 设备id
    :param n: 获取画像的前n个tag
    :return: list
    """
    try:
        cl_id_portrait_key = "user:service_portrait_tags2:cl_id:" + str(cl_id)
        if redis_client2.exists(cl_id_portrait_key):
            user_portrait = redis_client2.hgetall(cl_id_portrait_key)
        else:
            new_user_service_portrait_tags_key = "user:service_coldstart_tags2"
            user_portrait = redis_client2.hgetall(new_user_service_portrait_tags_key)
        user_portrait = {int(tag): float(score) for tag, score in user_portrait.items()}
        top_n = Counter(user_portrait).most_common(n)
        top_n_tags = [int(tag_info[0]) for tag_info in top_n]
        logging.info("user_service_portrait2_redis", cl_id, top_n_tags)
        return top_n_tags
    except:
        logging.error("user_service_portrait2_redis", traceback.format_exc())
        logging_exception()
        return list()


def get_user_latest_action_tag(cl_id, n=1):
    """
    :param cl_id: 设备id
    :param n: 获取用户最新行为对应的n个tag
    :return: list
    """
    try:
        device_latest_action_key = "device:latest:action:tags:" + str(cl_id)
        if redis_client2.exists(device_latest_action_key):
            n_tags = json.loads(redis_client2.get(device_latest_action_key))[:n]
            logging.info("user_latest_action_tag", cl_id, n_tags)
            return n_tags
        else:
            return list()
    except:
        logging.error("user_latest_action_tag", traceback.format_exc())
        logging_exception()
        return list()


def fetch_diary_by_user_portrait(device_id, current_city_tag_id, read_list, size):
    """
    :param device_id: 设备id
    :param current_city_tag_id: 设备当前的城市tag id
    :param read_list: 召回的过滤卡片list
    :param size: 召回个数
    :return:
    """
    try:
        # 获取用户画像以及最新行为对应的tag
        user_tags = get_user_service_portrait2_redis(device_id, 4)
        user_latest_tag = get_user_latest_action_tag(device_id, 1)
        if user_latest_tag:
            user_tags.extend(user_latest_tag)
        # 计算每个tag召回日记卡片的数量
        card_size = list()
        card_id_list = list()
        tag_size = len(user_tags)
        if tag_size:
            if tag_size == 4:
                card_size = [3, 3, 3, 3]
            elif tag_size == 5:
                card_size = [3, 3, 2, 2, 2]
            else:
                card_size = list()
        if card_size:
            query_body = ""
            diary_index_name = es_index_adapt(index_prefix=settings.ES_INDEX_PREFIX, doc_type="diary", rw="read")
            diary_header_dict = {'index': diary_index_name, 'type': "diary"}
            sort_list = [
                {'_script': {
                    'lang': settings.ES_SCRIPT_LANG,
                    'script_file': 'sort_diary-recommend',
                    'type': 'number',
                    'params': {
                        'user_city_tag_id': current_city_tag_id,
                    },
                    'order': 'desc',
                    '_cache': True,
                }}
            ]
            sort_list += [
                {
                    "_script": {
                        "order": "desc",
                        "script": {
                            "inline": "_score+doc['offline_score'].value"
                        },
                        "type": "number"
                    }
                },
                {'last_update_time': {'order': 'desc'}},
                '_score',
            ]
            for i in range(tag_size):
                q = dict()
                q['query'] = {"bool": {
                    "filter": [{"term": {"closure_tag_ids": user_tags[i]}}, {"term": {"is_online": True}},
                               {"term": {"has_cover": True}}, {"term": {"is_sink": False}},
                               {"terms": {"content_level": [5, 4, 3.5, 3]}}]}}
                q['size'] = 10
                q["_source"] = {
                    "includes": ["id"]
                }
                q['query']['bool']['must_not'] = [{"term": {"is_operate": True}}]
                if len(read_list) > 0:
                    q['query']['bool']['must_not'].append({"terms": {"id": read_list}})
                q['sort'] = sort_list
                query_body += "{}\n{}\n".format(json.dumps(diary_header_dict), json.dumps(q))
            res_diary = es_msearch(query_body)
            logging.info("fetch_diary_by_user_portrait", device_id, query_body, res_diary)
            if res_diary:
                tag_index = 0
                for tag in res_diary['responses']:
                    tag_diary_count = 0
                    for diary in tag['hits']['hits']:
                        diary_id = diary['_source']['id']
                        if diary_id not in card_id_list:
                            card_id_list.append(int(diary_id))
                            tag_diary_count += 1
                            if tag_diary_count == card_size[tag_index]:
                                break
                    tag_index += 1
                    card_size[tag_index+1] += (card_size[tag_index]-tag_diary_count)
            result = card_id_list[:size]
            logging.info("fetch_diary_by_user_portrait", device_id, result)
            return result
        else:
            return list()
    except:
        logging.error("fetch_diary_by_user_portrait", traceback.format_exc())
        logging_exception()
        return list()

