# coding=utf-8
# owner: lipeng or zhouyang
#
#
from __future__ import unicode_literals, absolute_import, print_function

import datetime
from django.db.models import Q, Avg
from django.conf import settings
import itertools
import math
import json

from api.models import Doctor
from api.models import DoctorRegisterInfo
from api.models import Hospital
from api.models import DOCTOR_ORDER_TYPE, ORDER_STATUS, DOCTOR_TYPE, TOPIC_TYPE
from api.models import Service
from api.models import Order
from api.models import DoctorCountRecord
from api.models import FamousDoctorClient, PhoneCallRecord
from api.models import ServiceComment
from lasker.models import AwardResult


from api.tool.image_utils import get_full_path
from api.tool.user_tool import get_user_city_tagid
from api.tool.user_tool import get_user_from_context
from api.tool.user_tool import get_doctor_by_user_id
from api.tool.geo_tool import get_location_tag_id_by_city_id
from api.tool.common_tool import number_floor
from api.tool.datetime_tool import (
    get_timestamp_or_none,
    get_datetime,
)
from api.tool.log_tool import logging_exception

from gaia.routers import thread_local
from gm_types.doctor import DOCTOR_TITLE_TYPE
from gm_types.gaia import HOSPITAL_TYPE, SERVICE_FLAG
from gm_types.user_hierarchy import EventType

from rpc.context import get_gaia_local_invoker, get_rpc_remote_invoker
from rpc.decorators import list_interface
from rpc.tool.error_code import gen, CODES
from rpc.tool.param_check import assert_uint
from rpc.decorators import bind, bind_context, cache_page
from statistic.models import DoctorView

from search.utils.doctor import filter_doctor
from social import SocialInfo
from mimas.tasks import send_event_task

# TODO: TALOS MIGRATE
from talos.models.diary import Diary
from services.custom_phone_service import PhoneService
from api.util.cache_util import cache_wrap
from rpc.cache import doctor_search_tags_cache

from category.models import CategoryPolymer

from hippo.models import Doctor, MerchantRelevance, TrafficStat, Doctor_Extra
from api.models.order import Order,RefundOrder
from maidan.models.order import MaidanOrder
from api.models.budan import BuDanLuRu
from gm_types.gaia import ORDER_STATUS, REFUND_STATUS
from helios import create_default_invoker
from django.conf import settings
from gm_types.doctor import BUDAN_LURU_STATUS, BUDAN_LURU_TYPE
from rpc.tool.log_tool import logging_exception
from hippo.models.merchant import MerchantLevelRecord
from django.db.models import Sum

PAY_STATUS_LIST = (
    ORDER_STATUS.USED, ORDER_STATUS.SETTLED, ORDER_STATUS.PAID,
    ORDER_STATUS.WAIT_REFUNDED, ORDER_STATUS.OTHER,
    ORDER_STATUS.AUDITING, ORDER_STATUS.SETTLING
)

USED_STATUS_LIST = (
    ORDER_STATUS.USED,
    ORDER_STATUS.SETTLED,
    ORDER_STATUS.OTHER,
    ORDER_STATUS.AUDITING,
    ORDER_STATUS.SETTLING,
)

DOCTOR_TAG_LIMIT = 6


def get_doctor_card_info_by_ids(ids):
    doctors = Doctor.objects.select_related('hospital').filter(id__in=ids, is_online=True)

    if not doctors:
        return []

    result = []
    for d in doctors:
        result.append({
            'id': d.id,
            'name': d.name,
            'portrait': d.portrait,
            'title': d.title,
            'hospital': d.hospital.name,
            'tags': d.get_doctor_item_list()[0]
        })

    return result


@bind_context("api/doctor/common_filter")
@list_interface(
    offset_name='offset', limit_name='limit',
    element_model=Doctor, element_func_list=[Doctor.doctor_detail_v2]
)
def api_filter_doctor_common(
        ctx, order_by=DOCTOR_ORDER_TYPE.DEFAULT2,
        filters=None, offset=0, limit=10, current_city_id=None, with_count=False, query="",device_id="0"):
    filters = filters or {}
    sort_params = {}

    #获取当前城市和国家的tag
    user = ctx.session.user
    user_city_tag_id = get_user_city_tagid(user.id)
    if user_city_tag_id is not None:
        user_city_tag_id = assert_uint(user_city_tag_id)

    country_tagid, city_tagid = get_location_tag_id_by_city_id(current_city_id)
    user_city_tag_id = city_tagid and city_tagid or user_city_tag_id
    if user_city_tag_id is not None:
        sort_params['user_city_tag_id'] = user_city_tag_id

    if 'adver_position' in filters:
        need_adver_info = True
        sort_params['adver_position'] = filters['adver_position']
        if 'adver_word' in filters:
            sort_params['adver_word'] = filters['adver_word']
        # @desc 当取广告时 如果没有用户地区信息，取当前用户所在城市做筛选， 确保用户只能看到当前城市的广告。
        # @pm 李浩楠 @rd 郑伟
        # @date 20170601
        if 'area_tag_id' not in filters and city_tagid:
            filters['area_tag_id'] = city_tagid
    else:
        need_adver_info = False

    offset = assert_uint(offset, 0)
    limit = assert_uint(limit, 10)

    # @desc 当用户没有设置城市信息时， 不展示广告
    # http://wiki.gengmei.cc/pages/viewpage.action?pageId=4149581
    # @pm 李浩楠 @rd 郑伟
    # @date 20170606
    if need_adver_info:
        if 'area_tag_id' not in filters:
            result = []
            if with_count:
                return {
                    'doctors': result,
                    'total_count': 0,
                }
            else:
                return result

    doctor_search_result = filter_doctor(
        offset=offset, size=limit,
        sort_type=order_by,
        filters=filters,
        sort_params=sort_params
    )
    high_light_list_data = doctor_search_result.get('hits', [])
    ids = doctor_search_result["doctor_ids"]
    if need_adver_info:
        doctor_adver_id_map = doctor_search_result['adver_id_map']
    doctors = Doctor.objects.prefetch_related(
        'business_partener', 'hospital__city__province', 'hospital__hospital_extra', 'hospital__doctor_hospital', 'tags'
    ).filter(id__in=ids)
    doctors = {d.id: d for d in doctors}

    all_doctor_ids=[doctor for doctor in doctors]
    # 获取查询相关cases
    case_dict = Doctor.get_doctor_tag_count_dict(ctx, device_id, all_doctor_ids, query)
    result = []
    for did in ids:
        if did not in doctors:
            continue

        if need_adver_info:
            advertise_id = doctor_adver_id_map[did] if did in doctor_adver_id_map else None
        else:
            advertise_id = None
        add_data = doctors[did].doctor_detail_v2(advertise_id=advertise_id, query=query,case_dict=case_dict)

        # 7765 高亮
        single_high_light = list(filter(lambda x: x.get('id') == did, high_light_list_data))
        if single_high_light:
            add_data.update({'highlight': single_high_light[0].get('highlight', {})})
        # 7680添加外显两个美购
        result.append(add_data)

    if not with_count:
        return result

    return {
        'doctors': result,
        'total_count': doctor_search_result['total_count']
    }


