# coding=utf-8
import math
from django.conf import settings
from django.utils import timezone
from gm_types.gaia import TAG_TYPE, ADVER_MANAGEMENT_POSITION_TYPE, DOCTOR_TYPE, HOSPITAL_TYPE
from answer.models import Answer
from api.business.tag import TagControl
from api.models import AdvertiseManagement, ServiceTag, Tag, Doctor
from api.models.message import Message
from api.models.tickets import Punishment
from api.models.counsellor import Counsellor
from statistic.models import DoctorRankFactor
from hippo.models import MerchantRelevance
from talos.models.diary import Diary
from talos.models.topic import Problem
from trans2es.doris.doctor import get_doctor_stat
from trans2es.graph import g
from .es import tzlc, to_epoch, update_max_min
from .transfer import get_area_tag_info, MAX_TIME
from search.utils.area import get_nearby_city_tag_ids
from gm_types.consultation import COUNSELLOR_TYPE, COUNSELLOR_STATUS
from gm_types.consultation import CONSULTATION_TABS
import json
import redis
from rpc.tool.log_tool import logging_exception
from statistic.models import DoctorRankFactor, HospitalRankFactor
from rpc.context import get_rpc_remote_invoker
from trans2es.utils.service_transfer import get_smart_rank_v4
import time
import datetime
from api.models.counsellor import Consultant,CounsellorSmartRank
from statistic.models import TranslateTreatmentScore
from hippo.models.merchant import Merchant,Account,PriorityBDtransfer,BDtransferConsume,MerchantRelevance


def get_advertise_info(d):
    """ a tuple of (advertise_position, advertise_searchwords, advertise_info)
    """
    # 只选出未过期的广告
    now = timezone.now()
    ad_positions = set()
    ad_searchwords = set()
    ad_tag_ids = set()
    ad_info = []
    ads = AdvertiseManagement.objects.filter(
        doctor=d, is_online=True, end_time__gt=now)
    for ad in ads:
        show_positions = set(ad.get_show_position)
        ad_positions.update(show_positions)
        if ADVER_MANAGEMENT_POSITION_TYPE.SREACH_RESULT in show_positions:
            ad_searchwords.update(set(ad.get_search_words))
        ad_info.append({
            'id': ad.id,
            'position': list(show_positions),
            'start_time_epoch': to_epoch(tzlc(ad.start_time)),
            'end_time_epoch': to_epoch(tzlc(ad.end_time) if ad.end_time else MAX_TIME),
            'searchwords': ad.get_search_words,
            "show_city_tags": ad.show_city_tags(),
            'ordering': ad.ordering,
        })
        ad_tag_ids.update([t.id for t in ad.tags.all()])
    return (list(ad_positions), list(ad_searchwords), ad_info, list(ad_tag_ids))


