# coding=utf8

from __future__ import unicode_literals, absolute_import, print_function

import json
from django.db.models import Count

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

from rpc.tool.log_tool import logging_exception
from talos.libs.utils import ObjFromDict
from talos.services.base import ServiceBase, RpcServiceModelCache
from talos.cache.base import talos_rpc_service_model_cache
from talos.services.models.user import User
from talos.backbone.rpc import GaiaRPCFaultException
from social.models import UserFollow


class UserService(ServiceBase):

    _model_name = 'user_v5'  # 改名原因：用户等级权益中新增字段，让所有cache失效

    __cached_layer = RpcServiceModelCache(talos_rpc_service_model_cache, _model_name)

    @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

        return users

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

    @classmethod
    def _get_user_from_cache_by_ids(cls, user_ids):

        cached_info = cls.__cached_layer.mget(user_ids)

        result = {}

        missing = cached_info.pop(cls.__cached_layer.missing_k)
        if 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]
            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 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_user_by_user_id(cls, user_id):
        if 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,
            }
        """
        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,
            }

        d = {
            'user_id': user_id,
            'doctor_id': result['doctor_id'],
            'hospital_id': result['hospital_id'],
        }
        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 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 GaiaRPCFaultException as e:
            if e.error == ERROR.USER_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, GaiaRPCFaultException):
            return None

        return result['user_id']


def get_fans_count_with_user_ids(user_ids):
    user_id2follow_count = dict()
    user_follow_info = UserFollow.objects.filter(
        follow_id__in=user_ids, bond=True
    ).values('follow_id').annotate(dcount=Count('id'))
    for _user_follow in user_follow_info:
        user_id2follow_count[_user_follow["follow_id"]] = _user_follow["dcount"]
    return user_id2follow_count