@bind_context("api/doctor/filter")
@list_interface(
    offset_name='offset', limit_name='limit',
    element_model=Doctor, element_func_list=[Doctor.doctor_detail_v2]
)
def api_filter_doctor(
        ctx, order_by=DOCTOR_ORDER_TYPE.DEFAULT2,
        filters=None, offset=0, limit=10, current_city_id=None, with_count=False):
    filters = filters or {}
    sort_params = {}

    # get country/city tagid
    user = ctx.session.user
    user_city_tag_id = get_user_city_tagid(user.id)
    if user_city_tag_id is not None:
        user_city_tag_id = assert_uint(user_city_tag_id)

    country_tagid, city_tagid = get_location_tag_id_by_city_id(current_city_id)
    user_city_tag_id = city_tagid and city_tagid or user_city_tag_id
    if user_city_tag_id is not None:
        sort_params['user_city_tag_id'] = user_city_tag_id

    # 2016.04.12 不再使用本国id作为筛选
    # if 'area_tag_id' not in filters and country_tagid:
    #     filters['area_tag_id'] = country_tagid

    offset = assert_uint(offset, 0)
    limit = assert_uint(limit, 10)

    # 只返回医生类型的doctor(排除机构管理者)
    filters['doctor_type'] = DOCTOR_TYPE.DOCTOR

    data = filter_doctor(
        offset=offset, size=limit,
        sort_type=order_by,
        filters=filters,
        sort_params=sort_params
    )
    ids = [doc['_id'] for doc in data["hits"]]
    doctors = Doctor.objects.prefetch_related(
        'business_partener', 'hospital__city__province', 'hospital__hospital_extra', 'hospital__doctor_hospital', 'tags'
    ).filter(id__in=ids)
    doctors = {d.id: d for d in doctors}

    result = []
    for did in ids:
        if did not in doctors:
            continue
        doctor = doctors[did]
        # 7745 医生/医院双证有其一 且 关联了商务才展示
        if not all([doctor.business_partener, doctor.b_licences or doctor.p_licences]):
            continue
        single_doctor_data = doctor.doctor_detail_v2()
        current_doctor_filter = filter(lambda x: x.get('id', '') == did, data["hits"])
        if len(current_doctor_filter) > 0:
            first_filter_data = current_doctor_filter[0]
            single_doctor_data.update({'highlight': first_filter_data.get('highlight', {})})
        result.append(single_doctor_data)

    if not with_count:
        return result

    return {
        'doctors': result,
        'total_count': data['total_count'],
    }


@bind_context('api/doctor/officer_filter')
@list_interface(
    offset_name='offset', limit_name='limit', element_model=Doctor,
    element_func_list=[Doctor.doctor_detail_v2]
)
def api_filter_officer(
        ctx, order_by=DOCTOR_ORDER_TYPE.DEFAULT2,
        filters=None, offset=0, limit=10, current_city_id=None,
        with_count=False
):
    '''机构管理者过滤器.

    TODO: refactor this func and api/doctor/filter
    '''
    filters = filters or {}
    sort_params = {}

    user = ctx.session.user
    user_city_tag_id = get_user_city_tagid(user.id)
    if user_city_tag_id is not None:
        user_city_tag_id = assert_uint(user_city_tag_id)

    country_tagid, city_tagid = get_location_tag_id_by_city_id(current_city_id)
    user_city_tag_id = city_tagid and city_tagid or user_city_tag_id
    if user_city_tag_id is not None:
        sort_params['user_city_tag_id'] = user_city_tag_id

    # 2016.11.10 不再使用本国id作为筛选
    # if 'area_tag_id' not in filters and country_tagid:
    #     filters['area_tag_id'] = country_tagid

    offset = assert_uint(offset, 0)
    limit = assert_uint(limit, 10)

    # 只返回机构管理者类型的doctor
    filters['doctor_type'] = DOCTOR_TYPE.OFFICER

    data = filter_doctor(
        offset=offset, size=limit,
        sort_type=order_by,
        filters=filters,
        sort_params=sort_params
    )
    ids = [doc['_id'] for doc in data["hits"]]
    doctors = Doctor.objects.prefetch_related(
        'business_partener', 'hospital__city__province', 'hospital__hospital_extra', 'hospital__doctor_hospital', 'tags'
    ).filter(id__in=ids)
    doctors = {d.id: d for d in doctors}

    result = []
    for did in ids:
        if did not in doctors:
            continue
        doctor = doctors[did]
        # 7745 医生/医院双证有其一 且 关联了商务才展示
        if not all([doctor.business_partener, doctor.b_licences or doctor.p_licences]):
            continue

        detail = doctor.doctor_detail_v2()
        detail['item_list'] = doctor.hospital.get_best_sell_tags() if getattr(doctor, "hospital_id") else []
        result.append(detail)

    if not with_count:
        return result

    return {
        'hospitals': result,
        'total_count': data['total_count'],
    }


@bind_context("api/doctor")
def get_doctor_info(ctx, id):
    """
    医生主页
    id: 医生ID
    """
    user = get_user_from_context(ctx)
    try:
        doctor = Doctor.objects.get(id=id)
        result = doctor.doctor_detail(user)
        return result
    except Doctor.DoesNotExist:
        return gen(CODES.DOCTOR_NOT_FOUND)


