# coding=utf8

import logging
import traceback
from datetime import datetime

from django.conf import settings
from gm_types.artemis import DETAIL_TYPE
from gm_types.gaia import SECKILL_STATUS
from gm_types.doris import RANK_MODE

from api.models.types import SERVICE_ORDER_TYPE, DOCTOR_TYPE
from api.models.service import Service, ServiceItem
from api.models.hospital import Hospital
from api.models.area import City
from api.models.special import SpecialSeckillService, Special, SpecialRelatedTag, SpecialItem, SpecialRelatedTagV3
from api.tool.user_tool import get_user_city_tagid
from api.tool.user_tool import get_user_from_context
from api.tool.geo_tool import get_location_tag_id_by_city_id
from api.tool.city_tool import get_city_id_by_options

from api.manager import service_info_manager
from hippo.models import Doctor, MerchantRelevance

from rpc.decorators import bind_context, bind
from rpc.decorators import list_interface
from rpc.tool.log_tool import search_logger, logging_exception, info_logger
from search.utils.diary import filter_diary
from search.utils.es import get_es, es_index_adapt

from search.utils.service import (
    search_service, filter_special_promtions, filter_special_promtions_new,
    filter_service)
from search.utils.common import get_query_matched_tags
from rpc.tool.param_check import assert_uint
from gm_types.gaia import ACTIVE_TYPE
from rpc.context import get_rpc_remote_invoker


def service_key(service):
    return service.sell_amount_display


def service_can_sell(service):
    if service.sell_num_limit is not None and service.sell_num_limit <= 0:
        return 0
    elif service.end_time is not None and service.end_time < datetime.now():
        return 0
    else:
        return 1


@bind_context("search/service_sku/esquery/v2")
def search_es_sku_service_v2(ctx, query='', user_city_tag_id=None, offset=0, size=5,
                             sort_type=SERVICE_ORDER_TYPE.DEFAULT, filters=None,
                             sort_params=None, current_city_id=None, extra_filters=None, ):
    """
        v2版本和之前版本的区别在于，v2返回的hits是 [{ 'sku_id':9, 'spu_id':1 }]
        之后会逐步将原有接口从直接返回完整数据变为返回核心数据。
    """
    data = _search_es_sku_service(ctx, current_city_id, extra_filters, filters, offset, query,
                                  size, sort_params, sort_type, user_city_tag_id)

    sku_ids = data['sku_ids']
    result = []
    if sku_ids:
        siid_and_sid = ServiceItem.objects.filter(parent_id=0, id__in=sku_ids).values_list('id', 'service_id')
        for siid, sid in siid_and_sid:
            result.append({
                'source': {
                    'service_item_id': siid,
                    'service_id': sid,
                },
                'highlight': '',
            })
    return {
        'matched_tag_ids': [tag.id for tag in get_query_matched_tags(query)],
        'hits': result,
        'total': data.get('total', 0),
        'rank_mode': data['rank_mode']
    }


@bind_context("search/service_sku/esquery")
def search_es_sku_service(ctx, query='', user_city_tag_id=None, offset=0, size=5,
                          sort_type=SERVICE_ORDER_TYPE.DEFAULT, filters=None,
                          sort_params=None, current_city_id=None, extra_filters=None,
                          get_diary_num=False, switch=False, has_gift=True, use_tagv3=False, is_sort_with_icon=False):
    """
    !!! 之后请使用 search/service_sku/esquery/v2 接口

    :param current_city_id: 保留这个参数  用来维持接口兼容
    :return:

    todo list:
    switch参数不在使用,后续开发迭代中可删除
    """
    data = _search_es_sku_service(ctx, current_city_id, extra_filters, filters, offset, query,
                                  size, sort_params, sort_type, user_city_tag_id, use_tagv3, is_sort_with_icon)

    user = get_user_from_context(ctx)

    sku_ids = data['sku_ids']
    result = []
    if sku_ids:
        siid_to_info = service_info_manager.get_toc_sku_info_list_mapping_by_sku_ids(service_item_ids=sku_ids, has_gift=has_gift)
        service_high_light = data.get('highlight', [])
        sku_high_light = data.get('inner_hits', [])
        for sku_id in sku_ids:
            if str(sku_id) in siid_to_info:
                source = siid_to_info[str(sku_id)]
                result.append({
                    'source': source,
                    'highlight': filter(lambda x: x.get('id', 0) == source.get('id', -1), service_high_light),
                    'sku_high_light': filter(lambda x: x.get('sku_id', 0) == sku_id, sku_high_light)
                })

    return {
        'matched_tag_ids': [tag.id for tag in get_query_matched_tags(query)],
        'hits': result,
        'total': data.get('total', 0),
        'rank_mode': data['rank_mode']
    }


