# coding=utf-8
from __future__ import unicode_literals, absolute_import, print_function

import re
import json
import copy
import datetime
from collections import defaultdict
from django.db.models import Q
from django.utils import timezone
from gm_types.gaia import RANK_LIST_TYPE, SPECIAL_TYPE, SLIDE_USER_TYPE, SLIDE_PAYMENT_TYPE, POLYMER_TYPE, POLYMER_TYPE
from gm_types.gaia import SPECIAL_MODULE, SPECIAL_EXTRA_POSITION, DOCTOR_TYPE, SECKILL_POLYMER_MODULES, BUTTON_SELECTION

from api.manager import service_info_manager
from api.models.ranklist import RankList, RankListService
from api.tool.log_tool import logging_exception
from api.tool.geo_tool import get_city_by_options
from api.tool.datetime_tool import get_datetime, get_timestamp, get_timestamp_epoch
from api.tool.service_extra_filters import specialexterior_aggregate_by_rank
from api.models import (
    Service,
    ServiceItem,
    Special,
    SpecialRelatedTag,
    SpecialGadget,
    SpecialResource,
    SpecialStorey,
    SpecialItem,
    SpecialLayout,
    SeckillNotify,
    SpecialExterior,
    City,
    SERVICE_ORDER_TYPE,
    WORLD_AREA_TYPE,
    Doctor,
    GroupBuySpecial,
    SeckillPolymer,
    SeckillPolymerLayout,
    SpecialRegion,
    SeckillPolymerButton,
)
from api.tool.user_tool import get_user_from_context, get_user_and_payment_info
from api.tool.image_utils import get_full_path
from api.tool.geo_tool import get_location_tag_id_by_city_id
from api.tool.user_tool import get_user_city_tagid
from search.utils.service import filter_service
from api.view.service import api_filter_service_extract_params

import rpc.db_opt
from rpc.all import get_rpc_remote_invoker
from rpc.tool.protocol import PushUrlProtocol
from rpc.tool.error_code import gen, CODES
from rpc.tool.es_helpers import get_objects_from_queryset_and_pk_list
from rpc.decorators import list_interface
from rpc.decorators import bind, cache_page, bind_context
from talos.models.diary import Diary

def get_service_is_recommend(specialitems, service):
    # 从service对应的specialitem的is_recommend
    for specialitem in specialitems:
        if specialitem.service_id == service.id:
            return specialitem.is_recommend
    return False


def build_service_info(services):
    """ build service info for seckill polymer page """
    result = []
    for service in services:
        if service['doctor_name']:
            if service['doctor_type'] == DOCTOR_TYPE.DOCTOR:
                show_name = service['city'] + " " + service['doctor_name']
            else:
                show_name = service['doctor_name']
        else:
            show_name = service['hospital_name']

        result.append(
            {
                'service_id': service['service_item_id'],
                'service_image': service['service_image'],
                'service_name': service['service_name'],
                'city_doctor': show_name,
                'price_gengmei': service['price_gengmei'],
            }
        )

    return result


@bind("api/service_special")
@bind("api/special/get")
@cache_page(120)
@list_interface(offset_name='start_num', limit_name='count',
                element_model=Service,
                element_func_list=[Service.get_service_info])
def get_service_special_list(id, count=10, start_num=0):
    """福利专题
    """
    result = None

    try:
        special = rpc.db_opt.add_related(
            Special.objects.filter(pk=id),
            Special.special_info,
        ).get()

        services = []
        if count:
            # NOTE: M站大促优化
            items = special.items.filter(service__is_online=True, service__doctor__is_online=True)
            items = items.order_by('rank')
            items = items[start_num:start_num + count]

            items = items.select_related('service')
            items = rpc.db_opt.add_related(
                items,
                Service.get_service_info,
                'service__',
            )

            for item in items:
                service = item.service.get_service_info()
                service['is_price_range'] = False
                if service.get('is_multiattribute'):
                    service['is_price_range'] = True
                    price = re.split(r'\s+-', str(service['gengmei_price']))[0]
                    service['gengmei_price'] = price
                    services.append(service)

        result = special.special_info()
        result['services'] = services
        return result

    except Special.DoesNotExist:
        pass

    return result


@bind("api/special/list_seckill")
@cache_page(120)
@list_interface(offset_name='offset', limit_name='limit',
                element_model=Service,
                element_func_list=[Service.get_service_detail])
def get_seckill_special_list(id, start_time=None, end_time=None,
                             province_id=None, offset=0, limit=10,
                             is_recommend=None):
    result = {}
    try:
        filters = Q(special_id=id)
        if is_recommend is not None:
            filters &= Q(is_recommend=is_recommend)
        specialitems = SpecialItem.objects.filter(filters).order_by('rank')
        service_ids = [specialitem.service_id for specialitem in specialitems]
        services_rank = {specialitem.service_id: specialitem.rank for specialitem in specialitems}

        q = Q(id__in=service_ids)
        q &= Q(is_online=True, doctor__is_online=True)
        if start_time and end_time:
            start = get_datetime(start_time)
            end = get_datetime(end_time)
            q &= Q(start_time__gte=start)
            q &= Q(start_time__lt=end)

        services = Service.objects.filter(q)
        services = services[offset:offset + limit]

        services = rpc.db_opt.add_related(
            services,
            Service.get_service_info,
        )

        items = []
        for service in services:
            is_recommend = get_service_is_recommend(specialitems, service)
            if province_id:
                if service.doctor.hospital.city.province_id == province_id:
                    service_detail = service.get_service_detail()
                    service_detail['is_recommend'] = is_recommend
                    service_detail['rank'] = services_rank[service.id]
                    items.append(service_detail)
            else:
                service_detail = service.get_service_detail()
                service_detail['is_recommend'] = is_recommend
                service_detail['rank'] = services_rank[service.id]
                items.append(service_detail)
        result['services'] = items

    except SpecialItem.DoesNotExist:
        return gen(CODES.SERVICE_NOT_EXSIT)

    return result


