# coding=utf-8
import random
import logging
from django.conf import settings
import logging
from .es import get_es, get_highlight, es_index_adapt, get_highlight_query

from .common import area_tag_id_filter, variousness
from api.models.types import DOCTOR_ORDER_TYPE, DOCTOR_TYPE
from gm_types.gaia import ADVER_MANAGEMENT_POSITION_TYPE
from gm_types.doris import INTERPOSE_TYPE
from gm_types.doris import STRATEGY_TYPE
from search.interpose.doctor_interpose import search_interpose
from search.interpose.doctor_interpose import filter_interpose
from search.utils.doris import get_params
from rpc.tool.log_tool import search_logger


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

    for k, v in filters.iteritems():
        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 == 'doctor_ids' and isinstance(v, list):
            # 20180410
            f.append({
                'terms': {'id': v}
            })
        elif k == 'city_tag_ids' and isinstance(v, list):
            f.append({'terms': {'hospital.city_tag_id': 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
        elif k == 'advertise_tag_ids' and isinstance(v, list) and v:
            f.append({
                'terms': {'advertise_tag_ids': v},
            })

    return f, nf


def process_sorting(sort_type, sort_params, append_score=False, search_do=False, search_query=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 += [
            # "_score",
            {'diary_count_over_thres': {'order': 'desc'}},
            {'last_update_time': {'order': 'desc'}},
        ]
    elif sort_type == DOCTOR_ORDER_TYPE.ONLINE_CONSULTATION:  # 在线咨询
        sorting += [
            # "_score",
            {'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
            # "_score",
            {'_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',
            }}
        ]
    elif sort_type == DOCTOR_ORDER_TYPE.DEFAULT:
        doris_params = get_params(STRATEGY_TYPE.DOCTOR_ORDER)

        sorting += [
            # "_score",
            {'_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',
            }}
        ]

        if search_query == True:
            sorting += [
                {
                    "_script": {
                        "order": "desc",
                        "script": {
                            "inline": "_score*factor+doc['rank_score'].value*factors",
                            "params": {
                                "factor": 0.1,
                                "factors": 0.9
                            }
                        },
                        "type": "number"
                    }
                }
            ]
            sorting += [
                {"rank_score": {"order": "desc"}}
            ]
        else:
            sorting += [
                {"rank_score": {"order": "desc"}}
            ]
        sorting += [
            {'_script': {
                'lang': settings.ES_SCRIPT_LANG,
                'script_file': 'sort_doctor-area',
                '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',
            }},
            {'_script': {
                'lang': settings.ES_SCRIPT_LANG,
                'script_file': 'sort_doctor-doris-smart',
                'type': 'number',
                'params': {
                    'weight_a': doris_params['a'] if doris_params.get('a') is not None else 0.25,
                    'weight_b': doris_params['b'] if doris_params.get('b') is not None else 0.5,
                    'weight_c': doris_params['c'] if doris_params.get('c') is not None else 0.30,
                    'weight_d': doris_params['d'] if doris_params.get('d') is not None else 0.15,
                    'weight_e': doris_params['e'] if doris_params.get('e') is not None else 0.15,
                    'weight_f': doris_params['f'] if doris_params.get('f') is not None else 0.10,
                },
                'order': 'desc',
            }}
        ]
        # else:
        #     sorting += [
        #         # "_score",
        #         {'_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',
        #         }},
        #         {"rank_score": {"order": "desc"}},
        #         {'_script': {
        #             'lang': settings.ES_SCRIPT_LANG,
        #             'script_file': 'sort_doctor-area',
        #             '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',
        #         }},
        #         {'_script': {
        #             'lang': settings.ES_SCRIPT_LANG,
        #             'script_file': 'sort_doctor-doris-smart',
        #             'type': 'number',
        #             'params': {
        #                 'weight_a': doris_params['a'] if doris_params.get('a') is not None else 0.25,
        #                 'weight_b': doris_params['b'] if doris_params.get('b') is not None else 0.5,
        #                 'weight_c': doris_params['c'] if doris_params.get('c') is not None else 0.30,
        #                 'weight_d': doris_params['d'] if doris_params.get('d') is not None else 0.15,
        #                 'weight_e': doris_params['e'] if doris_params.get('e') is not None else 0.15,
        #                 'weight_f': doris_params['f'] if doris_params.get('f') is not None else 0.10,
        #             },
        #             'order': 'desc',
        #         }},
        #     ]

    elif sort_type == DOCTOR_ORDER_TYPE.DEFAULT2:
        sorting += [
            # "_score",
            {'_script': {
                'lang': settings.ES_SCRIPT_LANG,
                'script_file': 'sort_doctor-selling-service',
                'type': 'number',
                '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'}},
        ]

    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,
                   is_hospital_search=False):
    """
    @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的文档将被过滤掉
    # 最高可能是机构,分数太高
    filtered = {}
    function_weight_list = []
    search_query = False
    q = {}
    function_query = {}
    nested = []
    multi_fields = {
        'name': 8,
        'hospital.name': 3,
        'hospital.officer_name': 3,
        'hospital.city_name': 2.5,
        'hospital.city_province_name': 2.5,
        'service_closure_tags': 1
    }
    if query:

        if 'doctor_type' in filters and filters['doctor_type'] == DOCTOR_TYPE.OFFICER:
            multi_fields.update({
                'hospital.doctor_names': 2,
            })

        fields = ['^'.join((k, str(v))) for (k, v) in multi_fields.iteritems()]
        multi_match = {
            'query': query,
            'type': 'best_fields',
            'operator': 'and',
            'fields': fields,
            'boost': 5,
            "analyzer": "gm_default_index"
        }

        if 'doctor_type' in filters and filters['doctor_type'] == DOCTOR_TYPE.DOCTOR:
            search_query = True
            nested = [
                {'nested': {
                    'path': "services",
                    'score_mode': 'sum',
                    'query': {
                        'multi_match': {
                            "query": query,
                            "fields": "services.name^2.5",
                            "operator": "and",
                            "type": "best_fields"
                        }
                    },
                    'boost': 2.5,
                }},
                {'nested': {
                    'path': "services",
                    'score_mode': 'sum',
                    'query': {
                        'multi_match': {
                            "query": query,
                            "fields": "services.name",
                            "operator": "and",
                            "type": "best_fields",
                            "analyzer": "keyword"
                        }
                    }

                }}
            ]

        function_query = {
            'bool': {
                'should': [
                    {'multi_match': multi_match},
                    {'multi_match': {
                        "query": query,
                        "fields": [
                            "hospital.name",
                            "service_closure_tags",
                            "hospital.officer_name"
                        ],
                        "operator": "and",
                        "type": "best_fields",
                        "analyzer": "keyword"
                    }},

                ],
                "minimum_should_match": 1
            }
        }

        if len(nested):
            function_query["bool"]["should"].extend(nested)

        f, nf = process_filters(filters=filters)

        function_query["bool"]["must"] = f
        function_query["bool"]["must_not"] = nf

        function_weight_list = [
            {
                "weight": 1000,
                "filter": {
                    "bool": {
                        "must": [
                            {
                                "multi_match": {
                                    "query": query,
                                    "fields": [
                                        "hospital.name",
                                        "service_closure_tags",
                                        "hospital.officer_name"

                                    ],
                                    "analyzer": "keyword",
                                    "operator": "and",
                                    "type": "best_fields"
                                }
                            }
                        ]
                    }
                }
            },
            {
                "filter": {
                    "bool": {
                        "must": [
                            {
                                "nested": {
                                    "path": "services",
                                    "score_mode": "sum",
                                    "query": {
                                        "multi_match": {
                                            "operator": "and",
                                            "query": query,
                                            "type": "best_fields",
                                            "analyzer": "keyword",
                                            "fields": "services.name"
                                        }
                                    }
                                }
                            }
                        ]
                    }
                },
                "weight": 1000
            }
        ]
        q = {
            'query': {
                'function_score': {
                    "functions": function_weight_list,
                    "query": function_query,
                    "boost_mode": "replace",
                    "score_mode": "sum"
                }
            }
        }

    else:
        search_query = False
        f, nf = process_filters(filters=filters)
        filtered['filter'] = {'bool': {'must': f, 'must_not': nf}}
        q = {
            'query': {'filtered': filtered}
        }

    q['highlight'] = get_highlight_query(["hospital.name", "services.short_description", "name"], query)

    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:
        max_score = res['hits']['hits'][1]['_score']

    # 医院搜索去掉质量分后的效果
    if not is_hospital_search:
        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, search_do=True,
                                search_query=search_query)

    # 高亮部分
    q['highlight'] = get_highlight_query(["hospital.name", "services.short_description", "name"], query)

    q['track_scores'] = True

    res = es.search(
        index=index,
        doc_type='doctor',
        timeout=settings.ES_SEARCH_TIMEOUT,
        body=q,
        from_=offset,
        size=size)

    search_logger.info("get search_doctor_query:%s" % q)

    return res


def _filter_doctor(offset=0, size=5, sort_type=DOCTOR_ORDER_TYPE.DEFAULT2, filters=None, sort_params=None):
    filters = filters or {}
    sort_params = sort_params or {}
    # 参数验证
    size = min(size, settings.COUNT_LIMIT)
    f, nf = process_filters(filters=filters)

    query = ''
    for k, v in filters.iteritems():
        if k == "name_raw" or k == "adver_word":
            query = v

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

    # 排序规则部分
    q['sort'] = process_sorting(sort_type=sort_type, sort_params=sort_params)
    q['highlight'] = get_highlight_query(["hospital.name", "services.short_description", "name"], query)

    # 特殊逻辑 对于广告 需要返回具体的广告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,
                    }
                }
            }
        }

    if 'doctor_type' in filters:
        if filters['doctor_type'] == DOCTOR_TYPE.DOCTOR:
            res = filter_interpose(offset=offset, size=size, filters=filters, sort_type=sort_type,
                                   interpose_type=INTERPOSE_TYPE.DOCTOR_FILTER)
        else:
            res = filter_interpose(offset=offset, size=size, filters=filters, sort_type=sort_type,
                                   interpose_type=INTERPOSE_TYPE.OFFICER_FILTER)
        if res:
            return res

    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)
    search_logger.info("get search_doctor_query:%s" % q)
    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']}
    search_logger.info("get res:%s" % res)

    return res


def search_doctor(offset=0, size=5, **kwargs):
    if offset < 300:
        data = _search_doctor(offset=0, size=300, **kwargs)
        hits = data["hits"]
        hits["hits"] = scatter(hits["hits"], offset, size)
        return data
    else:
        return _search_doctor(offset=offset, size=size, **kwargs)


def filter_doctor(offset=0, size=5, **kwargs):
    if offset < 300:
        data = _filter_doctor(offset=0, size=300, **kwargs)
        data["hits"] = scatter(data["hits"], offset, size)
        return data
    else:
        return _filter_doctor(offset=offset, size=size, **kwargs)


# 该方法只适用于（非广告）医生机构的打散
def scatter(data, offset, limit):
    """

    :param data:
    :param offset:
    :param limit:
    :return:
    """
    # 广告不允许打散
    for item in data:
        item['id'] = item['_id']
        item['group'] = item.get('_source', {}).get('merchant_id', str(random.randint(0, 10000)))
    """    
    单独处理增加权重的部分数据
    """
    haveweight_data = []
    noweight_data = []
    haveweight_scatter_data = []
    noweight_scatter_data = []
    all_data = []
    for item in data:
        if item["_score"] >= 1000:
            haveweight_data.append(item)
        else:
            noweight_data.append(item)

    search_logger.info("get haveweight_data:%s" % haveweight_data)
    search_logger.info("get noweight_data:%s" % noweight_data)

    if len(haveweight_data) > 0:
        haveweight_scatter_data = variousness(haveweight_data, len(haveweight_data))
    if len(noweight_data) > 0:
        noweight_scatter_data = variousness(noweight_data, len(noweight_data))

    all_data.extend(haveweight_scatter_data)
    all_data.extend(noweight_scatter_data)
    search_logger.info("get all_data:%s" % all_data)

    return all_data[offset: offset + limit]


def highlight_short_description(query='', hospital_id=""):
    q = {"query": {
        "bool": {
            "must": [{
                "nested": {
                    "path": "services",
                    "query": {
                        "bool": {
                            "must": {
                                "match": {
                                    "": query
                                }
                            }
                        }
                    }
                }
            }, {
                "term": {
                    "hospital.id": hospital_id
                }
            }]
        }
    }
    }
    q['highlight'] = get_highlight_query(["hospital.name", "services.short_description", "name"], query)