@bind('api/doctors/practice_time')
def get_doctors_practice_time(doctor_ids):
    if not doctor_ids:
        return {}
    res = Doctor_Extra.objects.filter(doctor_id__in=doctor_ids)
    return {r.doctor_id: r.working_year for r in res}


@bind("api/doctors/extra_info")
def get_doctors_extra_info(doctor_ids):
    """
    通过医生id列表,获取额外的其他信息

    :param id: 医生id列表
    :return: extra_data 字典
    """

    doctors = Doctor.objects.filter(pk__in=doctor_ids)

    extras_data = {}
    for doctor in doctors:

        extra_data = {
            "practice_time": doctor.practice_time,  # 执业年限
            'city_name': doctor.hospital.city.name if doctor.hospital and doctor.hospital.city else '',
        }
        extras_data[doctor.id] = extra_data

    return extras_data


@bind("api/doctor/extra_info")
def get_doctor_extra_info(id):
    """
    v 7.6.25
    通过医生id,获取额外的其他信息

    :param id: 医生id
    :return: extra_data 字典
    """
    try:
        doctor = Doctor.objects.prefetch_related('tags').get(id=id)

        # v 7.6.25  获取医生的预约总数, 计算规则: 医生所有美购(在线 + 下线) 预约数总和
        nums = sum([service.sell_amount_display for service in doctor.services.all()])

        # 医生相册 新加字段
        doctor_album = doctor.personalized_photo_album and [{
            "image_half": get_full_path(url, "-half"),
            "image_w": get_full_path(url, "-w"),
        } for url in doctor.personalized_photo_album]

        # 医生职业编号，执业时间
        extra = doctor.extra_info
        serial_number = extra and extra["professional_certificate_num"]  # 职业编号
        time_stamp = extra and extra["professional_certificate_time_float"]  # 注册时间 时间戳
        year = time_stamp and (datetime.datetime.now() - get_datetime(time_stamp)).days // 365

        # 热卖美购
        # service_ids = doctor.service_ranks.all().order_by('rank')\
        #     .values_list("service_id", flat=True)
        # diary_ids = doctor.diary_ranks.all().order_by('rank')\
        #     .values_list("diary_id", flat=True)

        badges, is_special_appearance = doctor.get_award_name_and_is_special_skin()
        extra_data = {
            "doctor_title": doctor.doctor_title_display or "执业医师",  # 职称
            "practice_time": year or 0,  # 执业年限
            "subscribe_num": nums,  # 预约数
            "doctor_new_rate": doctor.rate,  # update 7.6.55 总分
            "doctor_type": _get_doctor_type(doctor),  # 公立,民营,管理者
            "b_licences": [get_full_path(url, "-wwtm") for url in doctor.b_licences],  # 资格证
            "p_licences": [get_full_path(url, "-wwtm") for url in doctor.p_licences],  # 执业证
            "doctor_tags": doctor.personalized_label,  # 新增字段,获取医生设置标签
            "operate_tag": doctor.operate_tag()[:DOCTOR_TAG_LIMIT],  # 获取运营标签
            "badges": badges,  # 名医大赏标签
            "is_special_appearance": is_special_appearance,         # 名医大赏是否有特殊皮肤
            "doctor_album": doctor_album,  # 新增字段,获取医生个人相册,
            "doctor_services": [],  # 医生热卖美购,根据后台排名设置获取.
            "doctor_diaries": [],  # 推荐日记本id,根据医生后台排名
            "serial_number": serial_number or "",  # 新加字段 资格编号 !!!
        }
        return extra_data
    except Doctor.DoesNotExist:
        return gen(CODES.DOCTOR_NOT_FOUND)


def _get_doctor_type(doctor):
    """
    v 7.6.25 获取医生 所属医院类型标签
    :return:
    """
    if doctor.hospital.hospital_type == HOSPITAL_TYPE.PUBLIC:
        return HOSPITAL_TYPE.PUBLIC

    if doctor.doctor_type != DOCTOR_TYPE.OFFICER:
        return HOSPITAL_TYPE.PRIVATE

    return None


@bind("api/doctor/detail")
def get_doctor_detail(id, is_favored=False, need_intro=False):
    """get doctor info with doctor detail v2."""
    try:
        doctor = Doctor.objects.get(id=id, is_online=True)
        result = doctor.doctor_detail_v2(is_favored, need_intro)
        return result
    except Doctor.DoesNotExist:
        return gen(CODES.DOCTOR_NOT_FOUND)


@bind_context('api/doctor/multi_detail')
def multiget_doctor_detail(ctx, id_list, is_favored=False, need_intro=False, query="",device_id='0'):
    """get doctor info with doctor detail v2."""
    doctors = Doctor.objects.filter(id__in=id_list, is_online=True)
    res = {
        'doctor_info': {}
    }
    # 获取查询相关cases
    case_dict = Doctor.get_doctor_tag_count_dict(ctx, device_id, id_list, query)
    for doctor in doctors:
        res['doctor_info'][doctor.id] = doctor.doctor_detail_v2(is_favored, need_intro, query=query,case_dict=case_dict)
    return res


@bind_context('api/doctor/my/operate_service', login_required=True)
@list_interface(offset_name='start_num', limit_name='count', element_model=Service)
def api_doctor_operate_service(ctx, start_num=0, count=10):
    results = []
    user = get_user_from_context(ctx)
    doctor = Doctor.objects.get(user=user)

    user_services = Service.objects.filter(doctor=doctor)
    for service in user_services[start_num:start_num + count]:
        service_data = {
            'name': service.name,
            'pay_num': service.order_set.filter(status__in=PAY_STATUS_LIST).count(),
            'use_num': service.order_set.filter(status__in=USED_STATUS_LIST).count()
        }
        results.append(service_data)

    return results


@bind_context('api/doctor/my/order_validate')
@list_interface(offset_name='start_num', limit_name='count', element_model=Order)
def api_doctor_order_validate(ctx, start_num=0, count=10, ids=None):
    results = []

    user = get_user_from_context(ctx)
    validated_orders = Order.objects.filter(operate_user=user).order_by('-validate_time')
    if ids:
        if isinstance(ids, list):
            q = Q(id__in=ids)
        else:
            q = Q(id__contains=ids)
        validated_orders = validated_orders.filter(q)
    validated_orders = validated_orders[start_num:start_num + count]

    for validated_order in validated_orders:
        results.append(validated_order.data())
    return results