@bind("api/previous_service_special")
def previous_service_special(id=None):
    """往期专题(最新的3个福利专题，展示内容包括图片、标题、描述)
    result = None

    if id:
        now = datetime.datetime.now()
        q = Q(is_online=True) & Q(is_seckill=False) & Q(is_recommendpool=True)
        q = q & Q(start_time__lte=now)
        specials = Special.objects.filter(q)

        q = Q(end_time=None) | Q(end_time__gt=now)
        q = q & Q(type=Special.SPECIAL_TYPE.SERVICE)
        specials = specials.filter(q).exclude(id=id).order_by('-id')[:3]

        result = [{
            'title': special.title, 'content': special.content,
            'image': special.image, 'id': special.id,
            'sell_amount_display': special.sell_amount_display} for special in specials
        ]

    return result
    comment out at 2017.5.5
    """
    return []


@bind("api/special/list")
@cache_page(120)
@list_interface(offset_name='start_num', limit_name='count',
                element_model=Special,
                element_func_list=[Special.special_info])
def special_list(count=10, start_num=0, recommend_only=False):
    """福利专题列表
    """
    specials = Special.objects.filter(is_online=True)
    specials = specials.filter(Q(start_time__isnull=True) | Q(start_time__lt=timezone.now))
    specials = specials.filter(Q(end_time__isnull=True) | Q(end_time__gt=timezone.now))

    if recommend_only:
        specials = specials.filter(recommend=True)

    specials = specials.order_by('rank')

    specials = rpc.db_opt.add_related(
        specials,
        Special.special_info
    )

    result = []

    for special in specials[start_num: start_num + count]:
        result.append(special.special_info())

    return result


@bind("api/special/service_list")
@cache_page(120)
@list_interface(offset_name='start_num', limit_name='count',
                element_model=Special,
                element_func_list=[Special.special_info])
def special_list(count=10, start_num=0, special_type=SPECIAL_TYPE.SERVICE, recommend_only=False,
                 is_advertisement=False, is_seckill=False):
    """福利专题列表
    """
    specials = Special.objects.filter(is_online=True, type=special_type, is_seckill=is_seckill,
                                      is_advertisement=is_advertisement)
    specials = specials.filter(Q(start_time__isnull=True) | Q(start_time__lt=timezone.now))
    specials = specials.filter(Q(end_time__isnull=True) | Q(end_time__gt=timezone.now))

    if recommend_only:
        specials = specials.filter(recommend=True)

    specials = specials.order_by('-start_time')
    now = datetime.datetime.now()
    specials = sorted(specials, key=lambda s: now - s.start_time if s.start_time else 9999)

    result = []

    for special in specials[start_num: start_num + count]:
        result.append({
            'image': special.image,
            'title': special.title,
            'icon': special.image,
            'id': special.id,
        })

    return result


@bind_context("api/special/polymer")
def api_special_polymer(
        ctx,
        seckill_type,
        order_by=SERVICE_ORDER_TYPE.DEFAULT,
        start_num=0,
        count=10,
        current_city_id=None,
        sort_params=None
):
    result = {'seckill_list': []}
    now = timezone.now
    try:
        seckills = Special.objects.filter(is_online=True, is_seckill=True, seckill_type=seckill_type,
                                          start_time__lte=now, end_time__gte=now).order_by('rank', 'end_time', 'id')
    except Special.DoesNotExist:
        return gen(CODES.SPECIAL_DOES_NOT_EXIST)

    services_display_count = 4
    for seckill in seckills[start_num: start_num + count]:
        sort_params.update({'special_id': seckill.id})
        services = service_info_manager.filter_service_v2_seckill_list_for_special(
            ctx=ctx,
            order_by=order_by,
            filters={'special_id': seckill.id},
            sort_params=sort_params,
            current_city_id=current_city_id,
        )
        rank_mode = services['rank_mode']
        data = {
            'special_id': seckill.id,
            'end_timestamp': str(get_timestamp(seckill.end_time)),
            'banner': seckill.image,
            'services_list': [],
            'rank_mode': rank_mode
        }

        if len(services['service_list']) >= services_display_count:
            services_list = build_service_info(services['service_list'])
            data['services_list'] = services_list[:services_display_count]

        result['seckill_list'].append(data)

    return result


