from datetime import datetime

from django.conf import settings

from gm_types.gaia import SERVICE_ORDER_TYPE, ADVER_MANAGEMENT_POSITION_TYPE, SERVICE_ITEM_PRICE_TYPE
from gm_types.doris import STRATEGY_TYPE

from libs.filters import area_tag_id_filter
from libs.error import logging_exception
from libs.es import tzlc, get_es, es_query, get_highlight
from libs.debug import pretty_json
import logging
import traceback
import time

# TODO: this param is heavily depending on
# elasticsearch setting "index.max_result_window"
MAX_SERVICE_COUNT = 9999

SMARTRANK_SORT = (
    SERVICE_ORDER_TYPE.DEFAULT_SPECIAL,
    SERVICE_ORDER_TYPE.REGION,
    SERVICE_ORDER_TYPE.DEFAULT,
    SERVICE_ORDER_TYPE.DEFAULT_REALTIME,
    SERVICE_ORDER_TYPE.DIARY_RECOMMENDED,
)


class SKUPureFiltersProcesser(object):
    """基于sku mapping 的搜索过滤

    """
    ExtralFilterSupportList = ['tag_ids']

    def __init__(self, filters, sort_type=SERVICE_ORDER_TYPE.DEFAULT, query='', or_filters=[]):
        self.filters = filters
        self.sort_type = sort_type
        self.query = query
        self.or_filters = or_filters
        self.should_filters = []
        self.must_filters = [{'term': {'is_online': True}}]  # 只返回上线的福利]

    def _build_common_must_filters(self):
        # get sku must/should filters to build function score for base common must filters
        base_should = self.analyze_filters(self.filters)

        self.should_filters.append({
            'bool':
                {'must': base_should}
        })

    @staticmethod
    def analyze_filters(filters):
        f = []
        for k, v in filters.items():
            if k == 'ids' and isinstance(v, list):
                f.append({
                    'terms': {'id': v}
                })
            elif k == 'province_tag_id' and isinstance(v, int):
                f.append({
                    'term': {'doctor.hospital.city_province_tag_id': v}
                })
            elif k == 'city_tag_id' and isinstance(v, int):
                f.append({
                    'term': {'doctor.hospital.city_tag_id': v}
                })
            elif k == 'area_tag_id' and isinstance(v, (int, str)):
                f.append(area_tag_id_filter(['doctor.hospital.'], v))
            elif k == 'bodypart_tag_id':
                # compatibility
                if isinstance(v, list) and v:
                    f.append({
                        'terms': {'closure_tag_ids': v}
                    })
                elif isinstance(v, int):
                    f.append({
                        'term': {'closure_tag_ids': v}
                    })
            elif k == 'channel':
                f.append({
                    'term': {'channel': v}
                })
            elif k == 'doctor_id':
                f.append({
                    'term': {'doctor.id': v},
                })
            elif k == 'doctor_ids' and isinstance(v, list):
                f.append({
                    'terms': {'doctor.id': v}
                })
            elif k == 'hospital_id' and isinstance(v, str):
                f.append({
                    'term': {'doctor.hospital.id': v}
                })
            elif k == 'tag_ids' and isinstance(v, list) and v:
                f.append({
                    'terms': {'closure_tag_ids': v},
                })
            elif k == 'special_id':
                # f.append({
                #     'term': {'special_rank.special_id': v},
                # })
                if 'user_city_tag_id' in filters and filters['is_posed']:
                    f.append({
                        'bool': {
                            'must': [
                                {
                                    "nested": {
                                        "path": "special",
                                        "query": {
                                            "bool": {
                                                "must": [
                                                    {"term": {"special.id": v}},
                                                ]
                                            }
                                        }
                                    }
                                }
                            ],
                            'must_not': [
                                {
                                    'bool': {
                                        'must': [
                                            {
                                                "nested": {
                                                    "path": "special",
                                                    "query": {
                                                        "bool": {
                                                            "must": [
                                                                {"term": {"special.id": v}},
                                                                {"term": {"special.has_pos": True}}
                                                            ]
                                                        }
                                                    }
                                                }
                                            },
                                            {"term": {"doctor.hospital.city_tag_id": filters['user_city_tag_id']}}
                                        ]
                                    }
                                }
                            ]
                        }
                    })
                else:
                    f.append({
                        "nested": {
                            "path": "special",
                            "query": {
                                "bool": {
                                    "must": [
                                        {"term": {"special.id": v}},
                                    ]
                                }
                            }
                        }
                    })
            elif k == 'special_ids_and' and isinstance(v, list) and v:
                f.append({
                    'terms': {'special.id': v, 'execution': 'and'},
                })
            elif k == 'share_get_cashback' and isinstance(v, bool):
                f.append({
                    'term': {'share_get_cashback': v}
                })
            elif k == 'rating_gte' and isinstance(v, (float, int)):
                f.append({
                    'range': {'rating': {'gte': v}}
                })
            elif k == 'tip':
                f.append({
                    'term': {'tip': v}
                })
            elif k == 'tip_list_and':
                f.append({
                    'terms': {'tip': v, 'execution': 'and'}
                })
            elif k == 'famous_doctor' and isinstance(v, bool) and v:
                f.append({
                    'term': {'doctor.famous_doctor': True}
                })
            elif k == 'hospital_type':
                f.append({
                    'term': {'doctor.hospital.hospital_type': v}
                })
            elif k == 'is_stage' and isinstance(v, bool):
                f.append({
                    'term': {'is_fenqi': v}
                })
            elif k == 'is_fenqi' and isinstance(v, bool):
                f.append({
                    'term': {'is_fenqi': v}
                })
            elif k == 'is_insurance' and isinstance(v, bool):
                f.append({
                    'term': {'is_insurance': v}
                })
            elif k == 'doctor_title':
                f.append({
                    'terms': {'doctor.title': v}
                })
            elif k == 'hospital_oversea':
                f.append({
                    'term': {'doctor.hospital.is_oversea': v},
                })
            elif k == 'hospital_type_list':
                f.append({
                    'terms': {'doctor.hospital.hospital_type2': v},
                })
            elif k == 'service_coupons':
                f.append({
                    'range': {'gift_rank': {'gte': v}}
                })
            else:
                logging_exception()

        return f

    def _build_or_should_filters(self):
        if not self.or_filters:
            return

        for filters in self.or_filters:
            if (set(filters.keys()) - set(self.ExtralFilterSupportList)):  # if exist not support filter key, return
                return

            should_filters = self.analyze_filters(filters)
            self.should_filters.append({
                'bool':
                    {'must': should_filters}
            })

    def build_should_filters(self):
        self._build_or_should_filters()

    def build_must_filters(self):
        self._build_common_must_filters()

    def build_filters(self):
        self.build_must_filters()
        self.build_should_filters()
        return {
            'bool': {
                'should': self.should_filters,
                'must': self.must_filters
            }
        }

    def __call__(self, *args, **kwargs):
        return self.build_filters()