def _search_es_sku_service(ctx, current_city_id, extra_filters, filters, offset, query, size, sort_params, sort_type,
                           user_city_tag_id, use_tagv3=False, is_sort_with_icon=False):
    filters = filters or {}
    sort_params = sort_params or {}
    user = ctx.session.user
    if not user_city_tag_id:
        user_city_tag_id = get_user_city_tagid(user.id)

    country_tagid, city_tagid = get_location_tag_id_by_city_id(current_city_id)
    user_city_tag_id = 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 sort_type == SERVICE_ORDER_TYPE.DEFAULT:
    #    sort_type = SERVICE_ORDER_TYPE.DEFAULT_REALTIME
    # added 7.4.0  解析extra_filters    
    if extra_filters:
        from api.view.service import api_filter_service_extract_params
        params = api_filter_service_extract_params(
            user=user, filters=filters, extra_filters=extra_filters,
            sort_params=sort_params, current_city_id=current_city_id)
        sort_params = params['sort_params']
        filters = params['filters']

    # add promotion related tag
    or_filters = []
    if filters.get('special_id', None):
        if use_tagv3:
            tag_ids = list(Special.objects.get(id=filters['special_id']).new_tags.values_list('id', flat=True))
            if tag_ids:
                or_filters.append({'tagv3_ids': tag_ids})
        else:
            tag_ids = list(Special.objects.get(id=filters['special_id']).tags.values_list('id', flat=True))
            if tag_ids:
                or_filters.append({'tag_ids': tag_ids})

    data = {'sku_ids': [], 'rank_mode': RANK_MODE.DEFAULT, 'total': 0}
    try:
        data = filter_special_promtions(
            query=query,
            user_city_tag_id=user_city_tag_id,
            offset=offset,
            size=size,
            sort_type=sort_type,
            filters=filters,
            sort_params=sort_params,
            or_filters=or_filters,
            use_tagv3=use_tagv3,
            is_sort_with_icon=is_sort_with_icon,
        )

        ctx.logger.app(search_esquery=dict(type='sku', query=query, offset=offset, recall_count=len(data['sku_ids'])))
    except:
        logging_exception()
    return data