@bind("api/special_one/get")
@cache_page(120)
def api_special_one_get(special_id):
    try:
        special = Special.objects.get(pk=special_id, is_online=True)
    except Special.DoesNotExist:
        return gen(CODES.SPECIAL_DOES_NOT_EXIST)
    except:
        logging_exception()
        return gen(CODES.SPECIAL_DOES_NOT_EXIST)

    result = special.data()
    result.update({'static_template': SpecialGadget.get_gadgets(special_id)})

    result.update({'floors': SpecialLayout.layout_data(special_id)})
    # NOTE: SpecialLayout.special_layouts_data中已包含{'floors': SpecialLayout.layout_data(special_id)}的数据
    # NOTE: 为兼容老接口没有删掉 以后可以根据需求只是用special_layouts_data获取SpecialLayout的数据

    special_layouts = {'special_layout': SpecialLayout.special_layouts_data(special_id)}
    result.update(special_layouts)

    # 美购外显关联数据从关联专题中获取
    filters = {'special_id': special_id}
    or_filters = []
    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})

    rpc_client = get_rpc_remote_invoker()
    service_filter_result = rpc_client['doris/search/query_spu'](
        size=10,
        sort_type=SERVICE_ORDER_TYPE.DEFAULT_SPECIAL,
        filters=filters,
        or_filters=or_filters).unwrap()
    # service_filter_result = filter_service(
    #     size=10,
    #     sort_type=SERVICE_ORDER_TYPE.DEFAULT_SPECIAL,
    #     filters=filters,
    # )
    service_id_list = service_filter_result['service_ids']
    rank_mode = service_filter_result['rank_mode']
    special_exterior = SpecialExterior.objects.filter(special_id=special_id, show_position=SPECIAL_EXTRA_POSITION.SPECIAL,is_online=True)
    special_exterior_data = []
    if special_exterior:
        service_filter_result = SpecialItem.objects.filter(service__in=service_id_list, special_id=special_id)
        for exterior in service_filter_result:
            s = exterior.service
            data = {'special_id':special_id,
                    'banner':special_exterior.image,
                    'new_banner': special_exterior.new_big_image or special_exterior.new_image,
                    'services':{
                        'id': s.id,
                        'tag_name': (s.tags.first() or '') and s.tags.first().name,
                        'doctor_name': s.doctor and s.doctor.name,
                        'doctor_is_officer': s.doctor and s.doctor.doctor_type == DOCTOR_TYPE.OFFICER or False,
                        'hospital_name': s.hospital and s.hospital.name,
                        'gengmei_prices': s.gengmei_price_list,
                        'image_header': s.image_header,
                        'city_name': s.city_name,
                        'original_price': s.lowest_original_price,
                        }
                    }
            special_exterior_data.append(data)
    result.update({'special_exterior':special_exterior_data,"rank_mode":rank_mode})
    # result.update({'special_exterior': SpecialExterior.get_special_exterior(
    #     special_id, show_position=SPECIAL_EXTRA_POSITION.SPECIAL
    # )})

    return result


# @bind_context("api/special/diary_list")
# @list_interface(offset_name='start_num', limit_name='count',
#                 element_model=Diary, element_func_list=[Diary.get_diary_info])
# # TODO zhangyunyu 这里是有使用的
# def get_special_diary_list(ctx, id, count=10, start_num=0):
#     """案例专题"""
#     # todo 已无入口，不再维护 20180313
#     # todo 已无入口，不再维护 20180313
#     # todo 已无入口，不再维护 20180313
#     result = {}
#
#     try:
#         s = rpc.db_opt.add_related(
#             Special.objects.filter(pk=id),
#             Special.special_info,
#         ).get()
#
#         if not s.is_online:
#             return result
#
#         result.update(s.special_info())
#
#         diary_ids = list(s.items.values_list('diary_id', flat=True))
#         diarys = Diary.objects.filter(id__in=diary_ids)[start_num:start_num + count]
#
#         user = get_user_from_context(ctx)
#         data = [diary.get_diary_info(user=user) for diary in diarys]
#         result['diaries'] = data
#
#     except Special.DoesNotExist:
#         pass
#
#     return result


@bind('api/special/service_home')
@cache_page(120)
def get_special_servicehome():
    """美购首页banner"""
    result = []
    specials = Special.objects.filter(is_servicehome=True).filter(is_online=True).filter(
        type=Special.SPECIAL_TYPE.SERVICE)
    specials = specials.filter(Q(start_time__isnull=True) | Q(start_time__lt=timezone.now))
    specials = specials.filter(Q(end_time__isnull=True) | Q(end_time__gt=timezone.now))
    specials = specials.order_by('rank')

    specials = rpc.db_opt.add_related(specials, Special.special_info)

    for s in specials:
        result.append(s.special_info())
    return result


@bind_context('api/special_item/notify', login_required=True)
def set_seckill_notify(ctx, special_id, service_id, set_notify, service_item_id=None):
    """
    special_id: int   秒杀专场id
    service_id: int   美购id
    set_notify: bool  True:设置提醒  False:取消提醒
    """
    user = get_user_from_context(ctx)
    set_notify = json.loads(set_notify)

    seckill_special_item = SpecialItem.objects.filter(
        special_id=special_id, service_id=service_id, serviceitem_id=service_item_id
    ).exists()

    if not seckill_special_item:
        return gen(CODES.SECKILL_NOT_FOUND)

    if set_notify:
        # 设置提醒
        already_added = SeckillNotify.objects.filter(person=user.person,
                                                     service_id=service_id,
                                                     service_item_id=service_item_id,
                                                     special_id=special_id).exists()
        if already_added:
            # 已经设置过提醒
            return gen(CODES.SECKILL_NOTIFY_HAS_CREATED)
        else:
            # 正常设置提醒
            try:
                SeckillNotify.objects.create(
                    service_id=service_id, service_item_id=service_item_id,
                    special_id=special_id, person=user.person
                )
            except:
                # 处理创建时的异常
                return gen(CODES.SECKILL_NOTIFY_CREATE_FAIL)
    else:
        # 取消提醒
        try:
            notify = SeckillNotify.objects.filter(person=user.person,
                                                  service_id=service_id,
                                                  service_item_id=service_item_id,
                                                  special_id=special_id)
            notify.delete()
        except SeckillNotify.DoesNotExist:
            # 没有设置过提醒
            return gen(CODES.SECKILL_NOTIFY_NOT_FOUND)

    return {
        'set_success': True,
        'status': set_notify,
    }