def get_doctor(instance):
    d = instance

    res = {
        'id': d.id,
        'name': d.name,
        'name_pre':d.name,
        'name_highlight':d.name,
        'name_raw': d.name,
        'is_online': d.is_online,
        'is_recommend': d.is_recommend,
        'accept_private_msg': d.accept_private_msg,
        'is_authenticated': True if d.phone else False,
        'doctor_type': d.doctor_type,
        'famous_doctor': d.big,
        'user_id': d.user_id
    }

    # sink
    tickets_result = getattr(d, 'ticketsresult', None)
    if tickets_result:
        res['ticket_sink_start_time'] = tzlc(tickets_result.start_sink)
        res['ticket_sink_end_time'] = tzlc(tickets_result.end_sink)

    # hospital info
    if d.hospital:
        res['hospital'] = {
            'id': d.hospital.id,
            'name': d.hospital.name,
            'name_pre':d.hospital.name,
            'hospital_type': d.hospital.hospital_type,
        }
        res['hospital']['name'] = d.hospital.name
        res['hospital']['name_pre'] = d.hospital.name
        res['hospital']['name_highlight'] = d.hospital.name

        hospital_doctors = Doctor.objects.filter(doctor_type=DOCTOR_TYPE.DOCTOR, hospital=d.hospital, is_online=True)
        res['hospital']['doctor_names'] = list(hospital_doctors.values_list('name', flat=True))
        # hospital position info
        if d.hospital.city:
            h = res['hospital']
            h['city_name'] = d.hospital.city.name
            h['city_province_name'] = d.hospital.city.province.name
            h.update(get_area_tag_info(d.hospital.city))
        if d.hospital.officer:
            h = res['hospital']
            h['officer_name'] = d.hospital.officer.name

    # problem info
    res['problems'] = []
    problems = Problem.objects.prefetch_related('problemtag_set').filter(is_online=True, user_id=d.user_id)

    for p in problems:
        res['problems'].append({
            'id': p.id,
            'tags': [t.name for t in Tag.objects.filter(id__in=[pt.tag_id for pt in p.problemtag_set.all()])]
        })
    last_update_time = d.last_answered_time
    res['last_update_time'] = tzlc(last_update_time)

    try:
        merchant_id = MerchantRelevance.objects.get(doctor_id=d.id).merchant_id
        translate_treatment_score = get_merchant_translate_treatment_score(merchant_id)
        res["translate_treatment_score"] = translate_treatment_score
        res["merchant_level"] = Merchant.objects.get(id=merchant_id).merchant_level
        is_priority_send_orders, left_money = get_merchant_translate_treatment_balance(merchant_id)
        res["merchant_translate_treatment_left_consume"] = left_money
        res["merchant_left_money"] = get_merchant_balance(merchant_id)
        res["is_merchant_priority_send_order"] = is_priority_send_orders
    except:
        logging_exception()

    cpc_price = 0
    res["is_cpc"] = False
    res['doctor_score'] = 0
    invoker = get_rpc_remote_invoker()
    doctor_type = "doctor"

    try:
        if res['doctor_type'] == DOCTOR_TYPE.DOCTOR:
            rpc_client = invoker['artemis/cpc_doctor/is_promote'](doctor_id=d.id)
            rpc_result = rpc_client.unwrap()
            res["is_cpc"] = rpc_result["is_promote"]
            res['doctor_score'] = DoctorRankFactor.get_doctor_score(d.id)
            update_max_min("doctor_score", res['doctor_score'])
            if res["is_cpc"]:
                cpc_price = int(rpc_result["price"])
        else:
            if d.hospital and d.hospital.officer:
                doctor_type = "officer"
                rpc_client = invoker['artemis/cpc_doctor/is_promote'](doctor_id=d.hospital.officer.id)
                rpc_result = rpc_client.unwrap()
                res["is_cpc"] = rpc_result["is_promote"]
                res['doctor_score'] = HospitalRankFactor.get_hospital_score(d.hospital.id, d.id)
                update_max_min("officer_score", res['doctor_score'])
                if res["is_cpc"]:
                    cpc_price = int(rpc_result["price"])
    except:
        logging_exception()

    doctor_score_100 = score_to_100(doctor_type + "_score", res['doctor_score'])
    if doctor_score_100 != -1:
        res['cpc_doctor_score'] = 0.7 * doctor_score_100 + cpc_price
    else:
        res['cpc_doctor_score'] = cpc_price

    # service info
    res['services'] = [{
        'id': s.id,
        'name': s.name,
        'short_description': s.short_description,
        'short_description_highlight': s.short_description,
        'short_description_pre':s.short_description,
        'all_text': s.get_all_text(),
        'doctor_score': get_score(get_smart_rank_v4(s), res['doctor_score'], doctor_type, cpc_price),
        'doctor_score_v1': get_score_v1(get_smart_rank_v4(s), res['doctor_score'], doctor_type),
        'start_time': tzlc(s.start_time),
        'end_time': tzlc(s.end_time) if s.end_time else MAX_TIME,
        'special_ids': s.get_can_sell_item_ids_and_current_special_id().values()
    } for s in d.services.filter(is_online=True)]
    if res['services']:
        res['has_service'] = True
    else:
        res['has_service'] = False

    # bodypart tags
    bodypart_tags = d.tags.filter(tag_type=TAG_TYPE.BODY_PART, is_online=True)
    res['bodypart_tags'] = [bp.name for bp in bodypart_tags]
    # bodypart subitem tag
    bodypart_subitem_tags = d.tags.filter(tag_type=TAG_TYPE.BODY_PART_SUB_ITEM, is_online=True)
    res['bodypart_subitem_tags'] = [bp_si.name for bp_si in bodypart_subitem_tags]
    # closure_tags
    closure_tags = TagControl.get_ancestors(
        initial_set=d.tags.filter(is_online=True),
        exclude_init=False,
        is_online_only=True)

    # 添加公立／私立TAG
    tag_id = None
    try:
        tag_id = settings.PUBLIC_TAG_ID if d.hospital.hospital_type == HOSPITAL_TYPE.PUBLIC else settings.PRIVATE_TAG_ID
        closure_tags.append(
            Tag.objects.get(pk=tag_id)
        )
    except AttributeError:
        # 医生未关联Hospital
        pass

    # 添加地域TAG
    try:
        closure_tags.append(
            Tag.objects.get(pk=d.hospital.city.tag_id)
        )
    except (Tag.DoesNotExist, AttributeError):
        pass

    res['closure_tag_ids'] = [t.id for t in closure_tags]
    res['service_closure_tags_type123'] = []
    service_tag_ids = None
    if d.doctor_type == DOCTOR_TYPE.DOCTOR:
        service_tag_ids = ServiceTag.objects.filter(
            service__doctor=d, service__is_online=True).values_list('tag_id', flat=True)
    elif d.hospital:
        service_tag_ids = ServiceTag.objects.filter(
            service__doctor__hospital=d.hospital, service__is_online=True).values_list('tag_id', flat=True)
    if service_tag_ids is not None:
        service_tags = Tag.objects.filter(
            pk__in=service_tag_ids,
            tag_type__in=[TAG_TYPE.BODY_PART, TAG_TYPE.BODY_PART_SUB_ITEM, TAG_TYPE.ITEM_WIKI])

        res['service_closure_tags_type123'] = [t.name for t in service_tags]
        service_tags = TagControl.get_ancestors(
            initial_set=service_tags,
            exclude_init=False,
            is_online_only=True)
        res['service_closure_tag_ids'] = [t.id for t in service_tags]
        res['service_closure_tags'] = [t.name for t in service_tags]
    else:
        res['service_closure_tag_ids'] = []
        res['service_closure_tags'] = []

    try:
        private_or_public_tag = Tag.objects.get(pk=tag_id)
        res['service_closure_tag_ids'].append(private_or_public_tag.id)
        res['service_closure_tags'].append(private_or_public_tag.name)
    except Tag.DoesNotExist:
        pass

    res['has_diary'] = False
    res['diary_count_over_thres'] = False
    diary_count = 0
    online_diary_set = Diary.objects.filter(is_online=True, doctor_id=d.id)
    for diary in online_diary_set:
        if diary.topics.filter(is_online=True).count() > 0:  # 日记本关联日记帖必须至少有一个上线
            diary_count += 1
            res['has_diary'] = True
            if diary_count >= 10:
                res['diary_count_over_thres'] = True
                break

    # http://bug.gengmei.cc/index.php?m=story&f=view&storyID=125
    # 医生 关联的 所有 上线的日记本 关联的 所有 类型属于 {一级分类、二级分类、三级分类} 的 Tag 的名字
    diary_tag_names = set()
    for diary in online_diary_set:
        if d.is_online:
            diary_tag_names.update(map(
                lambda tag: tag.name,
                filter(
                    lambda tag: tag.tag_type in [
                        TAG_TYPE.BODY_PART,
                        TAG_TYPE.BODY_PART_SUB_ITEM,
                        TAG_TYPE.ITEM_WIKI
                    ],
                    diary.tags
                )
            ))
    res['diary_tag_names'] = list(diary_tag_names)

    res['msg_last_update_time'] = res['last_update_time']
    if d.user_id:
        msg_last_update_time = Message.objects.filter(user_id=d.user.id).order_by('-send_time').first()
        if msg_last_update_time is not None:
            res['msg_last_update_time'] = max(res['msg_last_update_time'], tzlc(msg_last_update_time.send_time))

    # 广告信息
    res['advertise_position'], res['advertise_searchwords'], res['advertise_info'], res['advertise_tag_ids'] = \
        get_advertise_info(d)
    res["operation_cpt"] = res['advertise_info']

    # 案例数
    res['share_diary_num'] = d.share_diary_num

    # 美购数
    res['services_num'] = len(res['services'])

    # 互动贡献
    last_recommend_answer = Answer.objects.filter(
        user=d.user,
        is_recommend=True,
    ).order_by('create_time').last()
    res['last_recommend_answer_epoch'] = to_epoch(
        tzlc(last_recommend_answer.create_time)) if last_recommend_answer else 0

    # 评价指数
    res['effect_rating'] = d.effect_rating
    res['attitude_rating'] = d.attitude_rating
    res['env_rating'] = d.env_rating

    doris_data = get_doctor_stat(d.id)
    # {"last_30d_payment": 0.0, "last_30d_ad_money": 0.0, "ctr": 0.0, "last_30d_uv": 0.0}
    res['last_30d_payment'] = doris_data.get('last_30d_payment', 0.0)
    res['last_30d_ad_money'] = doris_data.get('last_30d_ad_money', 0.0)
    res['ctr'] = doris_data.get('ctr', 0.0)
    res['last_30d_uv'] = doris_data.get('last_30d_uv', 0.0)
    res['xiadan_index'] = doris_data.get('xiadan_index', 0.0)

    punishment = Punishment.get_punishment_by_doctor_id(d.id).get('doctor', None)
    res["is_sink"] = False
    if punishment and punishment.get('type', None) in ['limit', ]:
        res['org_sink_start_time'] = tzlc(punishment['start_time'])
        res['org_sink_end_time'] = tzlc(punishment['end_time'])
        now = datetime.datetime.now()
        now = time.mktime(now.timetuple())
        if time.mktime(res['org_sink_start_time'].timetuple()) <= now < time.mktime(
                res['org_sink_end_time'].timetuple()):
            res["is_sink"] = True

    # 排序得分 = 咨询因子开根号 * ctr开根号*（单pv佣金贡献+0.7*单pv的CPT消耗）
    res['rank_score'] = DoctorRankFactor.get_rank_score(instance.id)

    # 医生临近城市
    try:
        res['nearby_city_tag_ids'] = get_nearby_city_tag_ids(d.hospital.city.id)
    except (AttributeError, KeyError):
        res['nearby_city_tag_ids'] = []

    try:
        res['merchant_id'] = MerchantRelevance.objects.get(doctor_id=d.id).merchant_id
    except MerchantRelevance.DoesNotExist:
        res['merchant_id'] = None

    ###视频面诊
    face_doctor_info = Counsellor.objects.filter(doctor_id=d.id).first()

    if face_doctor_info:
        res['facevideo_is_onlie'] = True if face_doctor_info.status in (2,3) else False
        res['facevideo_is_status'] = face_doctor_info.status
        res['facevideo_is_assistant'] = face_doctor_info.is_assistant
        res['facevideo_good_at'] = json.loads(face_doctor_info.good_at)
        res['facevideo_good_at_ids'] = len(json.loads(face_doctor_info.good_at))
        res['facevideo_rec_rank'] = face_doctor_info.facevideo_rec_rank
        res['facevideo_rank'] = face_doctor_info.facevideo_rank
    else:
        res['facevideo_is_onlie'] = False
        res['facevideo_is_status'] = 0
        res['facevideo_is_assistant'] = 0
        res['facevideo_good_at'] = []
        res['facevideo_good_at_ids'] = 0
        res['facevideo_rec_rank'] = 0
        res['facevideo_rank'] = 0

    try:
        obj = CounsellorSmartRank.objects.filter(doctor_id=d.id).first()
        if obj:
            res["consult_score"] = obj.recent_1_hour_connected_rate * 243 + obj.today_connected_rate * 81 + \
                                   obj.avg_7_days_connected_rate * 27 + obj.avg_30_days_connected_rate * 9 + \
                                   obj.avg_3_days_valid_rate * 27 + obj.avg_7_days_valid_rate * 9 + \
                                   obj.avg_30_days_valid_rate * 3 + obj.avg_90_days_valid_rate
        else:
            res["consult_score"] = 0
    except:
        logging_exception()
        res["consult_score"] = 0
    try:
        diarys = Diary.objects.filter(is_online=True, doctor_id=d.id).distinct()
        diary_ids = [diary.id for diary in diarys]
        diary_tags = {diary_id: [t.name for t in Diary.objects.get(id=diary_id).tags] for diary_id in diary_ids}
        tags = []
        for id in diary_ids:
            tags += diary_tags[id]
        tags = list(set(tags))
        tag_count = []
        for tag_name in tags:
            count = 0
            for id in diary_ids:
                if tag_name in diary_tags[id]:
                    count += 1
            tag_count.append({"tag": tag_name, "count": count})
        res["tag_count"] = tag_count
    except:
        logging_exception()
    return res


