# -*- coding: utf8 -*-

from __future__ import unicode_literals, absolute_import, print_function

import json

from talos.libs.utils import ObjFromDict
from talos.services.base import ServiceBase, RpcServiceModelCache
from talos.cache.base import talos_rpc_service_model_cache, talos_tagrel_cache
from talos.services.models.tag import Tag


class TagService(ServiceBase):

    _model_name = 'tag'

    __cached_layer = RpcServiceModelCache(talos_rpc_service_model_cache, _model_name)

    @classmethod
    def _cache_tag_json_data(cls, tid, jsonstr, timeout):
        cls.__cached_layer.set(tid, jsonstr, timeout)

    @classmethod
    def _cache_tag_info(cls, tag_infos):
        for i in tag_infos:
            if not Tag.validate_dict(i):
                continue

            v = json.dumps(i)
            cls._cache_tag_json_data(i['id'], v, 6 * 60 * 60)

    # @classmethod
    # def preload_tags(cls):
    #     """pre load operation tags in local cache."""
    #     r = cls.get_rpc_invoker()
    #
    #     start = 0
    #     step = 100
    #     while True:
    #         result = r['api/tag/iterator_all_tags'](
    #             start_num=start, count=step
    #         ).unwrap_or([])
    #         if not result or ((start / step) > 100):
    #             break
    #
    #         cls._cache_tag_info(result)
    #         start += step

    @classmethod
    def _get_by_ids_from_cache(cls, ids):
        cached_info = cls.__cached_layer.mget(ids)

        result = []

        missing = cached_info.pop(cls.__cached_layer.missing_k)
        if missing:
            rpc_result = cls.call_rpc('api/tag/info_by_ids', tag_ids=missing)
            if rpc_result:
                cls._cache_tag_info(rpc_result)
                result = [Tag.from_dict(i) for i in rpc_result]

        for k in cached_info:
            i = cached_info[k]
            v = json.loads(i)
            result.append(Tag.from_dict(v))

        return result

    @classmethod
    def _get_closure_by_ids_from_cache(cls, ids):
        """
        cache from tag_id to closure_tag_ids
        :param ids:
        :return:
        """
        if not ids:
            return []

        cache = talos_tagrel_cache
        key_prefix = object.__getattribute__(cache, 'prefix')  # manually handle mget keys
        keys = [key_prefix+':'+str(tag_id) for tag_id in ids]
        cached_info = talos_tagrel_cache.mget(keys)

        result = set()
        for closure_tag_ids in cached_info:
            if closure_tag_ids:  # None or empty string
                closure_tag_ids = map(int, closure_tag_ids.split(','))
                result.update(closure_tag_ids)

        # those not in cache
        for tag_id in ids:
            if tag_id not in result:
                closure_tag_info = cls.call_rpc('api/tag/closure_tags', tag_ids=[tag_id,])
                closure_tag_ids = [tag['id'] for tag in closure_tag_info]
                result.update(closure_tag_ids)
                cache.setex(str(tag_id), 6*60*60, ','.join(map(str, closure_tag_ids)))

        return list(result)

    @classmethod
    def get_tags_by_tag_ids(cls, ids):
        if not ids:
            return []

        ids = list(set(ids))
        return cls._get_by_ids_from_cache(ids)

    @classmethod
    def get_filter_options_for_filter_service_diaries(cls, service_id):
        """get bodypart tagid and related tags.

        :return:
            None, None if no useful tag found
            bodpart_tag_id, tag_ids
        """
        result = cls.call_rpc('api/tag/bodypart_tag_id_by_service_id', id=service_id)
        if not result:
            return None, None

        return result[0], result[1]

    @classmethod
    def get_closure_tags(cls, tag_ids):
        tag_ids = list(set(tag_ids))

        closure_tag_ids = cls._get_closure_by_ids_from_cache(tag_ids)
        result = cls.get_tags_by_tag_ids(closure_tag_ids)
        return result

    @classmethod
    def get_second_tag_id_by_diary_tags(cls, diary_tags):
        """
        此接口为一次性接口, 等产品修改相关逻辑后,即可删除相关代码
        :param diary_tags: diary.tags_new_era
        :return: tag_id or None
        """
        result = cls.call_rpc('api/tag/second_tag_id_by_diary_tags', diary_tags=diary_tags)
        return result