@bind_context('api/special/special_service')
def get_special_service(ctx, city_id, limit=10, sort_params={}, offset=5, device_id=None, platform=None, switch=False,
                        show_position=SPECIAL_EXTRA_POSITION.SERVICE_HOMIE):
    """
    7625修改: 获取美购首页外显banner, 增加新用户和是否支付维度
    7.15.0修改：区分展示位置(首页、美购首页)
    :param ctx:
    :param city_id:
    :param limit:
    :param sort_params:
    :param offset:
    :param device_id:
    :param platform:
    :param switch:
    :param show_position: 展示位置
    :return:
    """
    if switch == False:
        return old_get_special_service(ctx, city_id, limit=10, sort_params={}, offset=5, device_id=None, platform=None)
    user = get_user_from_context(ctx)
    params = {'platform': platform, 'device_id': device_id, 'user': user if user else None}
    user_payment_info = get_user_and_payment_info(**params)

    user_type = SLIDE_USER_TYPE.OLD_USER
    if user_payment_info and user_payment_info.get('is_new_user'):
        user_type = SLIDE_USER_TYPE.NEW_USER

    payment_type = SLIDE_PAYMENT_TYPE.HAVE_PAYMENT
    if user_payment_info and not user_payment_info.get('have_payment'):
        payment_type = SLIDE_PAYMENT_TYPE.NO_PAYMENT

    region, country = (None, None)
    try:
        city = City.objects.get(id=city_id)
        if city.province:
            region = city.province.region
            country = city.province.country
    except City.DoesNotExist:
        pass

    if not city_id or city_id in WORLD_AREA_TYPE or (region and region.english_name == 'haiwai') or \
            (country and country.id != 'china'):
        city_query = Q(cities__isnull=True, regions__isnull=True)
    else:
        city_query = Q(cities=city_id) | Q(cities__isnull=True, regions__isnull=True)
        if region:
            city_query |= Q(regions=region)

    user_payment_query = Q(user_type__in=[user_type, SLIDE_USER_TYPE.ALL_USER]) \
                            & Q(payment_type__in=[payment_type, SLIDE_PAYMENT_TYPE.ALL_PAYMENT])
    now = timezone.now
    query = city_query & Q(is_online=True) & user_payment_query
    query &= Q(start_time__lte=now)
    query &= (Q(end_time__isnull=True) | Q(end_time__gte=now))
    query &= Q(show_position=show_position)

    specials = SpecialExterior.objects.filter(query).order_by('rank').distinct()

    special_dct = {rank: item.id for rank, item in enumerate(specialexterior_aggregate_by_rank(specials, city_id, region and region.id)[:limit])}
    return special_dct