# 把分数映射到0到100
def score_to_100(key, score):
    try:
        redis_client = redis.StrictRedis.from_url(settings.DORIS_URL)
        data_dcit = redis_client.hgetall(key)
        if b"max" in data_dcit and b"min" in data_dcit:
            max_score = float(data_dcit[b"max"])
            min_score = float(data_dcit[b"min"])
            score_100 = ((score - min_score) / (max_score - min_score)) * 100

            if score_100 < 0:
                score_100 = 0
            elif score_100 > 100:
                score_100 = 100

            return score_100
        else:
            return -1

    except:
        logging_exception()
        return -1


# 医院整体得分、美购得分映射到（0,100）之间,医院排序得分 = （医院整体得分*70%+品类得分*30%+CPC)
# 医生整体得分映、美购得分射到（0,100）之间,医生排序得分 =（医生整体得分*70%+品类得分*30%+CPC)
def get_score(service_score, score, content, cpc):
    try:
        key = content + "_score"
        score_100 = score_to_100(key, score)
        service_100_score = score_to_100("service_score", service_score)
        if score_100 == -1 or service_100_score == -1:
            return -1
        else:
            return 0.5*(0.7 * score_100 + 0.3 * service_100_score) + cpc
    except:
        logging_exception()
        return -1


# 医院整体得分、美购得分映射到（0,100）之间,医院排序得分 = （医院整体得分*70%+品类得分*30%)
# 医生整体得分映、美购得分射到（0,100）之间,医生排序得分 =（医生整体得分*70%+品类得分*30%)
def get_score_v1(service_score, score, content):
    try:
        key = content + "_score"
        score_100 = score_to_100(key, score)
        service_100_score = score_to_100("service_score", service_score)
        if score_100 == -1 or service_100_score == -1:
            return -1
        else:
            return 0.7 * score_100 + 0.3 * service_100_score
    except:
        logging_exception()
        return -1

