# coding=utf-8
from django.conf import settings

from libs.es import get_es, get_highlight, es_index_adapt
from libs.filters import area_tag_id_filter
from gm_types.gaia import DOCTOR_ORDER_TYPE
from gm_types.gaia import ADVER_MANAGEMENT_POSITION_TYPE

from gm_rpcd.all import bind


def process_filters(filters):
    # 过滤器部分
    f = [{'term':{'is_online':True}}] # 只返回上线的医生
    # 特殊规则:过滤掉特殊医生
    INVISIBLE_DOCTOR_ID = 'wanmeizhensuo'
    nf = [{'term':{'id':INVISIBLE_DOCTOR_ID}}]

    for k, v in filters.items():
        if k=='province_tag_id':
            f.append({
                'term':{'hospital.city_province_tag_id':v}
            })
        elif k == 'area_tag_id':
            f.append(area_tag_id_filter(['hospital.'], v))
        elif k=='tag_ids' and isinstance(v, list) and v:
            f.append({
                'terms':{'closure_tag_ids':v}
            })
        elif k=='service_tag_ids' and isinstance(v, list) and v:
            f.append({
                'terms':{'service_closure_tag_ids':v}
            })
        elif k=='bodypart_tag_id':
            f.append({
                'term':{'closure_tag_ids':v}
            })
        elif k=='doctor_type':
            f.append({
                'term':{'doctor_type':v}
            })
        elif k=='famous_doctor' and filters[k]:  # 大牌名医
            f.append({
                'term':{'famous_doctor':True}
            })
        elif k=='hospital_type':
            f.append({
                'term':{'hospital.hospital_type':v},
            })
        elif k=='name_raw':
            f.append({
                'term':{'name_raw':v},
            })
        elif k == 'adver_position':  # 广告展示位置
            f.append({
                'term':{'advertise_position': v}
            })
            if v==ADVER_MANAGEMENT_POSITION_TYPE.SREACH_RESULT and 'adver_word' in filters:
                filter_searchword = filters['adver_word']
                f.append({
                    'term':{'advertise_searchwords': filter_searchword}
                })
            else:
                filter_searchword = None

            f.append({
                'script':{
                    'script_file':'filter_doctor-advertise',
                    'lang':settings.ES_SCRIPT_LANG,
                    'params':{
                        'adver_position': v,
                        'adver_searchword': filter_searchword,
                    }
                }
            })
        elif k == 'adver_searchwords':
            pass  # see above

    return f, nf