@bind_context("api/doctor/add_follow", login_required=True)
def doctor_add_follow(ctx, doctor_id):
    """
    专家添加关注
    :param doctor_id: 需要关注的专家ID
    :return:
    """
    try:
        doctor = Doctor.objects.get(id=doctor_id)
        if not doctor.user:
            return

        user = get_user_from_context(ctx)
        social_info = SocialInfo(uid=user.id)
        social_info.follow(uid=doctor.user.id)
        event_data = send_event_task(user_id=user.id, event_type=EventType.FOLLOW_MULTI_TIMES, related_item=doctor_id)
        growth_value, point_value = event_data['growth_value'], event_data['point_value']
        submit_count = event_data['submit_count']
        return {'growth_value': growth_value, 'point_value': point_value, 'submit_count': submit_count}
    except Doctor.DoesNotExist:
        return gen(CODES.USER_NOT_FOUND)


@bind_context("api/doctor/del_follow", login_required=True)
def doctor_del_follow(ctx, doctor_id):
    """
    专家取消关注
    :param doctor_id: 需要取消关注的专家ID
    :return:
    """
    try:
        doctor = Doctor.objects.get(id=doctor_id)
        user = get_user_from_context(ctx)
        social_info = SocialInfo(uid=user.id)
        social_info.unfollow(uid=doctor.user.id)
    except Doctor.DoesNotExist:
        return gen(CODES.USER_NOT_FOUND)
    return None


@bind('api/doctor/famous_doctors/get')
@cache_page(3 * 3600)
def get_famous_doctors_info(id):
    """
    用于名医大赏，根据医生ID返回基本信息
    :param id:
    :return: {"doctor_id": 1001,
              "name": "test",
              "portrait": "",
              "description": "",
              "hospital": '',
              "case_num": 0, 已服务消费者数=医生订单数*10+答疑数*2
              "title": '',
              }
    """
    rpc = get_rpc_remote_invoker()
    doctor = Doctor.objects.filter(id=id).first()
    if not doctor:
        return {}
    is_doctor = doctor.doctor_type == DOCTOR_TYPE.DOCTOR
    if is_doctor:
        doctor_ids = [doctor.id]
    else:
        doctor_ids = list(Doctor.objects.filter(hospital=doctor.hospital).values_list('id', flat=True))
    try:
        hospital = doctor.hospital.name
    except:
        hospital = None
    order_case = Order.objects.filter(service__doctor__in=doctor_ids, status=ORDER_STATUS.PAID).count()
    filters = {
        'doctor_ids': doctor_ids
    }
    try:
        from search.utils.diary import filter_diary
        diary_data = filter_diary(size=1, filters=filters, expose_total=True)
        diary_count = diary_data['total']
    except:
        logging_exception()
        diary_count = 0
    case_num = order_case * 10 + diary_count

    return {
        "doctor_id": doctor.id,
        "is_hospital": not is_doctor,
        "name": doctor.name if is_doctor else doctor.hospital.name,
        "portrait": doctor.portrait,
        "description": doctor.introduction,
        "hospital": hospital if is_doctor else '',
        "case_num": case_num,
        "title": DOCTOR_TITLE_TYPE.getDesc(doctor.title)
        if is_doctor and doctor.title != DOCTOR_TITLE_TYPE.EMPTY else '',
    }


@bind('api/doctor/query_info')
@list_interface(limit_name='limit')
@thread_local(DB_FOR_READ_OVERRIDE=settings.SLAVE_DB_NAME)
def query_doctor_info(name=None, limit=50):
    """
    NOTE:
        DESC: 根据name模糊查询doctor信息
        :type limit: object
        :param name:
        :return:
        change log: 2015-12-08
    """

    if not name:
        return []

    def get_data(f, default=None):
        try:
            return f()
        except Exception:
            return default

    def get_doctor_data(doctor):
        assert isinstance(doctor, Doctor)
        title = DOCTOR_TITLE_TYPE.getDesc(doctor.title) if doctor.title != DOCTOR_TITLE_TYPE.EMPTY else ''

        return {
            'id': doctor.id,
            'name': doctor.name,
            'is_hospital': False,
            'portrait': doctor.portrait,
            'type': 0,
            'title': title,
            'hospital': get_data(lambda: doctor.hospital.name),
            'city': get_data(lambda: doctor.hospital.city.name),
        }

    def get_hospital_data(hospital):
        assert isinstance(hospital, Hospital)
        return {
            'id': hospital.officer and hospital.officer.id,
            'name': hospital.name,
            'is_hospital': True,
            'portrait': hospital.officer and hospital.officer.portrait or hospital.portrait,  # 机构默认头像
            'type': 1,
            'title': None,
            'hospital': '',
            'city': get_data(lambda: hospital.city.name),
        }

    doctor_queryset = Doctor.objects.filter(is_online=True, name__contains=name)[:limit]
    hospital_queryset = Hospital.objects.filter(name__contains=name)[:limit]

    data = []
    queryset = itertools.chain(doctor_queryset, hospital_queryset)

    doctor_id_set = set()
    for obj in queryset:
        if isinstance(obj, Doctor):
            if obj.doctor_type == DOCTOR_TYPE.DOCTOR:
                participant = get_doctor_data(obj)
            elif obj.doctor_type == DOCTOR_TYPE.OFFICER and obj.hospital:
                participant = get_hospital_data(obj.hospital)
        elif isinstance(obj, Hospital):
            participant = get_hospital_data(obj)
        else:
            participant = None

        if not participant:
            continue

        if participant["id"] and participant['id'] not in doctor_id_set:
            doctor_id_set.add(participant['id'])
            data.append(participant)

    return data


@bind('api/doctor/can_create_topic')
def can_create_topic(id=0, user_id=0):
    try:
        if id:
            doctor = Doctor.objects.get(id=id, is_online=True)
        elif user_id:
            doctor = Doctor.objects.get(user_id=user_id)
        can_create_topic = doctor.can_create_topic()
        result = {
            'can_create_topic': can_create_topic
        }
        return result
    except Doctor.DoesNotExist:
        return gen(CODES.DOCTOR_NOT_FOUND)


