# coding=utf8

from __future__ import unicode_literals, absolute_import, print_function

import json

from helios.rpc import RPCFaultException
from gm_types.error import ERROR

from talos.rpc import logging_exception
from talos.libs.utils import ObjFromDict
from talos.services.base import ServiceBase, RpcServiceModelCache
from talos.cache.service import talos_rpc_service_model_cache
from talos.services.models.user import User, SimpleUser
from talos.logger import cache_hit_logger


class UserService(ServiceBase):
    _model_name = 'user_v7'
    __cached_layer = RpcServiceModelCache(talos_rpc_service_model_cache, _model_name)

    _model_name_v2 = 'user_v8'
    __cached_layer_v2 = RpcServiceModelCache(talos_rpc_service_model_cache, _model_name_v2)

    @classmethod
    def _generate_user_object_from_rpc_user_data(cls, result):
        if not result:
            return {}

        users = {}
        for user_info in result:
            user = User.from_dict(user_info)
            if user:
                users[user.person_id] = user
                users[user.id] = user
        import logging
        logging.info("get users:%s" % users)
        return users

    @classmethod
    def _cache_user(cls, user, cache=None):
        v = user.to_json()
        if cache is None:
            cache = cls.__cached_layer
        cache.set(user.id, v, 60 * 30)

    @classmethod
    def _cache_users(cls, users, cache=None):
        users_json = [(user.id, user.to_json()) for user in users]
        if cache is None:
            cache = cls.__cached_layer
        cache.mset(users_json, 60 * 30)

    @classmethod
    def _get_user_from_cache_by_ids(cls, user_ids):
        cached_info = cls.__cached_layer.mget(user_ids)

        result = {}

        cache_hit_logger.info("user: find cache")
        missing = cached_info.pop(cls.__cached_layer.missing_k)
        if missing:
            cache_hit_logger.info("diary: not cached user: {}".format(missing))
            rpc_result = cls.call_rpc('api/user/get_fundamental_info_by_user_ids', user_ids=missing)
            if rpc_result:
                for info in rpc_result:
                    user = User.from_dict(info)
                    if not user:
                        continue

                    result[user.id] = user
                    cls._cache_user(user)

        for k in cached_info:
            i = cached_info[k].decode()
            v = json.loads(i)
            user = User.from_dict(v)
            result[user.id] = user

        return result

    @classmethod
    def _get_user_data_by_user_ids(cls, user_ids):
        return cls._get_user_from_cache_by_ids(user_ids)

    @classmethod
    def _get_user_data_by_person_ids(cls, person_ids):
        person_ids = list(set(person_ids))
        r = cls.call_rpc('api/user/get_fundamental_info_by_person_ids', person_ids=person_ids)

        return cls._generate_user_object_from_rpc_user_data(result=r)

    @classmethod
    def get_user_from_context(cls, ctx=None):
        """通过ctx获取User object.

        :param:
            ctx: gaia.rpc.context.Context, 如果ctx为none, 从当前thread_local中取 for compatible

        :return:
            User object
        """
        user_infos = cls.call_rpc('api/user/get_fundamental_info_by_user_ids')
        if not user_infos:
            return User.get_anonymous_user()

        users = cls._generate_user_object_from_rpc_user_data(result=user_infos)
        return list(users.values())[0]

    @classmethod
    def get_users_by_person_ids(cls, person_ids):
        """return a dict as user_id: User object."""
        person_ids = list(set(person_ids))
        return cls._get_user_data_by_person_ids(person_ids=person_ids)

    @classmethod
    def get_user_by_person_id(cls, person_id):
        users = cls.get_users_by_person_ids(person_ids=[person_id])
        return users.get(person_id)

    @classmethod
    def get_users_by_user_ids(cls, user_ids):
        """return a dict as user_id: User object."""
        user_ids = list(set(user_ids))
        return cls._get_user_data_by_user_ids(user_ids=user_ids)

    @classmethod
    def get_format_user_by_user_ids(cls, user_ids):

        users_dict = cls.get_users_base_info_by_user_ids(user_ids)

        users = {}
        for uid, user in users_dict.items():
            info = {
                "id": user.id,
                "user_id": user.id,
                "user_name": user.nickname,
                "portrait": user.portrait,
                "user_type": user.user_type,
                "doctor_id": user.doctor_id,
                "doctor_type": user.doctor_type,
                "hospital_id": user.hospital_id,
                "membership_level": user.membership_level,
                "user_level": {
                    "membership_icon": user.membership_icon,  # 达人 icon
                    "level_icon": user.level_icon,  # 等级 icon
                    "constellation_icon": user.constellation_icon,  # 星座 icon
                }
            }
            users[uid] = info

        return users

    @classmethod
    def get_users_base_info_by_user_ids(cls, user_ids):

        user_ids = list(set(user_ids))

        return cls._get_user_base_info_by_ids(user_ids)

    @classmethod
    def _get_user_base_info_by_ids(cls, user_ids):

        cached_info = cls.__cached_layer_v2.mget(user_ids)
        cache_hit_logger.info("user base info: find cache")

        result = {}
        missing = cached_info.pop(cls.__cached_layer_v2.missing_k)
        if missing:
            cache_hit_logger.info("diary: not cached user base info: {}".format(missing))
            rpc_result = cls.call_rpc('api/user/get_base_info_by_user_ids', user_ids=missing)
            if rpc_result:
                missing_users = []
                for info in rpc_result:
                    user = SimpleUser.from_dict(info)
                    if not user:
                        continue
                    result[user.id] = user
                    missing_users.append(user)

                if missing_users:
                    cls._cache_users(missing_users, cls.__cached_layer_v2)

        for k in cached_info:
            i = cached_info[k].decode()
            v = json.loads(i)
            user = SimpleUser.from_dict(v)
            result[user.id] = user

        return result

    @classmethod
    def get_user_by_user_id(cls, user_id):
        user_id = int(user_id)
        users = cls.get_users_by_user_ids(user_ids=[user_id])
        return users.get(user_id)

    @classmethod
    def get_doctor_hospital_id_by_user_id(cls, user_id):
        """获取关联了user_id的医生机构id信息。

        :param:
            user_id: int

        :return:
            {
                'user_id': int,
                'doctor_id': str,
                'hospital_id': str,
                'doctor_title': str
            }
        """
        try:
            result = cls.call_rpc('api/user/user_doctor_info', user_id=user_id)

        except Exception:  # TODO CR 检查是否为相应的RPC Exception
            return {
                'user_id': None,
                'doctor_id': None,
                'hospital_id': None,
                'doctor_title': None,
            }

        d = {
            'user_id': user_id,
            'doctor_id': result['doctor_id'],
            'hospital_id': result['hospital_id'],
            'doctor_title': result.get("doctor_title", None)
        }
        return d

    @classmethod
    def user_is_doctor(cls, user_id):
        doctor_hospital_info = cls.get_doctor_hospital_id_by_user_id(user_id)
        if doctor_hospital_info['doctor_id'] is None:
            return False

        return True

    @classmethod
    def user_not_consumer(cls, user_id):
        hospital_info = cls.get_doctor_hospital_id_by_user_id(user_id)
        if hospital_info['doctor_id'] or hospital_info['hospital_id']:
            return True
        return False

    @classmethod
    def get_doctor_id_from_user_id(cls, user_id):
        doctor_hospital_info = cls.get_doctor_hospital_id_by_user_id(user_id)
        did = doctor_hospital_info['doctor_id']
        return did

    @classmethod
    def get_doctor_from_user_id(cls, user_id, need_wrapped=True):
        did = cls.get_doctor_id_from_user_id(user_id)
        if did is None:
            return None

        try:
            result = cls.call_rpc('api/doctor/detail', id=did)

        except Exception:  # TODO CR 检查是否为相应的RPC Exception
            return None

        result['id'] = result.pop('did')  # 调用的 rpc 接口一致性没做好, 所以需要在这边转换格式
        if result['hospital_id'] == '':
            result['hospital_id'] = None  # 调用的 rpc 接口一致性没做好, 所以需要在这边转换格式

        if need_wrapped:
            o = ObjFromDict(result)
            return o

        return result

    @classmethod
    def user_followed_tags_obj(cls, user_id):
        try:
            result = cls.call_rpc('api/tag/user_followed_tag_obj', user_id=user_id)
        except Exception:  # TODO CR 检查是否为相应的RPC Exception
            return None

        result = map(ObjFromDict, result)
        return result

    @classmethod
    def get_user_id_by_doctor_id(cls, doctor_id):
        result = None
        try:
            result = cls.call_rpc('api/user/by_doctor_id', id=doctor_id)
        except RPCFaultException as e:
            if e.error in (ERROR.USER_NOT_FOUND, ERROR.DOCTOR_NOT_FOUND):
                return None

            else:
                logging_exception()

        except:
            logging_exception()

        if result:
            return result['id']

        return None

    @classmethod
    def user_in_black_list(cls, user_id):
        try:
            result = cls.call_rpc('api/user/in_blacklist', user_id=user_id)

        except Exception:
            return False

        return result

    @classmethod
    def get_user_id_by_person_id(cls, person_id):
        try:
            result = cls.call_rpc('api/person/user_info', person_id=person_id)
        except RPCFaultException:
            return None

        return result['user_id']