def get_merchant_translate_treatment_score(merchant_id):
    score = 0.0
    try:
        yestoday_time = (datetime.datetime.now()-datetime.timedelta(days=1)).strftime('%Y-%m-%d')
        db_name = settings.DORIS_DB_NAME
        item = TranslateTreatmentScore.objects.using(db_name).filter(merchant_id=merchant_id,day_id=yestoday_time).first()
        if item:
            score = item.calc_score()
    except Exception as e:
        logging_exception()
    return score


def get_merchant_balance(merchant_id):

    """
    获取商户的余额
    """
    doctor_infos = MerchantRelevance.objects.filter(merchant_id=merchant_id)
    all_doctor_money = []
    for each_doctor_info in doctor_infos:
        doctor_id = each_doctor_info.doctor.id
        all_doctor_money.append(get_doctor_balance(doctor_id))
    return sum(all_doctor_money)


def get_doctor_balance(doctor_id):

    """
    获取医生的余额，余额=充值余额+返点余额
    """
    money = 0
    try:
        artimis_db_name = settings.ARTEMIS_DB_NAME
        doctor_info = Account.objects.using(artimis_db_name).filter(doctor_id=doctor_id).first()
        if doctor_info:
            money = int(doctor_info.rechange_amount)+int(doctor_info.cashback_amount)
    except Exception as e:
        logging_exception()
    return money