@bind_context('api/special/get_special_info')
def get_special_info(
        ctx, special_ext_id, sort_params,
        device_id, city_id, offset=5, floor_id=None,
        show_position=SPECIAL_EXTRA_POSITION.SERVICE_HOMIE):
    special_ext = SpecialExterior.objects.get(id=special_ext_id, show_position=show_position)
    special_polymer_id = special_ext.special_polymer_id
    aggregate_type = special_ext.aggregate_type

    lng = sort_params['lon']
    lat = sort_params['lat']

    # 取秒杀聚合页，最近有效的秒杀专场id
    if special_polymer_id and aggregate_type == POLYMER_TYPE.SECKILL_POLYMER:
        secp = SeckillPolymer.objects.get(id=special_polymer_id)
        _special_id = secp.get_future_special_id()
    elif special_polymer_id and aggregate_type == POLYMER_TYPE.SPECIAL_POLYMER:
        secp = SeckillPolymer.objects.get(id=special_polymer_id)
        if not city_id or city_id == 'worldwide':
            city = get_city_by_options(lat=lat, lng=lng)
            city_id = city.id if city else None
        _special_id = secp.get_target_special_id_by_condition(city_id=city_id)
    else:
        _special_id = special_ext.special_id

    try:
        special = Special.objects.get(id=_special_id)
    except Special.DoesNotExist:
        return gen(CODES.SPECIAL_DOES_NOT_EXIST)

    sort_params.update({
        'special_id': _special_id,
        'device_id': device_id,
    })
    # 获取service_id
    country_tagid, city_tagid = get_location_tag_id_by_city_id(city_id)
    user = ctx.session.user
    user_city_tag_id = city_tagid and city_tagid or get_user_city_tagid(user.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

    or_filters = []

    # 楼层里面的美购列表
    si_obj = SpecialItem.objects.filter(special_id=_special_id).first()
    floor_id = si_obj.floor_id if si_obj else None
    if special_ext and 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 = special and list(special.tags.values_list('id', flat=True))

    if tag_ids:
        or_filters.append({'tag_ids': tag_ids})

    filter_result = ctx.rpc_remote['doris/search/special'](
        special_id=_special_id,
        user_city_info=user_city_info,
        sort_params=sort_params,
        filters={},
        or_filters=or_filters,
        offset=0,
        size=10,
        floor_id=floor_id,  # 这里并不是过滤当前楼层的数据, 而是发现es那边没有过滤当前城市的数据, 做一个新老专题的兼容
    ).unwrap()
    rank_mode = filter_result['rank_mode']
    sku_ids = [v['sku_id'] for v in filter_result['skus']]
    if floor_id:
        # 去除位置为0的sku, 对剩余进行位置排序
        sorted_has_position_sku = sorted(list(filter(lambda item: item.get('position', 0), filter_result['skus'])), key=lambda v: v['position'])
        left_zero_postion_sku = list(set(sku_ids) - set([v['sku_id'] for v in sorted_has_position_sku]))
        sku_ids = [v['sku_id'] for v in sorted_has_position_sku]
        sku_ids.extend(left_zero_postion_sku)

    siid_to_sid_tuple = ServiceItem.objects.filter(
        parent_id=0, id__in=sku_ids, service__is_online=True, service__doctor__is_online=True, is_delete=False
    ).values_list('id', 'service_id')
    siid_to_sid = {}
    for siid, sid in siid_to_sid_tuple:
        siid_to_sid[siid] = sid
    service_ids = []
    for sku_id in sku_ids:
        if sku_id in siid_to_sid:
            service_ids.append(siid_to_sid[sku_id])

    services_dict = {x.id: x.get_special_show_info(city_id) for x in Service.objects.filter(id__in=service_ids)}
    services = [services_dict[x] for x in service_ids if x in services_dict][:offset]

    # services.append({
    #     "gengmei_price": '',
    #     "service_name": '',
    #     "service_image": '',
    #     "is_price_range": False,
    #     "service_id": '',
    #     "service_url": PushUrlProtocol.PROMOTION_SPECIAL.format(id=special_ext.special.id),
    #     "service_tag": '',
    #     "city_doctor": ''
    # })

    result = {
        'name': special_ext.name,
        'banner_pic': special_ext.image,
        'banner_image': special_ext.new_big_image or special_ext.new_image,
        'is_large': True if special_ext.new_big_image else False,
        'special_id': _special_id,
        'business_type': 'advertise' if special and special.is_advertisement else 'operating',
        'ordering': '-1',
        'banner_id': special_ext.id,
        'services': services,
        'rank_mode': rank_mode,
        'is_new_special': special and special.is_new_special or False,
        "special_is_seckill": special and special.is_seckill or False,
        "special_polymer_id": special_polymer_id or 0,
        "aggregate_type": aggregate_type,
    }
    result.update({
        "special_info": special and Special.format_seckill_info(special) or {},
    })
    return result


@bind("api/special/get_rank_list")
def get_rank_list(special_id):
    lists = RankList.objects.filter(special_id=special_id, is_online=True).order_by('rank', '-id')
    result = []
    for l in lists:
        d = {
            "rank_id": l.id,
            "rank_banner": l.image,
            "rank_type": 'doctor_rank' if l.rank_type == RANK_LIST_TYPE.DOCTOR else "service_rank"
        }
        result.append(d)
    return result


@bind("api/special/get_doctor_rank_list")
def get_doctor_rank_list(special_id, rank_id, start_num, count):
    rank = RankList.objects.get(special_id=special_id, id=rank_id)
    rank_services = RankListService.objects.filter(rank_list=rank,
                                                   service__is_online=True,
                                                   service__doctor__is_online=True
                                                   ).order_by('rank', '-service_id')[start_num: start_num + count]
    result = []
    for rank_service in rank_services:
        start_num += 1
        service = rank_service.service
        d = {
            'id': service.doctor.id,
            "rank": "%02d" % start_num,
            "doctor_name": service.doctor.name,
            "hospital": service.doctor.hospital.name if service.doctor.doctor_type == DOCTOR_TYPE.DOCTOR else "",
            "portrait": get_full_path(service.doctor.portrait, '-thumb'),
            "desc": rank_service.message,
            "case_count": service.diaries_count,
            "appoint_count": service.sell_amount_display,
            "related_service": {
                "id": service.id,
                "service_name": service.project_type.name if service.project_type else service.name,
                "gengmei_price": service.gengmei_price_display.split('-')[0]
            }
        }
        result.append(d)
    return result


@bind("api/special/get_service_rank_list")
def get_service_rank_list(special_id, rank_id, start_num, count):
    rank = RankList.objects.get(special_id=special_id, id=rank_id)
    rank_services = RankListService.objects.filter(rank_list=rank,
                                                   service__is_online=True,
                                                   service__doctor__is_online=True
                                                   ).order_by('rank', '-service_id')[start_num: start_num + count]

    result = []
    for rank_service in rank_services:
        start_num += 1
        service = rank_service.service
        d = {
            "id": service.id,
            "rank": "%02d" % start_num,
            "service_name": service.project_type.name if service.project_type else service.name,
            "doctor_name": service.doctor.name,
            "hospital": service.doctor.hospital.name if service.doctor.doctor_type == DOCTOR_TYPE.DOCTOR else "",
            "image": get_full_path(service.image_header, '-thumb'),
            "desc": rank_service.message,
            "appoint_count": service.sell_amount_display,
            "gengmei_price": service.gengmei_price_display.split('-')[0]
        }
        result.append(d)
    return result


@bind('api/special/recommend_doctor')
@list_interface(offset_name='start_num', limit_name='count')
def get_special_recommend_doctors(special_id, start_num, count):
    layout_data = SpecialLayout.layout_data(special_id, SPECIAL_MODULE.RECOMMEND_DOCTOR)
    doctors = layout_data['content']

    sorted_doctors = sorted(doctors, key=lambda k: k['rank'])
    all_sorted_doctor_ids = [d['doctor_id'] for d in sorted_doctors]
    doctor_id_list = sorted_doctors[start_num: start_num + count]

    doctor_ids = [d['doctor_id'] for d in doctor_id_list]

    if len(doctor_ids) < count:
        rpc_client = get_rpc_remote_invoker()

        if len(doctor_ids):
            offset = 0
        else:
            offset = start_num - len(sorted_doctors)
        size = count + len(sorted_doctors)

        try:
            filter_doctor_result = rpc_client['doris/search/get_doctors_by_special_id'](
                special_id=special_id, offset=offset, size=size
            ).unwrap()
            filter_doctor_ids = filter_doctor_result['doctors']

            reduced_doctor_ids = [d for d in filter_doctor_ids if d not in all_sorted_doctor_ids]
        except:
            logging_exception()
            reduced_doctor_ids = []

        doctor_ids.extend(reduced_doctor_ids[:count - len(doctor_ids)])

    doctors = get_objects_from_queryset_and_pk_list(Doctor.objects, doctor_ids)

    result = []
    for d in doctors:
        _, item_list = d.get_doctor_item_list()
        data = {
            'id': d.id,
            'hospital_id': d.hospital.id,
            'name': d.name,
            'portrait': d.portrait,
            'hospital': d.hospital.name,
            'case_count': d.share_diary_num,
            'tags': item_list,
            'doctor_type': d.doctor_type,
            'title': d.title,
            'rate': d.doctor_rate_star(),
        }
        data.update(d.get_doctor_lincence(d.title, d.services.count()))
        result.append(data)
    return result


@bind('api/special/get_special_by_ids')
def get_special_by_ids(special_ids=None):
    try:
        result = Special.objects.filter(pk__in=special_ids).values('id', 'title', 'start_time', 'is_online', 'image','desc', 'is_new_special')
    except:
        result = []

    result = list(result)
    map(lambda x: x.update({
        'start_time': x['start_time'].strftime("%Y-%m-%d %H:%M:%S") if x.get('start_time') else ''
    }), result)

    return result


@bind('api/special/get_special_seckill_by_ids')
def get_special_seckill_by_ids(special_ids=None):
    query = Q(pk__in=special_ids) & Q(is_seckill=True) & Q(is_online=True)

    import datetime
    current_time = datetime.datetime.now()
    query &= Q(start_time__lt=current_time)
    query &= Q(end_time__gte=current_time)
    try:
        special_info = Special.objects.filter(query).order_by('-start_time').values('id', 'start_time', 'end_time')
    except:
        special_info = []
    special_info = list(special_info)

    map(lambda x: x.update({
        'count_down': int((x['end_time'] - timezone.now()).total_seconds()) if x.get('end_time') else 0,
        'start_time': x['start_time'].strftime("%Y-%m-%d %H:%M:%S"),
        'end_time': x['end_time'].strftime("%Y-%m-%d %H:%M:%S")}), special_info)

    return special_info


@bind('api/special/gengmei_seckill')
def get_gengmei_seckill_by_id(special_id):
    return Special.get_gengmei_seckill_by_id(special_id=special_id)


def old_get_special_service(ctx, city_id, limit=10, sort_params={}, offset=5, device_id=None, platform=None):
    """
    7625修改: 获取美购首页外显banner, 增加新用户和是否支付维度
    :param ctx:
    :param city_id:
    :param limit:
    :param sort_params:
    :param offset:
    :param device_id:
    :param platform:
    :return:
    """
    user = get_user_from_context(ctx)
    params = {'platform': platform, 'device_id': device_id, 'user': user if user else None}
    user_payment_info = get_user_and_payment_info(**params)

    user_type = SLIDE_USER_TYPE.OLD_USER
    if user_payment_info and user_payment_info.get('is_new_user'):
        user_type = SLIDE_USER_TYPE.NEW_USER

    payment_type = SLIDE_PAYMENT_TYPE.HAVE_PAYMENT
    if user_payment_info and not user_payment_info.get('have_payment'):
        payment_type = SLIDE_PAYMENT_TYPE.NO_PAYMENT

    region, country = (None, None)
    try:
        city = City.objects.get(id=city_id)
        if city.province:
            region = city.province.region
            country = city.province.country
    except City.DoesNotExist:
        pass

    if not city_id or city_id in WORLD_AREA_TYPE or (region and region.english_name == 'haiwai') or \
            (country and country.id != 'china'):
        city_query = Q(cities__isnull=True, regions__isnull=True)
    else:
        city_query = Q(cities=city_id) | Q(cities__isnull=True, regions__isnull=True)
        if region:
            city_query |= Q(regions=region)

    user_payment_query = Q(user_type__in=[user_type, SLIDE_USER_TYPE.ALL_USER]) \
                            & Q(payment_type__in=[payment_type, SLIDE_PAYMENT_TYPE.ALL_PAYMENT])
    now = timezone.now
    query = city_query & Q(is_online=True) & user_payment_query
    query &= Q(start_time__lte=now)
    query &= (Q(end_time__isnull=True) | Q(end_time__gte=now))

    specials = SpecialExterior.objects.filter(query).order_by('rank').distinct()

    results = []
    for item in specialexterior_aggregate_by_rank(specials, city_id, region and region.id)[:limit]:
        special_ext = item
        sort_params.update({
            'special_id': special_ext.special.id,
            'device_id': device_id,
        })
        service_result = ctx.gaia_local["search/special/service_query"](
            sort_type=SERVICE_ORDER_TYPE.DEFAULT,
            size=10,
            special_id=special_ext.special.id,
            sort_params=sort_params,
            current_city_id=city_id,
        ).unwrap()
        rank_mode = service_result['rank_mode']

        service_ids = [x['source']['id'] for x in service_result['hits']]
        services_dict = {x.id: x.get_special_show_info() for x in Service.objects.filter(id__in=service_ids)}
        services = [services_dict[x] for x in service_ids if x in services_dict][:offset]

        services.append({
            "gengmei_price": '',
            "service_name": '',
            "service_image": '',
            "is_price_range": False,
            "service_id": '',
            "service_url": PushUrlProtocol.PROMOTION_SPECIAL.format(id=special_ext.special.id),
            "service_tag": '',
            "city_doctor": ''
        })

        result = {
            'banner_pic': special_ext.image,
            'special_id': special_ext.special.id,
            'banner_id': special_ext.id,
            'services': services,
            'rank_mode': rank_mode
        }
        results.append(result)

    return results


@bind('api/groupbuy_special/share_wechat_service_images_by_special_ids')
@cache_page(5 * 60)
def get_share_wechat_images_by_special_id(special_ids):
    special_id_and_share_wechat_images = GroupBuySpecial.objects.filter(
        special__id__in=special_ids
    ).values_list('special__id', 'share_wechat_images')
    special_id_to_share_wechat_images = {
        str(special_id): json.loads(share_wechat_images) if share_wechat_images else []
        for special_id, share_wechat_images in special_id_and_share_wechat_images
    }
    for special_id in special_ids:
        if str(special_id) not in special_id_to_share_wechat_images:
            special_id_to_share_wechat_images[str(special_id)] = []

    return special_id_to_share_wechat_images


@bind("api/new_special_one/get")
@cache_page(120)
def api_new_special_one_get(special_id):

    try:

        special = Special.objects.get(pk=special_id, is_online=True, is_new_special=True)
    except Special.DoesNotExist:
        return gen(CODES.SPECIAL_DOES_NOT_EXIST)

    coupons = SpecialLayout.new_special_layouts_data(special_id, SPECIAL_MODULE.COUPONS) or {}
    # 转盘
    turntables = SpecialLayout.new_special_layouts_data(special_id, SPECIAL_MODULE.TURNTABLE) or {}
    videos = SpecialLayout.new_special_layouts_data(special_id, SPECIAL_MODULE.VIDEO) or {}
    tool_bars = SpecialLayout.new_special_layouts_data(special_id, SPECIAL_MODULE.TOOL_BAR) or {}
    # 图片豆腐块
    gadget_templates = SpecialLayout.get_gadgets(special_id=special_id) or {}
    # 楼层数据
    storey_conf = SpecialLayout.new_special_layouts_data(special_id)
    if storey_conf:
        storey_conf['data']['data'] = []
        first_floor = SpecialStorey.get_first_floor(special_id)
        second_floor = SpecialStorey.get_second_floor(special_id)
        second_floor_data = defaultdict(list)
        for item in second_floor:  # special_id parent_id related sort
            second_floor_data[item["parent_id"]].append(item)

        for item in first_floor:
            item["data"] = second_floor_data.get(item["id"], [])

        storey_conf["data"]['data'].extend(first_floor)

    result = special.data()
    result.update({
        'floors': storey_conf,
        'videos': videos,
        'coupons': coupons,
        'turntables': turntables,
        'gadget_templates': gadget_templates,
        'tool_bars': tool_bars,
    })

    # 美购外显关联数据从关联专题中获取
    filters = {'special_id': special_id}
    or_filters = []
    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})

    rpc_client = get_rpc_remote_invoker()
    service_filter_result = rpc_client['doris/search/query_spu'](
        size=10,
        sort_type=SERVICE_ORDER_TYPE.DEFAULT_SPECIAL,
        filters=filters,
        or_filters=or_filters).unwrap()

    service_id_list = service_filter_result['service_ids']
    rank_mode = service_filter_result['rank_mode']
    special_exterior = SpecialExterior.objects.filter(special_id=special_id, show_position=SPECIAL_EXTRA_POSITION.SPECIAL, is_online=True)
    special_exterior_data = []
    if special_exterior:
        service_filter_result = SpecialItem.objects.filter(service__in=service_id_list, special_id=special_id)
        for exterior in service_filter_result:
            s = exterior.service
            data = {'special_id':special_id,
                    'banner':special_exterior.image,
                    'services':{
                        'id': s.id,
                        'tag_name': (s.tags.first() or '') and s.tags.first().name,
                        'doctor_name': s.doctor and s.doctor.name,
                        'doctor_is_officer': s.doctor and s.doctor.doctor_type == DOCTOR_TYPE.OFFICER or False,
                        'hospital_name': s.hospital and s.hospital.name,
                        'gengmei_prices': s.gengmei_price_list,
                        'image_header': s.image_header,
                        'city_name': s.city_name,
                        'original_price': s.lowest_original_price,
                        }
                    }
            special_exterior_data.append(data)
    result.update({'special_exterior':special_exterior_data, "rank_mode": rank_mode})

    return result