@bind('api/doctor/iterator')
@list_interface(limit_name='count')
def doctor_iterator(start_num=0, count=10, is_online=True):
    doctors = Doctor.objects.filter(is_online=is_online, doctor_type=DOCTOR_TYPE.DOCTOR)
    return [d.data() for d in doctors[start_num:start_num + count]]


@bind('api/doctor_officer/iterator')
@list_interface(limit_name='count')
def doctor_iterator(start_num=0, count=10, is_online=True):
    '''
    自身在线，并且美购在线的机构管理者(name同机构名)
    '''
    doctors = Doctor.objects.filter(is_online=is_online, services__is_online=is_online, doctor_type=DOCTOR_TYPE.OFFICER)
    return [d.data() for d in doctors[start_num:start_num + count]]


@bind('api/doctor/recommend')
def doctor_recommend(doctor_id=0):
    # todo 7660 上线后可删，已无调用
    result = {}
    try:
        doctor = Doctor.objects.get(id=doctor_id, is_online=True)
    except:
        return result

    rate = doctor.rate
    if rate - math.trunc(rate) >= 0.5:
        star = math.trunc(rate) + 0.5
    else:
        star = math.trunc(rate)

    number = Diary.objects.filter(
        doctor_id=doctor.id,
        is_online=True,
        rating__gt=0
    ).distinct().count()

    result = {
        'star': round(star, 1),
        'number': number,
        'effect_rating': number_floor(doctor.effect_rating, 1),
        'attitude_rating': number_floor(doctor.attitude_rating, 1),
        'env_rating': number_floor(doctor.env_rating, 1),
    }
    return result


@bind('api/doctor/recent_join')
@list_interface(limit_name='count')
def recent_join_doctors(start_num=0, count=10, is_online=True):
    doctors = Doctor.objects.filter(is_online=is_online).order_by('-created_at')[:count]
    return [doctor.doctor_detail_v2() for doctor in doctors]


@bind('api/doctor/basic_info')
@bind('api/doctor/basic_info_by_user_ids')
def basic_info(doctor_ids=[], user_ids=[]):
    if doctor_ids:
        if len(doctor_ids) > 100:
            doctor_ids = doctor_ids[:100]  # 防止数量,防止被爬库
        result = Doctor.objects.filter(id__in=doctor_ids).select_related('hospital').only('id', 'name', 'portrait', 'hospital') \
            .filter(is_online=True)
    elif user_ids:
        user_ids = user_ids[:100]
        result = Doctor.objects.filter(user_id__in=user_ids).select_related('hospital').only('id', 'name', 'portrait', 'hospital') \
            .filter(is_online=True)
    else:
        result = []

    data = []
    for res in result:
        doctor = {
            'doctor_id': res.id,
            'doctor_name': res.name,
            'doctor_portrait': res.portrait,
            'hospital_name': res.hospital.name if res.hospital else u'',
            'hospital_id': res.hospital.id if res.hospital else u'',
            'hospital_portrait': res.hospital.portrait if res.hospital else u'',
            'user_id': res.user_id,
            'doctor_type': res.doctor_type,
            'title': res.title,
        }
        data.append(doctor)
    return data


@bind('api/doctor/active')
def add_doctor_active(user_id):
    doctor = get_doctor_by_user_id(user_id)
    if doctor:
        day = datetime.date.today()
        doctor_view, new = DoctorView.objects.get_or_create(stat_date=day, doctor=doctor)
        doctor_view.is_active = True
        doctor_view.save()


@bind('api/doctor/sitemap')
def get_doctorlist_sitemap(doctor_type=DOCTOR_TYPE.DOCTOR,
                           count=settings.BUILD_SITEMAP_COUNT):
    doctor_info = []
    doctor_list = Doctor.objects.filter(doctor_type=doctor_type).order_by('-online_time')
    doctor_list = doctor_list[0:count]
    for doctor in doctor_list:
        doctor_info.append({
            'id': doctor.id,
            'online_time': get_timestamp_or_none(doctor.online_time),
        })
    return doctor_info


@bind('api/doctor/register_apply')
def client_doctor_register(doctor_info):
    dr = DoctorRegisterInfo.objects.filter(phone=doctor_info['phone'])
    if dr.count() == 0:
        try:
            DoctorRegisterInfo.objects.create(**doctor_info)
            return True
        except:
            logging_exception()
            return gen(CODES.UNKNOWN_ERROR)
    else:
        return gen(CODES.USER_ALREADY_REIGST)


@bind('api/doctor/diary_index_discount_score')
def doctor_discount(id):
    try:
        d = Doctor.objects.get(id=id)
    except Doctor.DoesNotExist:
        return gen(CODES.DOCTOR_NOT_FOUND)
    discount = DoctorCountRecord.get_diary_index_score(d)
    return discount


@bind('api/doctor/phone_ext')
def get_doctor_phone_ext(ext=None):
    result = []
    doctors = Doctor.objects.filter(phone_ext=ext).values('phone')
    for doctor in doctors:
        phone = doctor['phone']
        result.append(phone)

    return result


@bind('api/phone/record_ext_and_call_id')
def get_record_ext_and_call_id(ext, call_id):
    result = {'recorded': False}

    if not (call_id and ext):
        return result

    objs = PhoneCallRecord.objects.filter(call_id=call_id)
    if objs.exists():
        return result

    phone_call_record = PhoneCallRecord()
    phone_call_record.phone_ext = ext
    phone_call_record.call_id = call_id
    phone_call_record.save()
    result['recorded'] = True

    return result


@bind('api/doctor/hera_search')
def doctor_hera_search(id_query=None, doctor_type=None, offset=0, size=10):
    assert id_query is not None
    doctors = Doctor.objects.filter(id__startswith=id_query)
    if doctor_type is not None:
        doctors = doctors.filter(doctor_type=doctor_type)
    doctors = doctors[offset:size]
    res = []
    for doctor in doctors:
        res.append(doctor.doctor_detail_v2(is_favored=False, need_intro=False))

    return res


@bind('api/doctor/get_id_by_tags')
def get_doctor_id_by_tags(tag_ids, offset=0, size=10, sort_type=DOCTOR_ORDER_TYPE.DEFAULT2):
    doctor_ids = []
    filters = {'tag_ids': tag_ids}
    doctors_result = filter_doctor(
        offset=offset, size=size,
        filters=filters, sort_type=sort_type
    )
    doctor_ids.extend(doctors_result.get('doctor_ids'))
    return doctor_ids


