# coding=utf8

from __future__ import unicode_literals, absolute_import, print_function

import json

from gm_types.doctor import HOSPITAL_LEVEL

from talos.services.base import ServiceBase
from talos.services.base import RpcServiceModelCache
from talos.cache.service import talos_rpc_service_model_cache
from talos.services.models.doctor import Doctor
from talos.services.user import UserService


class DoctorService(ServiceBase):
    _model_name = 'doctor_v4'  # 因新加字段, 原key所有数据失效

    __cached_layer = RpcServiceModelCache(talos_rpc_service_model_cache, _model_name)

    @classmethod
    def _generate_doctor_object_from_rpc_doctor_data(cls, result):
        if not result:
            return []

        doctors = []
        for r in result:
            r.update({
                'id': r['doctor']['id'],
                'reply_limit': r['doctor']['reply_limit'],
                'is_online': r['doctor']['is_online'],
                'doctor_type': r['doctor']['doctor_type'],
                'portrait': r['doctor']['portrait'],
                'name': r['doctor']['name'],
                'phone': r['doctor']['phone'],
                'user_id': r['id'],
                'hospital_id': r['doctor']['hospital'] and r['doctor']['hospital']['id'] or None,
                'title': r["doctor"]["title"],
                'department': r["doctor"]["department"],
            })

            if r['doctor']['hospital']:
                r['hospital'] = {
                    'id': r['doctor']['hospital']['id'],
                    'name': r['doctor']['hospital']['name'],
                    'hospital_type': r['doctor']['hospital']['hospital_type'],
                    'hospital_level': r['doctor']['hospital']["hospital_level"] or HOSPITAL_LEVEL.NONE,
                    'city_tag_id': (
                        r['doctor']['hospital']['city'] and
                        r['doctor']['hospital']['city']['tag'] and
                        r['doctor']['hospital']['city']['tag']['id'] or None
                    ),
                    'province_tag_id': (
                        r['doctor']['hospital']['city'] and
                        r['doctor']['hospital']['city']['province'] and
                        r['doctor']['hospital']['city']['province']['tag'] and
                        r['doctor']['hospital']['city']['province']['tag']['id'] or None
                    ),
                    'country_tag_id': (
                        r['doctor']['hospital']['city'] and
                        r['doctor']['hospital']['city']['province'] and
                        r['doctor']['hospital']['city']['province']['country'] and
                        r['doctor']['hospital']['city']['province']['country']['tag'] and
                        r['doctor']['hospital']['city']['province']['country']['tag']['id'] or None
                    ),
                }

                # delete doctor
                del r['doctor']

            d = Doctor.from_dict(r)
            if d:
                doctors.append(d)

        return doctors

    @classmethod
    def _cache_doctor(cls, doctor):
        v = doctor.to_json()
        cls.__cached_layer.set(doctor.id, v, 60 * 60)

    @classmethod
    def _cache_doctor_by_user_id(cls, doctor):
        cls.__cached_layer.set(doctor.user_id, doctor.to_json(), 60 * 30)

    @classmethod
    def _get_doctor_from_cache_by_ids(cls, doctor_ids):
        cached_info = cls.__cached_layer.mget(doctor_ids)

        result = []

        missing = cached_info.pop(cls.__cached_layer.missing_k)
        if missing:
            query = '''query {
                doctors(pk__in:%s) {
                    id
                    is_online
                    doctor_type
                    portrait
                    name
                    phone
                    reply_limit
                    title
                    department
                    user {
                        id
                    }
                    hospital {
                      id
                      name
                      hospital_type
                      hospital_level
                      city {
                          tag {
                              id
                          }
                          province {
                              tag {
                                  id
                              }
                              country {
                                  tag {
                                      id
                                  }
                              }
                          }
                      }
                    }
                }
            }
            '''
            query = query % json.dumps(missing)
            res = cls.call_rpc("gql/execute", query=query)
            if res is not None:
                r = res['doctors']
                for info in r:
                    info['user_id'] = info['user'] and info['user']['id'] or None
                    info['hospital_id'] = info['hospital'] and info['hospital']['id'] or None
                    info['hospital'] = {
                        'id': info['hospital'] and info['hospital']['id'] or None,
                        'name': info['hospital'] and info['hospital']['name'] or None,
                        'hospital_level': info['hospital']["hospital_level"] or HOSPITAL_LEVEL.NONE,
                        'city_tag_id': (
                            info['hospital']['city'] and
                            info['hospital']['city']['tag'] and
                            info['hospital']['city']['tag']['id'] or None
                        ),
                        'province_tag_id': (
                            info['hospital']['city'] and
                            info['hospital']['city']['province'] and
                            info['hospital']['city']['province']['tag'] and
                            info['hospital']['city']['province']['tag']['id'] or None
                        ),
                        'country_tag_id': (
                            info['hospital']['city'] and
                            info['hospital']['city']['province'] and
                            info['hospital']['city']['province']['country'] and
                            info['hospital']['city']['province']['country']['tag'] and
                            info['hospital']['city']['province']['country']['tag']['id'] or None
                        ),
                    }

                    doctor = Doctor.from_dict(info)
                    if not doctor:
                        continue

                    result.append(doctor)
                    cls._cache_doctor(doctor)

        for k in cached_info:
            i = cached_info[k].decode()
            v = json.loads(i)
            h = Doctor.from_dict(v)
            result.append(h)

        return result

    @classmethod
    def _get_doctor_from_cache_by_user_ids(cls, user_ids):
        doctor_list = []
        if not user_ids:
            return doctor_list

        cached_info = cls.__cached_layer.mget(user_ids)
        missing = cached_info.pop(cls.__cached_layer.missing_k)
        if missing:
            query = '''query {
                users(pk__in:%s) {
                    id
                    doctor {
                        id
                        is_online
                        doctor_type
                        portrait
                        name
                        phone
                        reply_limit
                        title
                        department
                        hospital {
                            id
                            name
                            hospital_type
                            hospital_level
                            city {
                                tag {
                                    id
                                }
                                province {
                                    tag {
                                        id
                                    }
                                    country {
                                        tag {
                                            id
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            '''

            query = query % json.dumps(user_ids)
            res = cls.call_rpc("gql/execute", query=query)
            if res is None:
                return []

            _info_list = []
            for r in res.get('users', []):
                if not r['doctor']:
                    continue
                _info_list.append(r)
            rpc_doctor = cls._generate_doctor_object_from_rpc_doctor_data(_info_list)

            for doctor in rpc_doctor:
                doctor_list.append(doctor)
                cls._cache_doctor_by_user_id(doctor)

       # 缓存中的数据
        for k, v in cached_info.items():
            if isinstance(v, bytes):
                v = v.decode()
            h = Doctor.from_dict(json.loads(v))
            doctor_list.append(h)

        return doctor_list

    @classmethod
    def get_doctor_from_doctor_id(cls, id):
        if id is None:
            return None

        result = cls.get_doctor_from_doctor_ids(ids=[id])
        if not result:
            return

        return result[0]

    @classmethod
    def get_doctor_from_doctor_ids(cls, ids):
        return cls._get_doctor_from_cache_by_ids(ids)

    @classmethod
    def get_doctor_by_user_id(cls, user_id):
        if user_id is None:
            return None

        result = cls.get_doctor_from_user_ids(user_ids=[user_id])
        if result:
            return result[0]

        return None

    @classmethod
    def doctor_can_create_topic(cls, user_id=None, doctor_id=None):
        if user_id is None and doctor_id is None:
            return False
        kw = {
            'user_id': user_id,
            'doctor_id': doctor_id,
        }
        kw['id'] = kw.pop('doctor_id')      # 这么做的原因为对面接口的参数字段名就是这样的

        try:
            result = cls.call_rpc('api/doctor/can_create_topic', **kw)

        except Exception:
            return True

        return result['can_create_topic']

    @classmethod
    def get_discount_score_for_diary_index(cls, doctor_id):
        if doctor_id is None:
            return 0.0
        try:
            result = cls.call_rpc('api/doctor/diary_index_discount_score', id=doctor_id)

        except Exception:
            return 0.0

        return result

    @classmethod
    def get_doctor_from_context(cls, ctx):
        user = UserService.get_user_from_context(ctx)
        if user:
            return cls.get_doctor_by_user_id(user.id)
        else:
            return None

    @classmethod
    def get_doctor_from_user_ids(cls, user_ids):
        query = '''query {
            users(pk__in:%s) {
                id
                doctor {
                  id
                  is_online
                  doctor_type
                  portrait
                  name
                  phone
                  reply_limit
                  title
                  department
                  hospital {
                    id
                    name
                    hospital_type
                    hospital_level
                    city {
                        tag {
                            id
                        }
                        province {
                            tag {
                                id
                            }
                            country {
                                tag {
                                    id
                                }
                            }
                        }
                    }
                  }
                }
            }
        }
        '''
        user_ids = [int(i) for i in user_ids if i]
        if not user_ids:
            return []

        query = query % json.dumps(user_ids)
        res = cls.call_rpc("gql/execute", query=query)
        if res is None:
            return []

        _result = res['users']
        result = []
        for r in _result:
            if not r['doctor']:
                continue

            result.append(r)

        return cls._generate_doctor_object_from_rpc_doctor_data(result)

    @classmethod
    def get_doctor_from_user_ids_v1(cls, user_ids):
        user_ids = list(map(int, filter(None, user_ids)))
        return cls._get_doctor_from_cache_by_user_ids(user_ids)

    # @classmethod
    # def get_doctor_from_user_id_v1(cls, user_id):
    #     if user_id:
    #         user_list = cls.get_doctor_from_user_ids_v1([user_id])
    #         if user_list:
    #             return user_list[0]
    #     else:
    #         return None

    @classmethod
    def if_person_is_doctor(cls, person_id):
        try:
            result = cls.call_rpc('api/person/if_person_is_doctor', person_id=person_id)
            return result
        except Exception:
            return False

    @classmethod
    def get_doctor_ids(cls, start_num=0, count=10):
        try:
            result = cls.call_rpc('api/doctor/iterator_for_inner', start_num=start_num, count=count)['ids']
        except Exception:
            return []

        return result

    @classmethod
    def is_doctor_by_person_ids(cls, person_ids):
        """
        :param person_ids:
        :return: dict (key->person_id, value->is_doctor)
        """
        return cls.call_rpc("api/person/is_doctor_by_person_ids", person_ids=person_ids)

    @classmethod
    def get_doctor_by_user_id_v1(cls, user_id):
        try:
            result = cls.call_rpc('api/user/get_doctor_id', user_id=user_id)
        except Exception:
            return None

        return result

    @classmethod
    def doctor_user_key(cls, user_id):
        return "doctor_by_uid:{}".format(user_id)

    @classmethod
    def list_doctors_by_user_ids(cls, user_ids):

        keys = [cls.doctor_user_key(uid) for uid in user_ids]
        cached_info = cls.__cached_layer._cache.mget(keys)

        ret, missing_uids = {}, []
        for idx, uid in enumerate(user_ids):
            v = cached_info[idx]
            if v is not None:
                if isinstance(v, bytes):
                    v = v.decode()
                ret[uid] = json.loads(v)
            else:
                missing_uids.append(uid)

        if not missing_uids:
            return ret

        try:
            result = cls.call_rpc('api/user/doctor_infos', user_ids=missing_uids)
        except Exception:
            return ret

        missing_data = {}
        for item in result:

            if not item["id"]:
                info = {}
            else:
                info = {
                    "user_id": item["user_id"],
                    "doctor_id": item["id"],
                    "nickname": item["nickname"],
                    "portrait": item["portrait"],
                }
            missing_data[item["user_id"]] = info
            cls.__cached_layer._cache.set(cls.doctor_user_key(item["user_id"]), json.dumps(info), 60 * 60 * 24)

        ret.update(missing_data)

        return ret

    @classmethod
    def list_doctors_by_person_ids(cls, person_ids):
        try:
            result = cls.call_rpc('doctor/get_info/by_pids', person_ids=person_ids)
        except Exception:
            return {}

        doctors_info = {}
        for doctor in result:
            for person_id, d in doctor.items():
                doctors_info[person_id] = d

        return doctors_info

    @classmethod
    def get_doctor_by_doctor_ids_v1(cls, doctor_ids):

        try:
            result = cls.call_rpc('api/batch_get_doctor_info_by_doctor_ids', doctor_ids=doctor_ids, with_fields=['meta'])
        except Exception:
            return []

        return result