@bind("api/new_special_one/floor_info")
@cache_page(120)
def api_new_special_floor_info(special_id, floor_id):

    floor_info = SpecialStorey.get_floor_data(special_id, floor_id)

    return floor_info


@bind("api/seckill/polymer/info")
def api_seckill_polymer_info(seckill_polymer_id):
    """
    秒杀聚合页接口
    :param seckill_polymer_id:
    :return:
    """
    try:
        seckill_polymer = SeckillPolymer.objects.get(id=seckill_polymer_id, is_online=True)
    except SeckillPolymer.DoesNotExist:
        return gen(CODES.SPECIAL_DOES_NOT_EXIST)

    active_seckill_id = seckill_polymer.get_future_special_id()
    seckill_data_list = seckill_polymer.get_related_seckill_activities()  # -1: 已结束, 0: 正在抢, 1: 未开始
    sec_layout = SeckillPolymerLayout.objects.filter(
        seckill_polymer_id=seckill_polymer.id, module=SECKILL_POLYMER_MODULES.TOOLBAR).first()

    try:
        sp = Special.objects.get(id=active_seckill_id)
    except Special.DoesNotExist:
        sp = None

    if sec_layout:
        is_show_toolbar = sec_layout.is_visible
        toolbar_list = json.loads(sec_layout.related)
    else:
        is_show_toolbar = False
        toolbar_list = []

    return {
        "title": seckill_polymer.title,
        "is_show_rule": bool(seckill_polymer.rules),
        "activity_rules": seckill_polymer.rules,
        "polymer_banner_image": seckill_polymer.image,
        "seckill_list": seckill_data_list,
        "is_show_toolbar": is_show_toolbar,
        "toolbar_list": toolbar_list,
        "active_seckill_id": active_seckill_id,
        "share_data": seckill_polymer.get_share_data(),
    }