@bind('api/doctor/get_famous_doctor_badge')
@cache_page(10*60)
def get_famous_doctor_badge(ids=[], query_type=SERVICE_FLAG.DOCTOR):
    """
    :return {"doctor_id1": ["最佳鼻部", "最佳眼眉"], "doctor_id2": ["最佳眼眉"], "doctor_id3": [], ...}
     """
    res = {}
    if query_type == 'normal':
        ars = AwardResult.objects.filter(laureate_id__in=ids)
    elif query_type == SERVICE_FLAG.DOCTOR:
        ars = AwardResult.objects.filter(laureate_type=SERVICE_FLAG.DOCTOR)
    else:
        ars = AwardResult.objects.filter(laureate_type=SERVICE_FLAG.HOSPITAL)

    ars = ars.filter(is_deleted=False)
    for ar in ars:
        res[ar.laureate_id] = ar.award_name.split(',')

    return res


@bind('api/doctor/iterator_for_inner')
def doctor_iterator_for_inner(start_num=0, count=10):
    '''仅供内部调用!!!'''
    return_dict = {
        'ids': [],
        "doctors": {},  # todo 等 mip 上线之后删除，勿动！！！
    }

    doctors = Doctor.objects.filter(is_online=True)
    for d in doctors[start_num:start_num + count]:
        return_dict['ids'].append(d.id)
        user_id = d.user_id
        if user_id:
            return_dict["doctors"].update({
                user_id: {
                    "doctor_id": d.id,
                    "doctor_title": d.title,
                }
            })

    return return_dict


@bind("api/doctor/list_by_service_ids")
def get_doctors_list_by_service_ids(service_ids):
    """根据美购ID获取对应的医生。"""

    services = Service.objects.filter(id__in=service_ids).select_related("doctor")
    if not services:
        return {}

    res = {}
    for service in services:
        res[service.id] = {
            "id": service.doctor_id,
            "hospital_id": service.doctor.hospital_id,
        }

    return res


@bind('api/batch_get_doctor_info_by_doctor_ids')
@cache_page(2 * 60)
def batch_get_doctor_info_by_doctor_ids(doctor_ids, with_fields=None):
    """
    :param doctor_ids:
    :param with_fields:
    :return:
    """
    result = {}
    if with_fields is None:
        with_fields = []
    doctors = Doctor.objects.filter(id__in=doctor_ids).all()
    for doctor in doctors:
        data = {}
        if 'meta' in with_fields:
            data['meta'] = {
                'id': doctor.id,
                'name': doctor.name,
                'display_title': doctor.doctor_title_display or "执业医师",
                'title': doctor.title,
                'doctor_type': doctor.doctor_type,
                'hospital_id': doctor.hospital_id,
                'portrait': get_full_path(doctor.portrait or settings.DOCTOR_DEFAULT_PORTRAIT_KEY),
            }
        result[str(doctor.id)] = data
    return result


@bind('api/batch_get_doctor_info_by_user_ids')
@cache_page(2 * 60)
def batch_get_doctor_info_by_user_ids(user_ids, with_fields=None):
    """
    :param doctor_ids:
    :param with_fields:
    :return:
    """
    result = {}
    if with_fields is None:
        with_fields = []
    doctors_qs = Doctor.objects.prefetch_related('hospital').filter(user_id__in=user_ids).all()
    for doctor in doctors_qs:
        data = {}
        if 'meta' in with_fields:
            data['meta'] = {
                'id': doctor.id,
                'name': doctor.name,
                'display_title': doctor.doctor_title_display or "执业医师",
                'title': doctor.title,
                'doctor_type': doctor.doctor_type,
                'portrait': get_full_path(doctor.portrait or settings.DOCTOR_DEFAULT_PORTRAIT_KEY),
                'hospital_id': doctor.hospital_id,
                'hospital_exists': bool(doctor.hospital),
                'hospital_name': doctor.hospital.name if doctor.hospital else None,
            }
        result[doctor.user_id] = data
    return result


@bind('api/batch_get_toc_doctor_info_by_doctor_ids')
def batch_get_toc_doctor_info_by_doctor_ids(doctor_ids, is_extra=False):
    """
    :param doctor_ids:
    :param is_extra:
    :return:
    """

    result = {}
    doctors = Doctor.objects.filter(id__in=doctor_ids).all()

    for doctor in doctors:
        data = {
            'id': doctor.id,
            'name': doctor.name,
            'display_title': doctor.doctor_title_display or "执业医师",
            'title': DOCTOR_TITLE_TYPE.getDesc(doctor.title),
            'doctor_type': doctor.doctor_type,
            'portrait': get_full_path(doctor.portrait or settings.DOCTOR_DEFAULT_PORTRAIT_KEY),
        }

        if is_extra:
            hospital = doctor.hospital
            data.update({
                'hospital': hospital.name,
                'hospital_lat': hospital.baidu_loc_lat,
                'hospital_lng': hospital.baidu_loc_lng,
                'rate': doctor.rate,
                'case': doctor.cases(),
                'good_at': doctor.good_at,
                'subscribe_num': doctor.sell_amount_display(),
                'share_amount': doctor.share_diary_num,
                'doctor_user_id':  doctor.user.id if doctor.user else None,
                'accept_private_msg': doctor.accept_private_msg,
            })
        result[str(doctor.id)] = data
    return result