def get_merchant_translate_treatment_balance(merchant_id):

    """
    获取商户的转诊预算消耗度
    """
    artimis_db_name = settings.ARTEMIS_DB_NAME
    is_priority_send_orders = False
    left_money = 0
    try:
        priori_send_order_info = PriorityBDtransfer.objects.using(artimis_db_name).filter(merchant_id=merchant_id,is_open=True).first()
        budget = 0
        consume = 0
        if priori_send_order_info:
            is_priority_send_orders = True
            budget = priori_send_order_info.budget
            consume = get_merchant_translate_treatment_consume(merchant_id)
        left_money = budget - consume
    except Exception as e:
        logging_exception()
    return is_priority_send_orders, left_money


def get_current_timestr():
    return datetime.datetime.now().strftime("%Y-%m-%d")


def get_merchant_translate_treatment_consume(merchant_id):

    """
    获取当天转诊消耗总额
    """
    artimis_db_name = settings.ARTEMIS_DB_NAME
    try:

        translate_treatment_consume = BDtransferConsume.objects.using(artimis_db_name).filter(merchant_id=merchant_id,
                                                                                              date=get_current_timestr()).first()
        consume_money = 0
        if translate_treatment_consume:
            consume_money = translate_treatment_consume.amount
        return int(consume_money)
    except Exception as e:
        logging_exception()
        return 0