def process_sorting(sort_type, sort_params, append_score=False):
    sorting = [
        {'_script': {
            'lang': settings.ES_SCRIPT_LANG,
            'script_file': 'sort_doctor-sink',
            'type': 'number',
            'order': 'asc',
        }},
    ]

    # 机构罚单下沉
    sorting += [
        {
            '_script': {
                'lang': settings.ES_SCRIPT_LANG,
                'script_file': 'sort_doctor-sink-by-org',
                'type': 'number',
                'order': 'asc',
            }
        }
    ]

    if sort_type == DOCTOR_ORDER_TYPE.POPULARITY: #好评专家
        sorting += [
            {'diary_count_over_thres':{'order':'desc'}},
            {'last_update_time':{'order':'desc'}},
        ]
    elif sort_type == DOCTOR_ORDER_TYPE.DEFAULT2:
        sorting += [
            {'_script':{
                'lang':settings.ES_SCRIPT_LANG,
                'script_file':'sort_doctor-selling-service',
                'type':'number',
                'order':'desc',
            }},
            {'has_diary':{'order':'desc'}},
            {'_script':{
                'lang':settings.ES_SCRIPT_LANG,
                'script_file':'sort_doctor-default2',
                'type':'number',
                'params':{
                    'user_city_tag_id':sort_params['user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1,
                },
                'order':'desc',
            }},
            {'is_recommend': {'order': 'desc'}},
            {'last_update_time':{'order':'desc'}},
        ]
    elif sort_type == DOCTOR_ORDER_TYPE.ONLINE_CONSULTATION: # 在线咨询
        sorting += [
            {'accept_private_msg':{'order':'desc'}},
            {'is_authenticated':{'order':'desc'}},
            {'msg_last_update_time':{'order':'desc'}},
        ]
    elif sort_type == DOCTOR_ORDER_TYPE.ADVERTISE_ORDER:
        sorting = [  # force not concerning is_sink
            {'_script':{
                'lang':settings.ES_SCRIPT_LANG,
                'script_file':'sort_doctor-advertise',
                'type':'number',
                'params':{
                    'adver_position': sort_params['adver_position'],
                    'adver_searchword': sort_params['adver_word'] if 'adver_word' in sort_params else None,
                },
                'order':'asc',
            }}
        ]
    else:  # DOCTOR_ORDER_TYPE.DEFAULT
        # 临时调权算法
        sorting += [
            {'_script':{
                'lang':settings.ES_SCRIPT_LANG,
                'script_file':'sort_doctor-search',
                'type':'number',
                'params':{
                    'user_city_tag_id':sort_params['user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1,
                    'city_tag_match_weight':20.0,
                    'has_diary_w':4.0,
                    'has_service_w':2.0,
                    'last_update_time_w':1.0,
                },
                'order':'desc',
            }},
        ]

    if append_score:
        sorting.append('_score')

    return sorting


def search_doctor(query='', offset=0, size=5, sort_type=DOCTOR_ORDER_TYPE.DEFAULT, filters=None, sort_params=None):
    """
    @param query: 搜索词
    @param user_city_tag_id: 用户所在城市id(如果有)
    @param offset: 偏移量
    @param size: 返回个数
    @param sort_type: 排序方式[POPULARITY<好评专家>]
    @param filters: 筛选器{"province_tag_id":<省份tag id>, "bodypart_tag_id":<一级tag id>}

    医生搜索
    搜索域:[
        1.姓名
        2.医院
        3.地点
        ]
    默认排序:[
        本地在前
        匹配度
        有相关日记在前
        有在售福利在前
        最新发帖/回复
        ]
    其它排序:[
        好评专家:(专家相关日记数大于10在前, 按最新发布/回复时间排序)
        ]
    """
    filters = filters or {}
    sort_params = sort_params or {}
    # 参数验证
    size = min(size, settings.COUNT_LIMIT)

    MAX_SCORE_SCALE = 0.05 # 低于次高分数1/20的文档将被过滤掉
                           # 最高可能是机构,分数太高

    # 搜索关键字部分
    # 搜索域
    multi_fields = {
        'name':8,
        'hospital.name':3,
        'hospital.city_name':2.5,
        'hospital.city_province_name':2.5,
        'diary_tag_names': 1,
    }
    fields = ['^'.join((k,str(v))) for (k,v) in multi_fields.items()]
    multi_match = {
        'query': query,
        'type': 'cross_fields',
        'operator':'and',
        'fields': fields,
        'boost':5,
    }

    q = {
        'bool':{
            'should':[
                {'multi_match':multi_match},
            ]
        }
    }

    nested_fields = {
        'services.name':2.5,  # 5.9.2 重新加上美购名字
    }
    for nf_key, nf_boost in nested_fields.items():
        q['bool']['should'].append({
            'nested':{
                'path':nf_key.split('.')[0],
                'score_mode':'sum',
                'query':{'match':{nf_key:query}},
                'boost':nf_boost,
            },
        })

    f, nf = process_filters(filters=filters)

    q = {
        'query':{'filtered':{
            'query':q,
            'filter':{'bool':{'must':f, 'must_not':nf}}
        }}
    }

    es = get_es()
    # 首先进行一次搜索，获得最高分数，然后通过和最高分数的相对值比较过滤结果
    index = es_index_adapt(
        index_prefix=settings.ES_INDEX_PREFIX,
        doc_type='doctor',
        rw='read'
    )
    res = es.search(
        index = index,
        doc_type = 'doctor',
        timeout = settings.ES_SEARCH_TIMEOUT,
        body = q,
        from_ = 0,
        size = 2)

    if not res['hits']['max_score']: # 没有匹配结果
        return res
    elif res['hits']['total'] < 2:
        max_score = res['hits']['max_score']
    else:  # >=2
        max_score = res['hits']['hits'][1]['_score']

    if res['hits']['total'] >= 12:
        q['min_score'] = max_score*MAX_SCORE_SCALE
    else:
        q['min_score'] = 0

    q['sort'] = process_sorting(sort_type=sort_type, sort_params=sort_params, append_score=True)

    # 高亮部分
    q['highlight'] = get_highlight((list(multi_fields.keys()) + list(nested_fields.keys())))
    q['track_scores'] = True

    res = es.search(
        index = index,
        doc_type = 'doctor',
        timeout = settings.ES_SEARCH_TIMEOUT,
        body = q,
        from_ = offset,
        size = size)
    '''
    {  
      "hits":{  
        "hits":[ //结果列表
          {  
            "sort":[...], //排序用数值
            "_source":{ //医生文档, 参见mapping/doctor.json
              "last_update_time":"2015-05-23T02:53:33+08:00", //注意日期值的返回值是字符串"YYYY-mm-ddTHH:ii:ss+ZZ",需要datetime时要自己转换
            },
            "highlight":{ //高亮字段
              "problem.tags":["\u5207\u5f00<em>\u53cc\u773c\u76ae</em>", "\u5207\u5f00<em>\u53cc\u773c\u76ae</em>", "\u5207\u5f00<em>\u53cc\u773c\u76ae</em>"], 
              "services.short_description":[...],
              "bodypart_subitem_tags":[...]
            },
            "_id":"like" //医生id
          }
        ],
        "total":125, //搜索结果总数
      },
    }
    '''
    return res


def filter_doctor(offset=0, size=5, sort_type=DOCTOR_ORDER_TYPE.DEFAULT2, filters=None, sort_params=None, nested_fields={}):
    filters = filters or {}
    sort_params = sort_params or {}

    # 参数验证
    size = min(size, settings.COUNT_LIMIT)



    f, nf = process_filters(filters=filters)

    if nested_fields:
        for key, val in nested_fields.items():
            filter_item = {}
            #过滤专题
            if key == "special_id":
                filter_item = {
                    'nested': {
                        'path': 'services',
                        'query': {
                            "bool":{
                                "must":[
                                    {'term': {'services.special_ids': val}}
                                ]
                            }
                        }
                    }
                }
            if filter_item:
                f.append(filter_item)


    q = {
        'query':{'filtered':{
            'query':{'match_all':{}},
            'filter':{'bool':{'must':f, 'must_not':nf}}
        }}
    }


    # 排序规则部分
    q['sort'] = process_sorting(sort_type=sort_type, sort_params=sort_params)

    # 特殊逻辑 对于广告 需要返回具体的广告ID
    if 'adver_position' in filters:
        q['script_fields'] = {
            'adver_id':{
                'script':{
                    'file':'field_doctor-advertise',
                    'params':{
                        'adver_position': filters['adver_position'],
                        'adver_searchword': filters['adver_word'] if 'adver_word' in filters else None,
                    }
                }
            }
        }

    es = get_es()
    index = es_index_adapt(
        index_prefix=settings.ES_INDEX_PREFIX,
        doc_type='doctor',
        rw='read'
    )
    res = es.search(
        index = index,
        doc_type = 'doctor',
        timeout = settings.ES_SEARCH_TIMEOUT,
        body = q,
        from_ = offset,
        size = size)

    tmp = res

    res = {
        'doctor_ids':[d['_id'] for d in tmp['hits']['hits']],
        'total_count': tmp['hits']['total'],
        'hits': tmp['hits']['hits'],
    }

    # 特殊逻辑 对于广告 需要返回具体的广告ID
    if 'adver_position' in filters:
        res['adver_id_map'] = {d['_id']: d['fields']['adver_id'][0] for d in tmp['hits']['hits']}
    return res


#todo
#取美购列表中美购关联的医生
#排序按照找医生默认排序输出
@bind('dymas/search/get_doctors_by_special_id')
def get_doctors_by_special_id(special_id,  offset=0, size=10, sort_type=DOCTOR_ORDER_TYPE.DEFAULT):
    nested_fields = { "special_id": special_id}
    es_data = filter_doctor(
        filters={},
        offset=offset,
        size=size,
        sort_type=sort_type,
        sort_params={},
        nested_fields = nested_fields
    )
    hits = es_data['hits']
    hit_ids = [hit["_id"] for hit in hits]
    return {'doctors': hit_ids}