@bind_context('api/doctor_toc_doctor_info')
def doctor_toc_doctor_info(ctx,doctor_id):
    """
    author: oldman
    7790 医生主页医生的基础信息
    :param doctor_id:医生ID
    :return:
    """
    do = Doctor.objects.get(id=doctor_id)
    info = do.doctor_detail_v2(need_intro=True)
    basic_info = {
        "doctor_id": info["doctor_id"],
        "name": info["name"],
        "badges": info["badges"],
        "is_special_appearance": info["is_special_appearance"],  # 名医大赏是否有特殊皮肤
        "display_tags": info["display_tags"],
        "title": info["title"],
        "practice_time": info["practice_time"],
        "rate": info["rate"],
        "doctor_type": info["doctor_type"],
        "subscribe_num": info["subscribe_num"],
        "p_licences": do.p_licences,
        "b_licences": do.b_licences,
        "hospital_name": info["hospital_name"],
        "address": info["address"],
        "lat": info["lat"],
        "lng": info["lng"],
        "portrait": info["portrait"],
        "introduction": info["introduction"],
        "service_amount": do.get_doctor_service_num(),
        "hospital_id": info["hospital_id"],
        "good_at": info["good_at"],
        "hospital_type": info["hospital_type"],
    }
    related_info = {
        "accept_private_msg": do.accept_private_msg,
        "accept_call": do.accept_call,
        "phone_ext":do.phone_ext,
        "phone":PhoneService.get_phone_prefix(do.phone_ext),
        "user_id": info["user_id"]
    }
    is_following = False
    user = get_user_from_context(ctx)
    if user:
        social_info = SocialInfo(uid=user.id)
        is_following = social_info.is_following_user(uid=do.user.id)
    basic_info['is_following'] = is_following
    return {"basic_info": basic_info,
            "related_info": related_info}


@bind_context('api/doctors_follow_info')
def doctors_follow_info(ctx, doctor_ids):
    """
    用户医生关注信息
    :param doctor_ids:医生ID列表
    :return:
    """
    user = get_user_from_context(ctx)
    if not user:
        return {}

    follow_info = {}
    social_info = SocialInfo(uid=user.id)

    doctors = Doctor.objects.filter(pk__in=doctor_ids)
    for doctor in doctors:
        is_following = social_info.is_following_user(uid=doctor.user_id)
        follow_info[doctor.id] = is_following

    return follow_info


@bind('api/doctor/diary_tags')
def diary_tags(doctor_id, tag_v3=False):
    """
    获取医生的案例标签
    :param doctor_id:
    :param tag_v3:bool 是否用标签3.0
    :return:
    """
    do = Doctor.objects.get(id=doctor_id)
    items = do.get_doctor_item_list(tag_v3=tag_v3)
    return {
        "item_list": items[0],
        "item_list_raw": items[1]
    }


@bind('api/get_officer_by_hospital_id')
def get_officer_by_hospital_id(hospital_id):
    if not hospital_id:
        return {}

    doctor = Doctor.objects.filter(hospital_id=hospital_id, doctor_type=DOCTOR_TYPE.OFFICER).first()
    if not doctor:
        return {}

    return {'officer_id': doctor.id}


@bind('api/doctor/merchant_info')
def get_doctor_merchant_info(doctor_id):
    from hippo.models import MerchantRelevance
    merchant = MerchantRelevance.objects.get(doctor_id=doctor_id).merchant
    return {
        'merchant_doctor_id': merchant.doctor_id,
        'merchant_id': merchant.id
    }


@bind('api/doctor/card_search')
def doctor_card_search(doctor_type, q):
    """
    生成医生、机构卡片功能 搜索接口
    :param type:
    :param q:
    :return:
    """
    result = []
    if doctor_type == '0':
        hospital_ids = Doctor.objects.filter(doctor_type=1, is_online=1).values_list('hospital_id')
        objs = Doctor.objects.filter(doctor_type=doctor_type, hospital_id__in=hospital_ids, name__contains=q).only('hospital').order_by('-hospital__created_time',
                                                                                  '-created_at')
        for obj in objs:
            try:
                result.append({'doctor_name': obj.name, 'hospital_name': obj.hospital.name, 'doctor_id': obj.id, 'is_officer': False})
                if len(result) == 20:
                    break
            except:
                pass
    elif doctor_type == '1':
        objs = Doctor.objects.filter(doctor_type=doctor_type, name__contains=q, is_online=True).only('hospital').order_by('-hospital__created_time', '-created_at')
        for obj in objs:
            try:
                result.append({'city_name': obj.hospital.city.name, 'hospital_name': obj.hospital.name,
                               'doctor_id': obj.id, 'is_officer': True})
                if len(result) == 20:
                    break
            except:
                pass

    return result


@bind('api/get_merchant_doctor_ids_by_hospital_id')
def get_doctors_by_hospital_id(hospital_id):
    '''机构下的所有商户医生'''
    if not hospital_id:
        return None
    doctor_qs_ids = Doctor.objects.filter(hospital_id=hospital_id, is_merchant=True).values_list('id', flat=True)
    if doctor_qs_ids:
        return ','.join(set(doctor_qs_ids))
    return None


doctor_cache_wrap = cache_wrap(doctor_search_tags_cache, pre_fix='doctor_feed_info', expire=7200, time_range=600)


@bind('api/doctor/feed_info')
def gets_doctor_feed_infos(ids):
    if not ids:
        return {}
    if len(ids) > 100:
        return {}
    return _gets_doctor_feed_infos(ids)


@doctor_cache_wrap.batch
def _gets_doctor_feed_infos(ids):
    model = Doctor
    objs = model.objects.filter(id__in=ids)

    res = {}
    for obj in objs:
        info = {
            'accept_private_msg': obj.accept_private_msg,
            'is_recommend': obj.is_recommend,
            'rate': obj.rate,
            'portrait': obj.portrait,
            'hospital_name': obj.hospital.name,  # hospital name
            'hospital_id': obj.hospital.id,
            'hospital_type': obj.hospital.hospital_type,
            'doctor_type': obj.doctor_type,
            'city_id': obj.hospital.city_id,
            'lng': obj.hospital.baidu_loc_lng if obj.hospital.baidu_loc_lng else obj.hospital.google_loc_lng,
            'lat': obj.hospital.baidu_loc_lat if obj.hospital.baidu_loc_lat else obj.hospital.google_loc_lat,
        }

        info['did'] = info['doctor_id'] = obj.id
        lincence = obj.get_doctor_lincence(obj.title, obj.get_doctor_service_num())
        info['title'] = lincence['title']
        info['name'] = obj.name
        service_amount = obj.get_doctor_service_num()
        info['show_v'] = '1' if service_amount > 0 else '0'
        info['show_rating'] = '1' if obj.rate >= 1 else '0'
        info['subscribe_num'] = obj.sell_amount_display()
        info['accept_call'] = obj.accept_call
        info['share_amount'] = obj.share_diary_num
        info['extend_tips'] = ""
        info['items'] = obj.operate_tag()
        info['service_num'] = service_amount
        badges, is_special_appearance = obj.get_award_name_and_is_special_skin()
        info['is_special_appearance'] = is_special_appearance
        info['badges'] = badges
        try:
            hospital_scale = obj.hospital.hospital_extra.get_scale_desc()
        except:
            hospital_scale = ''
        operate_tags = obj.operate_tag()
        info['display_tags'] = obj.format_display_tags(hospital_scale, badges, operate_tags)
        info['is_hospital'] = True
        info['cases'] = obj.cases()
        res[obj.id] = info
    return res