#查询专场美购列表
@bind_context("search/special/service_query")
def search_special_service_query(
        ctx, special_id, user_city_tag_id=None, offset=0, size=5,
        sort_type=SERVICE_ORDER_TYPE.DEFAULT, filters={},
        sort_params=None, current_city_id=None,
        extra_filters=None,
        get_diary_num=False,
        floor_id=None,
        mix_floor=False,
        use_tagv3=False,
):
    """
    使用新的接口,类似 `search/service_sku/esquery`
    filter_special_promtions 替换为 filter_special_promtions_new
    直接从doris 获取已打散后的数据  -- by zhongchengyang, 20180412

    :param current_city_id: 保留这个参数  用来维持接口兼容
    :return:
    """

    #search_logger.info("gaia专场美购数据查询----")

    # 是否考虑没有special 就直接return
    special = Special.objects.filter(id=special_id, is_online=True).only('groupbuy_id').first()

    #search_logger.info("拿到的美购---")
    #search_logger.info(special)

    #是否是团购专场
    is_groupbuy_special = False
    sort_params = sort_params or {}

    #用户城市tag
    _city_id = get_city_id_by_options(lat=sort_params.get('lat', 0), lng=sort_params.get('lng', 0))
    user = ctx.session.user
    if not user_city_tag_id:
        user_city_tag_id = get_user_city_tagid(user.id)

    # 如存在经纬度, 则根据经纬度获取城市信息
    if not current_city_id and _city_id:
        current_city_id = _city_id

    #城市id和城市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:
        city = City.objects.get(tag_id=user_city_tag_id)
        user_city_info = {'user_city_id': city.id, 'user_city_tag_id': user_city_tag_id}
    else:
        user_city_info = None

    # added 7.4.0  解析extra_filters
    if extra_filters:
        from api.view.service import api_filter_service_extract_params
        params = api_filter_service_extract_params(
            user=user, filters=filters, extra_filters=extra_filters,
            sort_params=sort_params, current_city_id=current_city_id)
        sort_params = params['sort_params']
        filters = params['filters']

    # add promotion related tag
    or_filters = []

    # 楼层里面的美购列表
    if use_tagv3:
        try:
            if mix_floor:
                special_related_tags = set(SpecialRelatedTagV3.objects.filter(
                    special_id=special.id).values_list('tagv3_id', flat=True))
                special_tags = set(Special.objects.get(id=special_id).new_tags.values_list('id', flat=True))
                tag_ids = list(special_related_tags | special_tags)

            else:
                if special and special.is_new_special and floor_id:
                    tag_ids = list(SpecialRelatedTagV3.objects.filter(
                        special_id=special.id, floor_id=floor_id
                    ).values_list('tag_id', flat=True))
                else:
                    tag_ids = list(Special.objects.get(id=special_id).new_tags.values_list('id', flat=True))

            if tag_ids:
                or_filters.append({'tagv3_ids': tag_ids})
        except Exception as e:
            search_logger.info(e)
    else:
        try:
            if mix_floor:
                special_related_tags = set(SpecialRelatedTag.objects.filter(
                        special_id=special.id).values_list('tag_id', flat=True))
                special_tags = set(Special.objects.get(id=special_id).tags.values_list('id', flat=True))
                tag_ids = list(special_related_tags | special_tags)

            else:
                if special and special.is_new_special and floor_id:
                    tag_ids = list(SpecialRelatedTag.objects.filter(
                        special_id=special.id, floor_id=floor_id
                    ).values_list('tag_id', flat=True))
                else:
                    tag_ids = list(Special.objects.get(id=special_id).tags.values_list('id', flat=True))

            if tag_ids:
                or_filters.append({'tag_ids': tag_ids})
        except Exception as e:
            search_logger.info(e)

    result = []
    rank_mode = RANK_MODE.DEFAULT
    try:
        # 特殊专场id
        specific_special_id = [7149, 7225, 7208, 7224]
        specific_callback = False
        if special_id in specific_special_id:
            specific_callback = True
            sort_type = SERVICE_ORDER_TYPE.DEFAULT_SPECIAL

        #从doris提取专场的美购数据
        filter_result = ctx.rpc_remote['doris/search/special'](
            special_id=special_id,
            user_city_info=user_city_info,
            filters=filters,
            sort_type=sort_type,
            sort_params=sort_params,
            or_filters=or_filters,
            offset=offset,
            size=size,
            floor_id=floor_id,
            mix_floor=mix_floor,
            use_tagv3=use_tagv3,
            is_special_rank=specific_callback,
        ).unwrap()

        rank_mode = filter_result['rank_mode']

        #获取sku_ids
        skus_info = filter_result['skus']
        sku_ids = [v['sku_id'] for v in skus_info]

        search_logger.info("from doris---")
        info_logger.info({
            'url': 'doris/search/special',
            'special_id': special_id,
            'user_city_info': user_city_info,
            'filters': filters,
            'sort_type': sort_type,
            'sort_params': sort_params,
            'or_filters': or_filters,
            'offset': offset,
            'size': size,
            'floor_id': floor_id,
            'mix_floor': mix_floor,
            'use_tagv3': use_tagv3,
            'is_special_rank': specific_callback,
            'res': filter_result,
        })

        # 暂时在这提供拼团专场 美购列表的获取方法的改变  等到策略组将 通过special_id 获取对应拼团sku_id 做好久可以删除掉
        # if special and special.groupbuy_id is not None:
        #     is_groupbuy_special = True
        #     siid_to_sid_tuple = ServiceItem.objects.filter(
        #         parent_id=0, id__in=sku_ids, is_delete=False
        #     ).values_list('id', 'service_id')
        #     siid_to_sid = {}
        #     for siid, sid in siid_to_sid_tuple:
        #         siid_to_sid[siid] = sid
        #     sid_to_info = service_info_manager.get_toc_spu_info_list_mapping_by_spu_ids(siid_to_sid.values())
        #
        #     for sku_info in skus_info:
        #         sku_id = sku_info['sku_id']
        #         if sku_id in siid_to_sid:
        #             sid = siid_to_sid[sku_id]
        #             spu_base_info = sid_to_info[str(sid)]
        #             source = spu_base_info
        #             source['__src_type'] = sku_info['__src_type']
        #             result.append({
        #                 'source': source,
        #                 'highlight': '',
        #             })
        if special and floor_id:
            # 获取当前楼层的sku
            sku_index_to_dict = {}
            skus_info_extra = {sku_dict['sku_id']: sku_dict['__src_type'] for sku_dict in skus_info}

            serviceitem_ids = SpecialItem.objects.filter(
                special_id=special_id, floor_id=floor_id).values_list('serviceitem_id', flat=True)

            union_sku_ids = set(serviceitem_ids) & set(sku_ids)
            left_sku_ids = list(set(sku_ids) - set(serviceitem_ids))

            for sku_id in union_sku_ids:
                sku_index_to_dict[sku_id] = SpecialItem.objects.get(serviceitem_id=sku_id, floor_id=floor_id).position

            sku_index_to_list = sorted(sku_index_to_dict.items(), key=lambda x: x[1])

            # 保持原有顺序
            left_sku_ids.sort(key=sku_ids.index)
            # 此处操作无效 待产品确认后再做调整
            for sku_id, index in sku_index_to_list:
                if sku_id in sku_ids:
                    left_sku_ids.insert(sku_ids.index(sku_id), sku_id)
                elif index > 0:
                    left_sku_ids.insert(index - 1, sku_id)
                else:
                    left_sku_ids.append(sku_id)

            # sku_ids = left_sku_ids
            siid_to_info = service_info_manager.get_toc_sku_info_list_mapping_by_sku_ids(sku_ids)

            for sku_id in sku_ids:
                src_type = skus_info_extra[sku_id]
                if str(sku_id) in siid_to_info:
                    sku_base_info = siid_to_info[str(sku_id)]
                    source = sku_base_info
                    source['__src_type'] = src_type
                    groupbuy_type = special.groupbuy and special.groupbuy.active_type or None
                    source["groupbuy_type"] = groupbuy_type

                    result.append({
                        'source': source,
                        'highlight': '',
                    })
            search_logger.info("专场有楼层---")
        else:
            siid_to_info = service_info_manager.get_toc_sku_info_list_mapping_by_sku_ids(sku_ids)

            for sku_info in skus_info:
                sku_id = sku_info['sku_id']
                if str(sku_id) in siid_to_info:
                    sku_base_info = siid_to_info[str(sku_id)]
                    source = sku_base_info
                    source['__src_type'] = sku_info['__src_type']
                    groupbuy_type = special and special.groupbuy and special.groupbuy.active_type or None
                    source["groupbuy_type"]=groupbuy_type
                    result.append({
                        'source': source,
                        'highlight': '',
                    })
            search_logger.info("专场没楼层---")
            search_logger.info(result)
        ctx.logger.app(
            search_esquery=dict(type='sku', special_id=special_id, offset=offset,
                                recall_count=len(sku_ids)))
    except Exception as e:
        search_logger.info(e)
        logging_exception()

    return {
        'hits': result,
        'total': 0,
        'rank_mode': rank_mode,
        'is_groupbuy_special': is_groupbuy_special
    }


