# coding=utf8

import logging
import traceback
from datetime import datetime
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
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 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.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


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):
    """
    !!! 之后请使用 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)

    user = get_user_from_context(ctx)
    from api.view.coupon import calculate_preferential_price

    sku_ids = data['sku_ids']
    result = []
    if sku_ids:
        siid_to_info = service_info_manager.get_toc_sku_info_list_mapping_by_sku_ids(sku_ids)
        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):
    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):
        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,
        )

        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,
):
    """
    使用新的接口,类似 `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 = []

    # 楼层里面的美购列表
    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:

        #从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,
        ).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,
            '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):
    """
    :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,
    )

    #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
    )
    logging.info("get llllllllllllll:%s" % service_filter_result["service_ids"])
    return {
        'service_ids': service_filter_result['service_ids'],
        'total_count': service_filter_result.get('total_count'),
        'rank_mode': service_filter_result['rank_mode'],
        'doctor_ids': service_filter_result.get('doctor_ids'),
    }