@bind('doctor/statement/merchant_rank')
def merchant_rank(doctor_id):
    merchant_rank_info = {"merchant_rank": "unknown", "merchant_name": "unknown"}
    try:
        doctor = Doctor.objects.filter(id=doctor_id).first()
        if not doctor:
            return merchant_rank_info
        merchant = doctor.merchant
        if merchant:
            rank = merchant.merchant_level
            name = merchant.name
            merchant_rank_info = {"merchant_rank":rank,"merchant_name":name}
        return merchant_rank_info
    except:
        logging_exception()
        return merchant_rank_info


@bind('doctor/statement/merchant_rank_indicator')
def merchant_rank_indicator(doctor_id,start_time,end_time):
    try:
        end_time_plus = (datetime.datetime.strptime(end_time,"%Y-%m-%d") + datetime.timedelta(days=1))\
            .strftime('%Y-%m-%d')
        merchant_id = MerchantRelevance.objects.get(doctor_id=doctor_id).merchant_id
        doctor_ids = list(MerchantRelevance.objects.filter(merchant_id=merchant_id).values_list('doctor_id', flat=True))
        invoker = create_default_invoker(debug=settings.DEBUG)
        rpc_client = invoker['artemis/account/accountflow/new_sum'](doctor_ids={str(merchant_id): doctor_ids},
                                                                    start_time=start_time, end_time=end_time_plus)
        res = rpc_client.unwrap()
        ad_consume = res.get(str(merchant_id), 0.0)
        # 订单
        orders = Order.objects.filter(service__doctor_id__in=doctor_ids, validate_time__gte=start_time,
                                      validate_time__lt=end_time_plus, status=ORDER_STATUS.USED)
        res_dict = orders.aggregate(sum=Sum("discount"))
        order_sum = res_dict.get("sum") if res_dict.get("sum") else 0.00
        # 退款率计算
        pay_orders = Order.objects.filter(service__doctor_id__in=doctor_ids, pay_time__gte=start_time,
                                          pay_time__lt=end_time_plus,
                                          status__in=[ORDER_STATUS.PAID, ORDER_STATUS.REFUNDED, ORDER_STATUS.USED,
                                                      ORDER_STATUS.SETTLED, ORDER_STATUS.WAIT_REFUNDED]).only('id')
        pay_order_ids = list(pay_orders.values_list('id', flat=True))
        refund_orders = RefundOrder.objects.filter(order_id__in=pay_order_ids, created_at__gte=start_time,
                                                   created_at__lt=end_time_plus,
                                                   status__in=[REFUND_STATUS.REFUNDED, REFUND_STATUS.DOCTOR_APPROVE])
        refund_proportion = refund_orders.count() / float(pay_orders.count()) if pay_orders.count() else 0.0

        # 买单
        res_dict = MaidanOrder.objects.filter(doctor_id__in=doctor_ids, payment_time__gte=start_time,
                                              payment_time__lt=end_time_plus,
                                              status=ORDER_STATUS.PAID).aggregate(sum=Sum("discount_cent"))
        maidan_sum = res_dict.get("sum") / 100.0 if res_dict.get("sum") else 0.00
        # 补单录入
        res_dict = BuDanLuRu.objects.filter(doctor_id__in=doctor_ids, created_time__gte=start_time,
                                            created_time__lt=end_time_plus, status=BUDAN_LURU_STATUS.ENTERED,
                                            type=BUDAN_LURU_TYPE.COMMON).aggregate(sum=Sum("amount"))
        budanluru_sum = res_dict.get("sum") if res_dict.get("sum") else 0.00

        traffic_stat_objs = TrafficStat.objects.filter(doctor_id__in=doctor_ids, date__gte=start_time,
                                                       date__lte=end_time).values('date').annotate(
            service_pv_num=Sum('service_pv_num'), home_pv_num=Sum('home_pv_num'))
        pv_num = 0

        from hippo.views.stats import gen_pv
        for stat in traffic_stat_objs:
            # 页面浏览量
            pv_num += gen_pv(stat['service_pv_num'], stat['home_pv_num'])
        verify_pv_amount = (order_sum + maidan_sum + budanluru_sum) * 10 / pv_num if pv_num else 0

        return {"valid_verify_order_book": (order_sum + maidan_sum + budanluru_sum) * 10, "ad_consume": ad_consume,
                "per_pv_verify": verify_pv_amount, "refund": refund_proportion}

    except:
        logging_exception()
        return dict()


@bind('doctor/statement/merchant_rank_detail')
def merchant_rank_detail(doctor_id,page):
    total = 0
    result = []
    try:
        merchant_id = MerchantRelevance.objects.get(doctor_id=doctor_id).merchant_id
        tmp = MerchantLevelRecord.objects.filter(merchant_id=merchant_id)
        total = tmp.count()
        if total:
            obj = tmp.order_by("-created_time")[int(page)*10:int(page)*10+10]
            for record in obj:
                result.append({
                    "month": record.created_time.strftime("%Y-%m"),
                    "merchant_rank": record.merchant_level,
                    "last_3_month_verify_GMV_mean": record.verify_gmv_amount,
                    "last_3_month_ad_consume_mean": record.advertise_consume_amount,
                    "last_3_month_pv_verify_mean": record.verify_pv_amount,
                    "last_3_month_refund":record.refund_proportion
                })

        return {"total":total,"items":result}
    except:
        logging_exception()
        return {"total":total,"items":result}


@bind('query/merchant/level')
def get_merchant_level(doctor_id):
    merchant = MerchantRelevance.objects.get(doctor_id=doctor_id).merchant
    return {'merchant_level': merchant.merchant_level}