@bind_context("search/service/esquery")
@list_interface(offset_name='offset', limit_name='size', element_model=Service)
def search_es_service(ctx, query='', user_city_tag_id=None, offset=0, size=5,
                      sort_type=SERVICE_ORDER_TYPE.DEFAULT,
                      filters={}, sort_params=None, current_city_id=None, extra_filters=None, has_gift=True):
    """
    :param query:
    :param user_city_tag_id:
    :param offset:
    :param size:
    :param sort_type:
    :param filters:
    :return
        total  匹配结果数量
        id
        name
        image_header
        gengmei_price
        original_price
        detail_description
        doctor_name         //医生名称
        hospital_name       //医院名称(医生)
        organization_name   //机构名称
        tags 福利关联的tag列表
    """
    sort_params = sort_params or {}

    user = ctx.session.user
    if not user_city_tag_id:
        user_city_tag_id = get_user_city_tagid(user.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

    # added 7.4.0  解析extra_filters
    if extra_filters:
        from api.view.service import api_filter_service_extract_params
        params = api_filter_service_extract_params(
            user=user, filters=filters, extra_filters=extra_filters,
            sort_params=sort_params, current_city_id=current_city_id)
        sort_params = params['sort_params']
        filters = params['filters']

    results = []
    es_data = search_service(
        query=query,
        offset=offset,
        size=size,
        sort_type=sort_type,
        filters=filters,
        sort_params=sort_params,
    )
    total = es_data['hits']['total']
    hits = es_data["hits"]["hits"]
    rank_mode = es_data['rank_mode']

    service_ids = [hit['_id'] for hit in hits]


    sid_to_service_info = service_info_manager.get_toc_spu_info_list_mapping_by_spu_ids(
        service_ids=service_ids, has_gift=has_gift
    )

    #search_logger.log("能查到的信息---")
    #search_logger.log(sid_to_service_info)

    for hit in hits:
        spu_id = str(hit['_id'])

        if spu_id not in sid_to_service_info:
            search_logger.info(u"Service {} not found".format(id))
            continue

        highlight = hit.get('highlight', None)
        source = sid_to_service_info[spu_id]
        source['id'] = source['service_id']
        source['tags'] = hit['_source'].get('item_wiki_tags', '')
        results.append({
            'source': source,
            'highlight': highlight,
        })
    ctx.logger.app(search_esquery=dict(type='service', query=query, offset=offset, recall_count=len(hits)))

    return {
        'matched_tag_ids': [tag.id for tag in get_query_matched_tags(query)],
        'hits': results,
        'total': total,
        "rank_mode": rank_mode
    }


@bind("filter/esquery/service")
@list_interface(offset_name='offset', limit_name='size', element_model=Service)
def filter_es_service(filters=None, sort_params=None, sort_type=SERVICE_ORDER_TYPE.DEFAULT, offset=0, size=10):
    """ 新增接口  只返回美购ID  部分排序规则不完善 例如根据用户位置排序只能排序等
    """
    if filters is None:
        filters = {}
    if sort_params is None:
        sort_params = {}
    offset = assert_uint(offset, 0)
    size = assert_uint(size, 0)
    sort_type = assert_uint(sort_type, SERVICE_ORDER_TYPE.DEFAULT)
    #service_filter_result = filter_service(
    #    offset=offset,
    #    size=size,
    #    sort_type=sort_type,
    #    filters=filters,
    #    sort_params=sort_params
    #)
    if filters.get("doctor_id"):
        is_doctor = True
        id = filters.get("doctor_id")
        tags = filters.get("tag_ids", [])
    else:
        is_doctor = False
        id = filters.get("hospital_id")
        tags = filters.get("tag_ids", [])
    service_filter_result = get_rpc_remote_invoker()['doris/recommend/doctor_service'](
            is_doctor=is_doctor,
            id=id,
            device_id="",
            offset=offset,
            size=size,
            tags=tags,
            query="",
            sort_type=sort_type
        ).unwrap()
    logging.info("get llllllllllllll:%s" % service_filter_result.get("service_ids"))
    return {
        'service_ids': service_filter_result.get('service_ids'),
        'total_count': service_filter_result.get('total_count'),
        'rank_mode': service_filter_result.get('rank_mode'),
        'doctor_ids': service_filter_result.get('doctor_ids'),
    }


@bind("search/service/multi_query")
def search_multi_query(query='', size=100, doctor_id='', detail_type=''):
    #获取对应商户下的所有医生
    try:
        merchant = MerchantRelevance.objects.get(doctor_id=doctor_id).merchant
        doctor_id = list(MerchantRelevance.objects.filter(merchant_id=merchant.id).values_list('doctor_id', flat=True))
    except:
        logging_exception()
        doctor_id = [doctor_id]
    source_list = []
    try:
        multi_fields = {
            'short_description': 3,
            'name': 8,
            'closure_tags': 8
        }
        fields = ['^'.join((k, str(v))) for (k, v) in multi_fields.iteritems()]

        multi_match = {
            'query': query,
            'type': 'cross_fields',
            'operator': 'or',
            'fields': fields,
            'analyzer': 'gm_default_index'
        }

        f = [{'term': {'is_online': True}}, {'terms': {'doctor.id': doctor_id}}]

        q = {'query': {'bool': {
                'should': {'multi_match': multi_match},
                'minimum_should_match': 1,
                'must': f}},
            '_source': {"include": ["id", "name", "doctor"]}
        }

        search_logger.info("service/multi_query_doctor_query:{}_query:{}".format(doctor_id, q))

        es = get_es()
        index = es_index_adapt(
            index_prefix=settings.ES_INDEX_PREFIX,
            doc_type='service',
            rw='read'
        )
        res = es.search(
            index=index,
            doc_type='service',
            timeout=settings.ES_SEARCH_TIMEOUT,
            body=q,
            from_=0,
            size=size)
        if res and "hits" in res and len(res["hits"]["hits"]) > 0:
            if detail_type == DETAIL_TYPE.HOME_SEARCH_SERVICE_LIST_RANK:
                source_list = [
                    {"id": i['_source']['id'], "name": i['_source']['name']} for i in res['hits']['hits']]
            elif detail_type == DETAIL_TYPE.HOME_SEARCH_DOCTOR_LIST_RANK:
                record = set()
                for item in res['hits']['hits']:
                    if item['_source']['doctor']['id'] not in record:
                        source_list.append({"id": item['_source']['doctor']['id'], "name": item['_source']['doctor']['name']})
                        record.add(item['_source']['doctor']['id'])
            elif detail_type == DETAIL_TYPE.HOME_SEARCH_HOSPITAL:
                doctor_id = res['hits']['hits'][0]['_source']['doctor']['id']
                doctor = Doctor.objects.get(id=doctor_id)
                doctor_officer = doctor.get_officer()
                if doctor_officer:
                    source_list = [{'id': doctor_officer.id, 'name': doctor_officer.name}]
        search_logger.info("service/multi_query_doctor_empty_res_doctor:{}_res:{}".format(doctor_id, res))
        return {"source_list": source_list}
    except:
        logging_exception()
        return {"source_list": source_list}


@bind_context("search/service_sku/esquery/v3")
def search_es_sku_service_v3(ctx, query='', user_city_tag_id=None, offset=0,  size=5,
                             sort_type=SERVICE_ORDER_TYPE.DEFAULT, filters=None,
                             sort_params=None, current_city_id=None, extra_filters=None,
                             use_tagv3=False, is_sort_with_icon=False):
    """
    :param ctx:
    :param query:
    :param user_city_tag_id:
    :param offset:
    :param size:
    :param sort_type:
    :param filters:
    :param sort_params:
    :param current_city_id:
    :param extra_filters:
    :param use_tagv3:
    :param is_sort_with_icon:
    :return:
    """
    data = _search_es_sku_service(ctx, current_city_id, extra_filters, filters, offset, query,
                                  size, sort_params, sort_type, user_city_tag_id, use_tagv3, is_sort_with_icon)
    return {'sku_ids': data['sku_ids'], 'total': data.get('total', 0), 'rank_mode': data['rank_mode'],
            "service_ids": data["service_ids"]}