@bind("api/seckill/base/info")
def api_seckill_base_info(seckill_id):
    """
    获取秒杀单场基础信息接口
    :param seckill_id:
    :return:
    """
    try:
        special = Special.objects.get(id=seckill_id, is_online=True)
    except Special.DoesNotExist:
        return gen(CODES.SPECIAL_DOES_NOT_EXIST)

    sp_data_dict = Special.format_seckill_info(special)
    if "special_id" not in sp_data_dict:
        sp_data_dict['special_id'] = special.id

    data = special.data()
    sp_data_dict.update({
        "banner_image": special.image,
        "selected_image": special.polymer_backgrond_image,
        "unselected_image": special.polymer_unselected_image,
        "is_show_rule": bool(special.activity_rules),
        "activity_rules": special.activity_rules,
        "special_layout": SpecialLayout.special_layouts_data(special.id),
        "share_data": special.get_share_data()
    })
    data.update(sp_data_dict)
    return {"data": data}


@bind("api/special/polymer/info")
def api_special_polymer_info(special_polymer_id, current_city_id=None, lng=None, lat=None):
    """
    获取专题聚合数据
    :param special_polymer_id:
    :param current_city_id:
    :return:
    """
    try:
        seckill_polymer = SeckillPolymer.objects.get(
            id=special_polymer_id, is_online=True,
            polymer_type=POLYMER_TYPE.SPECIAL_POLYMER
        )
    except SeckillPolymer.DoesNotExist:
        return gen(CODES.SPECIAL_DOES_NOT_EXIST)

    button_data = []

    # 按地域进行选中
    if seckill_polymer.button_selection == BUTTON_SELECTION.GEOGRAPHICAL:
        button_data = SeckillPolymerButton.get_target_polymer_data(seckill_polymer.id)
        region_id = None
        try:
            city = City.objects.get(id=current_city_id)
            region_id = city.province.region_id
        except City.DoesNotExist:
            city = get_city_by_options(lat=lat, lng=lng)
            if city:
                region_id = city.province.region_id

        if not region_id:
            region_id = -1

        for button in button_data:
            if SpecialRegion.objects.filter(
                    special_id=button['special_id'], region_id=region_id
            ).exists():
                button.update({'is_selected': True})
            else:
                button.update({'is_selected': False})

    elif seckill_polymer.button_selection == BUTTON_SELECTION.FIRST:  # 按第一位进行选中
        button_data = SeckillPolymerButton.get_target_polymer_data(seckill_polymer.id)
        for index, button in enumerate(button_data):
            if index == 0:
                button.update({'is_selected': True})
            else:
                button.update({'is_selected': False})

    return {
        'polymer_id': seckill_polymer.id,
        'polymer_title': seckill_polymer.title,
        'polymer_image': seckill_polymer.image,
        "share_data": seckill_polymer.get_share_data(),
        'button_data': button_data,
    }


