#!/usr/bin/env python
# -*- coding: utf-8 -*-

from __future__ import unicode_literals, absolute_import, print_function

import json

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


class TagV3Service(ServiceBase):
    _model_name = "tagv3"
    __cached_layer = RpcServiceModelCache(talos_rpc_service_model_cache, _model_name)

    @staticmethod
    def _filter_valid_ids(ids):
        return list(filter(None, set(ids)))

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

    @classmethod
    def _cache_tag_v3(cls, tag_v3):
        v = tag_v3.to_json()
        cls.__cached_layer.set(tag_v3.id, v, 60 * 60 * 8)

    @classmethod
    def _get_tag_v3_from_cache_by_ids(cls, tag_v3_ids):
        cached_info = cls.__cached_layer.mget(tag_v3_ids)

        result = {}

        missing = cached_info.pop(cls.__cached_layer.missing_k)
        if missing:
            rpc_result = cls.call_rpc(
                'api/tag_v3/info_by_ids',
                tag_ids=missing
            )
            if rpc_result:
                for tag_v3_id in missing:
                    tag_v3_info = rpc_result.get(str(tag_v3_id), {})
                    if not tag_v3_info:
                        continue

                    tag_v3_obj = TagV3.from_dict({
                        "id": tag_v3_info["id"],
                        "name": tag_v3_info["name"],
                        "tag_type": tag_v3_info["tag_type"],
                    })

                    cls._cache_tag_v3(tag_v3_obj)
                    result.update({
                        tag_v3_id: tag_v3_obj
                    })

        for k in cached_info:
            i = cached_info[k].decode()
            tag_v3 = TagV3.from_dict(json.loads(i))
            result.update({
                tag_v3.id: tag_v3
            })

        return result

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

        def convert_tag_v3_closure_key(tag_id):
            return "tagv3_{}".format(tag_id)

        if not ids:
            return []

        cache = talos_tagrel_cache
        key_prefix = object.__getattribute__(cache, 'prefix')  # manually handle mget keys
        keys = ["{}:{}".format(
            key_prefix,
            convert_tag_v3_closure_key(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_v3/get_closure_tags',
                    tag_ids=[tag_id, ]
                )
                closure_tag_ids = [tag['id'] for tag in closure_tag_info]
                result.update(closure_tag_ids)
                cache.setex(
                    convert_tag_v3_closure_key(str(tag_id)),
                    6 * 60 * 60,
                    ','.join(map(str, closure_tag_ids))
                )

        return list(result)

    @classmethod
    def get_tags_by_tag_v3_ids(cls, tag_v3_ids):
        """return a dict as tag_id: TagV3 object."""
        tag_v3_ids = cls._filter_valid_ids(tag_v3_ids)
        return cls._get_tag_v3_from_cache_by_ids(tag_v3_ids=tag_v3_ids)

    @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_v3_ids(closure_tag_ids).values()
        return result

    @classmethod
    def get_tag_v3_by_old_tag_ids(cls, old_tag_ids):
        """
        :param old_tag_ids:
        :return: {old_tag_id: [{"id": x, "name": xx, "tag_type": xx}, 新标签]}
        """
        map_dict = cls.call_rpc(
            'api/tag_v3/get_map_new_tag_by_old_ids',
            old_tag_ids=old_tag_ids
        )

        return map_dict

    @classmethod
    def get_tag_v3_ids_by_tag_names(cls, tag_name_list, tag_type):
        """
        通过 标签名 + 标签类型 获取标签数据
        :param tag_name_list:
        :param tag_type:
        :return:
        """
        tag_v3_list = cls.call_rpc(
            'api/tag_v3/gets_info',
            tag_names=tag_name_list,
            tag_type=tag_type
        )

        return tag_v3_list
