# -*- coding: utf8 -*-

from __future__ import unicode_literals, absolute_import, print_function

import json
from talos.services.base import ServiceBase, RpcServiceModelCache
from talos.cache.service import talos_rpc_service_model_cache
from talos.cache.gaia import talos_tagrel_cache
from talos.services.models.tag import Tag, TagType


class TagService(ServiceBase):
    _model_name = 'tag'

    __cached_layer = RpcServiceModelCache(talos_rpc_service_model_cache, _model_name)

    @staticmethod
    def format_tag(tag_obj):
        return {
            "id": tag_obj.id,
            "tag_id": tag_obj.id,
            "name": tag_obj.name,
            "tag_name": tag_obj.name,
            "tag_type": tag_obj.tag_type,
        }

    @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."""

        start = 0
        step = 100
        while True:
            result = cls.call_rpc('api/tag/iterator_all_tags', start_num=start, count=step)
            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].decode()
            v = json.loads(i)
            result.append(Tag.from_dict(v))

        return result

    @classmethod
    def _get_by_ids_from_cache_type(cls, ids):
        result = list()
        cached_info = cls.call_rpc('tag/info_by_ids_type', tag_ids=ids)
        # cached_info = [TagType.from_dict(i) for i in rpc_result]
        for k in cached_info:
            # i = cached_info[k].decode()
            # v = json.loads(k)
            result.append(TagType.from_dict(k))
        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.decode().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_by_id_from_cache(cls, id):
        """return obj."""
        result = cls._get_by_ids_from_cache(ids=[id, ])
        return result and result[0] or None

    @classmethod
    def get_tag_by_tag_id(cls, id):
        if not id:
            return

        return cls._get_by_id_from_cache(id)

    @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_tags_dict_by_tag_ids(cls, tag_ids):

        tags = cls.get_tags_by_tag_ids(list(set(tag_ids)))

        result = {}
        for tag in tags:
            result[tag.id] = tag

        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

    @classmethod
    def get_tag_by_name(cls, name, count=1):
        result = cls.call_rpc('api/tag/tag/query', name=name, count=count)
        return result