def process_sorting(sort_type, sort_params, append_score=False):
    now = int(time.time())
    sort_params = sort_params if sort_params is not None else {}
    sorting = [  # 所有福利排序，下沉的与不可售的都需要放后面
        {'_script': {
            'type': 'number',
            'order': 'asc',
            "script": {
                "id": "service-sink",
                "params": {"now": now}
            }

        }},
        {'_script': {
            'type': 'number',
            'order': 'desc',
            "script": {
                "id": "service-time-valid",
                "params": {"now": now}
            }
        }},
    ]

    # 机构罚单下沉
    sorting += [
        {
            '_script': {
                'type': 'number',
                'order': 'asc',
                "script": {
                    "id": "service-sink-by-org",
                    "params": {"now": now}
                }
            }
        }
    ]

    if sort_params.get('query') and sort_type != SERVICE_ORDER_TYPE.DORIS_SMART:
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "service-tag-first",
                    "params": {
                        "query": sort_params['query']
                    }

                },
                'order': 'desc',
            }},
        ]

    if sort_type == SERVICE_ORDER_TYPE.ORDER_HIGHEST_SALES:
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',
            }},
            {'sales_count': {'order': 'desc'}},
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.ORDER_LOWEST_PRICE:  # 价格最低
        now_str = tzlc(datetime.now()).isoformat()
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',

            }},
            {
                "sku_list.price": {
                    "order": "asc",
                    "mode": "min",
                    "nested_path": "sku_list",
                    "nested_filter": {
                        "bool": {
                            "must": [
                                {
                                    "range": {
                                        "sku_list.start_time": {"lte": "now"}
                                    }
                                },
                                {
                                    "range": {
                                        "sku_list.end_time": {"gt": "now"}
                                    }
                                }
                            ]
                        }
                    }
                }
            },
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.ORDER_LOWEST_PRICE_FOR_SKU:  # sku 价格最低
        now_str = tzlc(datetime.now()).isoformat()
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',

            }},
            {
                "price_range.price": {
                    "order": "asc",
                    "mode": "min",
                    "nested_path": "price_range",
                    "nested_filter": {
                        "bool": {
                            "must": [
                                {
                                    "range": {
                                        "price_range.start_time": {"lte": "now"}
                                    }
                                },
                                {
                                    "range": {
                                        "price_range.end_time": {"gt": "now"}
                                    }
                                }
                            ]
                        }
                    }
                }
            },
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.CASE_COUNT:
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',
            }},
            {'case_count': {'order': 'desc'}},
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.DEFAULT_SPECIAL:
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',
            }},
            {'_script': {
                'type': 'number',
                'order': 'asc',
                "script": {
                    "id": "service-default-special",
                    "params": {
                        "special_id": sort_params['special_id'],
                    }
                }
            }},
            {'ordering': {'order': 'asc'}},
            {'smart_rank2': {'order': 'desc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.LOWEST_PRICE_SPECIAL:
        now_str = tzlc(datetime.now()).isoformat()
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',
            }},
            {'_script': {
                'type': 'number',
                'order': 'asc',
                "script": {
                    "id": "service-price-3",
                    "params": {
                        "now": now
                    }
                }
            }},
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.REGION:
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',
            }},
            {'smart_rank2': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.DISTANCE:
        # NOTE: 20161124, service tab order by time in zone detail page
        # use this sort type, since it's the closest alg.
        # sort_params will be empty {}
        # if this sort type alg needs to be updted, pls let me know:
        # pengfei.x
        # 为和客户端兼容 使用lng参数替代之前的lon参数 @kula
        if ('lng' in sort_params and sort_params['lng'] is not None) and (
                'lat' in sort_params and sort_params['lat'] is not None):  # check if param exists
            sorting.append({'_geo_distance': {
                'location': {'lon': sort_params['lng'], 'lat': sort_params['lat']},
                'order': 'asc',
                'unit': 'km',
            }})

        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',

            }},
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.ORDER_EVALUATE:
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',

            }},
            {'rating': {'order': 'desc'}},
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.POPULARITY:
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',

            }},
            {'pv': {'order': 'desc'}},
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.DEFAULT:  # 智能排序
        sorting += [
            {
                '_script': {
                    'type': 'number',
                    'order': 'desc',
                    "script": {
                        "id": "service-region-related",
                        "params": {
                            "user_city_tag_id": sort_params.get('user_city_tag_id', -1),
                            "in_whitelist": int(sort_params.get('in_whitelist', False))
                        }
                    }
                }
            },
            {'smart_rank2': {'order': 'desc'}},
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.DEFAULT_REALTIME:  # 实时智能排序
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',
            }},
            '_score',
            {'smart_rank2': {'order': 'desc'}},
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.DIARY_RECOMMENDED:
        sorting += [
            {'_script': {
                'type': 'number',
                'order': 'desc',
                "script": {
                    "id": "service-region",
                    "params": {
                        "city_tag_id": sort_params['city_tag_id'] if 'city_tag_id' in sort_params else -1,
                        "city_province_tag_id":  sort_params['city_province_tag_id'] if 'city_province_tag_id' in sort_params else -1,

                    }
                }
            }},
            {'smart_rank2': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.ORDERED_USER:
        sorting += [
            {'sales_count': {'order': 'desc'}},
            {'start_time': {'order': 'desc'}},
        ]
    elif sort_type == SERVICE_ORDER_TYPE.ORDER_HIGHEST_PRICE:
        now_str = tzlc(datetime.now()).isoformat()
        sorting += [
            {'_script': {
                'type': 'number',
                "script": {
                    "id": "sku-default",
                    "params": {'user_city_tag_id': sort_params[
                        'user_city_tag_id'] if 'user_city_tag_id' in sort_params else -1},
                },
                'order': 'desc',
            }},
            {
                "sku_list.price": {
                    "order": "desc",
                    "mode": "max",
                    "nested_path": "sku_list",
                    "nested_filter": {
                        "bool": {
                            "must": [
                                {
                                    "range": {
                                        "sku_list.start_time": {"lte": "now"}
                                    }
                                },
                                {
                                    "range": {
                                        "sku_list.end_time": {"gt": "now"}
                                    }
                                }
                            ]
                        }
                    }
                }
            },
            {'ordering': {'order': 'asc'}},
            {'start_time': {'order': 'desc'}},
        ]
    if append_score and sort_type != SERVICE_ORDER_TYPE.DEFAULT_REALTIME:
        sorting.append('_score')

    return sorting


# @desc sku召回
# @rd 郑伟
# @param  query                          搜索词
# @param  offset                         起始行数
# @param  size                           返回行数
# @param  sort_type                      排序类型
# @param  filters                        过滤条件
# @param  sort_params                    排序条件sku
# @date 20170709
def recall_sku_pure(query='',
                    offset=0,
                    size=10,
                    sort_type=SERVICE_ORDER_TYPE.DEFAULT,
                    sort_params={},
                    filters={},
                    or_filters=[],
                    fields=[]):
    size = min(size, settings.COUNT_LIMIT)
    # 限制interest tag时才走实时智能排序
    if sort_type == SERVICE_ORDER_TYPE.DEFAULT_REALTIME and sort_params.get('interest_tags', False):
        return realtime_recall_sku_pure(query, offset, size, sort_type, sort_params, filters)
    # 搜索关键字部分
    # 搜索域
    multi_fields = {
        'short_description': 8,
        'doctor.name': 4,
        'doctor.hospital.name': 3,
        'doctor.hospital.city_name': 2,
        'doctor.hospital.city_province_name': 2,
        'closure_tags': 2,  # 5.9版 搜索所有tag
        'doctor.hospital.officer_name': 3  # 搜索机构管理者
    }
    query_fields = ['^'.join((k, str(v))) for (k, v) in multi_fields.items()]
    multi_match = {
        'query': query,
        'type': 'cross_fields',
        'operator': 'and',
        'fields': query_fields,
    }
    sku_query = {
        "nested": {
            "path": "sku_list",
            "query": {
                "multi_match": {
                    "query": query,
                    "fields": ["sku_list.name^2"],
                    'operator': 'and',
                    'type': 'cross_fields'
                }
            }
        }
    }

    #
    # spu_filter = {'bool': {'must': f}}
    # pretty_json(spu_filter)
    sku_filter = SKUPureFiltersProcesser(filters, sort_type, query, or_filters)()
    q = {
        'query': sku_filter
    }

    if query != '':
        q['query'] = {
            'bool': {
                "should": [
                    {'multi_match': multi_match},
                    sku_query
                ],
                "minimum_should_match": 1
            }
        }
        sort_params['query'] = query

        if len(sku_filter["bool"]["should"]) > 0:
            q['query']['bool']['should'].append(sku_filter["bool"]["should"])

        if len(sku_filter["bool"]["must"]) > 0:
            q["query"]['bool']['must'].append(sku_filter["bool"]["must"])

    if fields:
        q["_source"] = {
            "include": fields
        }

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

    # pretty_json(q)

    res = es_query('sku', q, offset, size)
    return res


# @desc 实时sku召回
# @rd 郑伟
# @param  query                          搜索词
# @param  offset                         起始行数
# @param  size                           返回行数
# @param  sort_type                      排序类型
# @param  filters                        过滤条件
# @param  sort_params                    排序条件
# @date 20170725
def realtime_recall_sku_pure(query='',
                             offset=0,
                             size=10,
                             sort_type=SERVICE_ORDER_TYPE.DEFAULT,
                             sort_params={},
                             filters={},
                             or_filters=[],
                             fields=[]):
    size = min(size, settings.COUNT_LIMIT)
    # 搜索关键字部分
    # 搜索域
    multi_fields = {
        'short_description': 8,
        'doctor.name': 4,
        'doctor.hospital.name': 3,
        'doctor.hospital.city_name': 2,
        'doctor.hospital.city_province_name': 2,
        'closure_tags': 2,  # 5.9版 搜索所有tag
        'doctor.hospital.officer_name': 3  # 搜索机构管理者
    }
    query_fields = ['^'.join((k, str(v))) for (k, v) in multi_fields.items()]
    multi_match = {
        'query': query,
        'type': 'cross_fields',
        'operator': 'and',
        'fields': query_fields,
    }
    sku_query = {
        "nested": {
            "path": "sku_list",
            "query": {
                "multi_match": {
                    "query": query,
                    "fields": ["sku_list.name^2"],
                    'operator': 'and',
                    'type': 'cross_fields'
                }
            }
        }
    }

    #
    # spu_filter = {'bool': {'must': f}}
    sku_filter = SKUPureFiltersProcesser(filters=filters, sort_type=sort_type, query=query, or_filters=or_filters)()
    interest_tags = sort_params.get('interest_tags')

    functions = []
    if sort_type == SERVICE_ORDER_TYPE.DEFAULT_REALTIME and interest_tags:
        functions.append(
            {
                "filter": {
                    "terms": {
                        "closure_tag_ids": interest_tags
                    }
                },
                "weight": 5
            }
        )

    function_score = {
        "boost_mode": "replace",
        "functions": functions,
        "min_score": 0,
        "score_mode": "max"
    }
    if query != '':
        function_score['query'] = {
            'bool': {
                "should": [
                    {'multi_match': multi_match},
                    sku_query
                ],
                "minimum_should_match": 1
            }
        }
        sort_params['query'] = query

    q = {
        "query": {
            "function_score": function_score
        },
        'filter': sku_filter
    }

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

    if fields:
        q["_source"] = {
            "include": fields
        }

    # pretty_json(q)

    res = es_query('sku', q, offset, size)
    return res


def _service_break_up(ret_list, city_same_tag_service_list, city_other_tag_service_list, size, have_read_key_set):
    try:
        same_key_val_dict = dict()
        for same_city_term_tuple in city_same_tag_service_list:
            service_id = same_city_term_tuple[0]
            merchant_id = same_city_term_tuple[1]

            if merchant_id in have_read_key_set:
                if merchant_id in same_key_val_dict:
                    same_key_val_dict[merchant_id].append(service_id)
                else:
                    same_key_val_dict[merchant_id] = [service_id]
            else:
                have_read_key_set.add(merchant_id)
                ret_list.append(service_id)
                if len(ret_list) >= size:
                    break

        if len(ret_list) < size:
            for same_city_term_tuple in city_other_tag_service_list:
                service_id = same_city_term_tuple[0]
                merchant_id = same_city_term_tuple[1]

                if merchant_id in have_read_key_set:
                    if merchant_id in same_key_val_dict:
                        same_key_val_dict[merchant_id].append(service_id)
                    else:
                        same_key_val_dict[merchant_id] = [service_id]
                else:
                    have_read_key_set.add(merchant_id)
                    ret_list.append(service_id)
                    if len(ret_list) >= size:
                        break

        read_over_key_num = 0
        while len(ret_list) < size and read_over_key_num < len(same_key_val_dict):
            for key_item in same_key_val_dict:
                if len(same_key_val_dict[key_item]) > 0:
                    ret_list.append(same_key_val_dict[key_item][0])
                    same_key_val_dict[key_item].pop(0)
                if len(same_key_val_dict[key_item]) == 0:
                    read_over_key_num += 1

        return True
    except:
        logging.error("catch exception,err_log:%s" % traceback.format_exc())
        return False


def bad_service_query_break_up(same_city_same_tag_service_list, same_city_other_tag_service_list,
                               nearby_city_same_tag_service_list,
                               nearby_city_other_tag_service_list, other_city_same_tag_service_list,
                               other_city_other_tag_service_list, size):
    try:
        ret_list = list()
        have_read_key_set = set()
        _service_break_up(ret_list, same_city_same_tag_service_list, same_city_other_tag_service_list, size,
                          have_read_key_set)
        _service_break_up(ret_list, nearby_city_same_tag_service_list, nearby_city_other_tag_service_list, size,
                          have_read_key_set)
        _service_break_up(ret_list, other_city_same_tag_service_list, other_city_other_tag_service_list, size,
                          have_read_key_set)

        return ret_list
    except:
        logging.error("catch exception,err_log:%s" % traceback.format_exc())
        return []


def _get_service_item_info(sku_ids, org_ids, doctor_ids, sku_list, inner_hits, highlight_list, item, service_ids):
    try:
        highlight_item = {}
        if "highlight" in item.keys():
            for key in item['highlight'].keys():
                if key == 'short_description_pre':
                    highlight_item["highlight"] = {"short_description": item["highlight"]["short_description_pre"]}
                highlight_item["id"] = item["_source"]["id"]
            highlight_list.append(highlight_item)

        if 'inner_hits' in item:
            hit = item['inner_hits']
            sku = hit['sku_list']['hits']['hits'][0]
            sku_id = sku['_source']['sku_id']
            sku_ids.append(sku_id)
            service_ids.append(item["_id"])
            org_ids.append(item['_source']['doctor']['hospital']['id'])
            doctor_ids.append(item['_source']['doctor']['id'])

            sku_list.append({
                'city_tag_id': item['_source']['doctor']['hospital']['city_tag_id'],
                'nearby_city_tags': [elt['tag_id'] for elt in item['_source'].get('nearby_city_tags', [])]
            })
            sku_id_name = {}
            for i in sku:
                if i == "highlight":
                    sku_id_name["highlight"] = sku["highlight"]["sku_list.name"]

                if i == "_source":
                    sku_id_name["sku_id"] = sku["_source"]['sku_id']

            inner_hits.append(sku_id_name)

        return True
    except:
        logging.error("catch exception,err_log:%s" % traceback.format_exc())
        return False


def service_query_break_up(sku_ids, org_ids, doctor_ids, sku_list, inner_hits, highlight_list, item,
                           have_read_key_set, size, same_key_val_dict, same_key_val_user_first=False,
                           dispose_same_key_val_dict=dict(), service_ids=list()):
    try:
        read_over_key_num = 0
        if same_key_val_user_first and len(dispose_same_key_val_dict) > 0:
            same_key_len = len(dispose_same_key_val_dict)
            while len(sku_list) < size and read_over_key_num < same_key_len:
                empty_key_num = 0
                for key_item in dispose_same_key_val_dict:
                    if len(dispose_same_key_val_dict[key_item]) > 0:
                        _get_service_item_info(sku_ids, org_ids, doctor_ids, sku_list, inner_hits, highlight_list,
                                               dispose_same_key_val_dict[key_item][0], service_ids)
                        dispose_same_key_val_dict[key_item].pop(0)
                        if len(dispose_same_key_val_dict[key_item]) == 0:
                            read_over_key_num += 1
                            empty_key_num += 1
                    else:
                        empty_key_num += 1
                if empty_key_num >= same_key_len:
                    break

            if len(sku_ids) < size and item is not None:
                merchant_doctor_id = item['_source']['merchant_doctor_id']
                _get_service_item_info(sku_ids, org_ids, doctor_ids, sku_list, inner_hits, highlight_list, item,
                                       service_ids)
                have_read_key_set.add(merchant_doctor_id)
        else:
            if len(sku_list) < size and item is not None:
                merchant_doctor_id = item['_source']['merchant_doctor_id']
                if merchant_doctor_id not in have_read_key_set:
                    _get_service_item_info(sku_ids, org_ids, doctor_ids, sku_list, inner_hits, highlight_list, item,
                                           service_ids)
                    have_read_key_set.add(merchant_doctor_id)
                else:
                    if merchant_doctor_id not in same_key_val_dict:
                        same_key_val_dict[merchant_doctor_id] = [item]
                    else:
                        same_key_val_dict[merchant_doctor_id].append(item)

        return read_over_key_num
    except:
        logging.error("catch exception,err_log:%s" % traceback.format_exc())
        return False
