# -*- 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
from brain.library.v1.library_grpc_client import LibraryGrpcClient
from google.protobuf import json_format
from django.conf import settings
import json
from django.settings import BRAIN_SERVER, BRAIN_PORT

from gm_types.gaia import AUTHOR_TYPE, TAG_TYPE


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)
            rpc_result = cls.get_tags_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_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_info = cls.get_tags_info_by_ids(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_tags_info_by_ids(tag_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):
        all_tags = []
        # result = cls.call_rpc('api/tag/tag/query', name=name, count=count)
        client = LibraryGrpcClient(settings.BRAIN_SERVER, settings.BRAIN_PORT)
        res = client.findByTagName(name=name)
        if res:
            stringValue = res.get("Entry", {}).get("content", {})
            tag_id = stringValue.get("tag_id")
            tag_name = stringValue.get("tag_name")
            tag_type = stringValue.get("tag_type")
            is_online = stringValue.get("is_online")
            recommend_type = stringValue.get("recommend_type")
            all_tags.append({"tag_id": tag_id, "tag_name": tag_name, "tag_type": tag_type, "is_online": is_online,
                             "recommend_type": recommend_type})

    @classmethod
    def get_tags_info_by_ids(cls, tag_ids):
        # 测试环境
        all_tags = []
        client = LibraryGrpcClient(settings.BRAIN_SERVER, settings.BRAIN_PORT)
        res = client.findByTagIds(tagIds=tag_ids)
        res_dict = json_format.MessageToDict(res)
        if res_dict:
            for item in res_dict.get("Entry", []):
                stringValue = item.get("Entry", {}).get("content", {}).get("data", {}).get("stringValue", {})
                tag_id = stringValue.get("tag_id")
                tag_name = stringValue.get("tag_name")
                tag_type = stringValue.get("tag_type")
                is_online = stringValue.get("is_online")
                recommend_type = stringValue.get("recommend_type")
                all_tags.append({"tag_id": tag_id, "tag_name": tag_name, "tag_type": tag_type, "is_online": is_online,
                                 "recommend_type": recommend_type})

        """
        {"id": item.id, "tag_name": item.name, "recommend_type": item.recommend_type,
                     "tag_type": item.tag_type}
        """

        return all_tags
