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

import datetime
import hashlib

from django.conf import settings
from django.db import transaction
from gm_types.gaia import (
    SERVICE_ITEM_PRICE_TYPE, COUPON_TYPES, SETTLEMENT_CANCEL_REASON, SETTLEMENT_BUY_AGAIN_RESULT,
    GROUPBUY_STATUS, ORDER_STATUS
)
from gm_types.trade import INSURANCE_TYPE
from gm_types.poseidon import AllPromotionType

import point
from api.models import Person, OrderCouponInfo, ServiceItemPrice, OrderUserInfo
from api.models import (
    Settlement,
    SettlementItem,
    SERVICE_PAYMENT_TYPE,
    SETTLEMENT_STATUS,
    CouponInfo,
    ORDER_OPERATION_TYPE,
    ORDER_OPERATION_ROLE,
    Order,
    ServiceItem,
    Shopcart,
    Service,
    PeriodHospital,
)
from api.tasks import order_task
from api.tool.datetime_tool import get_timestamp
from api.tool.log_tool import logging_exception
from api.tool.service_tool import get_serviceitem_by_option_id
from api.tool.settlement_tool import cancel_settlement
from api.tool.user_tool import get_user_extra_by_user_id
from api.tool.user_tool import get_user_from_context
from api.tool.user_tool import is_merchant_user
from maidan.models.order import MaidanOrder
from pay.manager import settlement_manager
from pay.manager.settlement_manager import get_services_from_cart_or_service_id_itemkey
from pay.tool import own_tool
from pay.views.purchase import compute_points_deduction_per_service_v2
from rpc.decorators import bind_context
from rpc.tool.error_code import gen, CODES
from rpc.tool.protocol import gm_protocol


@bind_context('api/settlement/delete', login_required=True)
def settlement_delete(ctx, id, cancel_reason=SETTLEMENT_CANCEL_REASON.UNKNOWN, device_id=None, ip=None):
    user = get_user_from_context(ctx)

    try:
        settlement = Settlement.objects.get(id=id, person=user.person)
    except Settlement.DoesNotExist:
        return gen(CODES.SETTLEMENT_DOES_NOT_EXIST)

    if cancel_reason not in (
        SETTLEMENT_CANCEL_REASON.UNKNOWN, SETTLEMENT_CANCEL_REASON.TIMEOUT, SETTLEMENT_CANCEL_REASON.WONT_BUY,
        SETTLEMENT_CANCEL_REASON.WRONG, SETTLEMENT_CANCEL_REASON.NOT_USE_POINT
    ):
        return gen(CODES.SETTLEMENT_CANCEL_FAIL)

    if settlement.status not in (SETTLEMENT_STATUS.NOT_PAID, SETTLEMENT_STATUS.PAYING):
        return gen(CODES.SETTLEMENT_CANCEL_FAIL)

    try:
        cancel_settlement(settlement, cancel_reason)
        orders = [i.order for i in settlement.items.all()]
        #记录用户设备标识,IP
        order_task.record_user_information.delay(orders, '', device_id, ip, ORDER_STATUS.CANCEL)
    except:
        logging_exception()
        return gen(CODES.SETTLEMENT_CANCEL_FAIL)

    return gen(CODES.SUCCESS)


def _can_buy(user, service_item_id=None, service_ids=None):
    # 判断黑名单用户是否可以购买该商品
    if not user:
        return True
    from django.conf import settings
    blacklist = settings.BLACKLIST
    if user.id not in blacklist:
        return True
    service_id_list = []
    if service_item_id:
        service_item = ServiceItem.objects.filter(id=service_item_id).first()
        if not service_item:
            return True
        service_id_list = [service_item.service_id]
    if service_ids:
        service_id_list = service_ids
    status = [ORDER_STATUS.PAID, ORDER_STATUS.PAYING, ORDER_STATUS.USED, ORDER_STATUS.REFUNDED, ORDER_STATUS.SETTLED, ORDER_STATUS.WAIT_REFUNDED]
    orders = Order.objects.filter(user_id=user.id, service_id__in=service_id_list, status__in=status)
    if orders:
        return False
    return True


@bind_context('api/settlement/preview', login_required=True)
def settlement_preview_v2(
        ctx, cart_item_ids=[], service_id=None, item_key=None, number=1,
        cart_item_info={}, service_item_id=None, only_show_platform_coupon=True,
        coupon_id=None, auto_select_best_coupon=False,
        platform_coupon_id=None, auto_select_platform=False,
        doctor_coupon_id=None, auto_select_doctor=False,
        use_platform_and_doctor_coupon=False,
        groupbuy_code=None, create_groupbuy_price_id=None,
        device_id=None, use_coupon_grey=None,
        promotion_item_relation=None,
    ):
    """get settlement preview.
        promotion_item_relation = {
                commodity_id: [{
                    'promotion_id': int,
                    'promotion_type': string,  # 活动类型 考虑转化为枚举
                    'promotion_discount': float,
                    'advance_discount': float,
                    'final_discount': float,
                    'advance_payment': float,
                    'final_payment': float,
                    'inventory': int,
                }]
            }
    return:
        services: list of service detail info
        person: dict of user extra info
        coupon_count: count of coupons can be used
    """
    promotion_ids = [obj['promotion_id'] for item in promotion_item_relation.values()
                     for obj in item] if promotion_item_relation else []
    user = get_user_from_context(ctx)

    # 黑名单 限制指定用户购买相同spu
    can_buy = _can_buy(user=user, service_item_id=service_item_id)
    if not can_buy:
        return gen(CODES.SETTLEMENT_CREATE_FAIL)

    result = {
        'services': [],
        'person': {},
        'points_deduction': 0,
        'is_can_discount': True,
    }

    groupbuy_team_id, groupbuy_price_id = settlement_manager.try_get_groupbuy_team_id_and_groupbuy_price_id(
        user.id, groupbuy_code, create_groupbuy_price_id
    )

    service_item_id_to_ordering_info = settlement_manager.get_service_item_id_to_ordering_info(
        cart_item_info,
        service_item_id, number,
        user, groupbuy_price_id, promotion_item_relation
    )
    service_item_ids = service_item_id_to_ordering_info.keys()
    if service_item_id:
        if service_item_ids:
            service_item_ids.append(service_item_id)
        else:
            service_item_ids = [service_item_id]

    cart_item_info = {item['id']: item['number'] for item in cart_item_info if item['id'] > 0}

    if promotion_item_relation:
        # 替换商品价格信息及活动信息
        # service_item_id_to_ordering_info
        for commodity_id, info in promotion_item_relation.items():
            info = info[0]  # 取第一个价格信息
            ordering_price_info = service_item_id_to_ordering_info[int(commodity_id)].ordering_price_info
            ordering_price_info['gengmei_price'] = info.get('advance_payment') + info.get('final_payment')
            ordering_price_info['pre_payment_price'] = info.get('advance_payment')
            promotion_type = info.get('promotion_type')
            if promotion_type == AllPromotionType.SEC_KILL:
                ordering_price_info['price_type'] = SERVICE_ITEM_PRICE_TYPE.SECKILL
            elif promotion_type == AllPromotionType.GROUP_BOOK:
                ordering_price_info['price_type'] = SERVICE_ITEM_PRICE_TYPE.GROUPBUY

    user_extra = get_user_extra_by_user_id(user.id)
    result['person'] = {
        'phone': user_extra.phone or user.person.phone,
        'points': point.get_point_for(user),
        'real_name': user_extra.name or u'',
        'address': user_extra.address or u'',
    }

    sum_gengmei_price = 0
    sum_service_prepayment = 0
    sum_payment_final_price = 0
    support_insurance = False
    payment_final_price = 0

    service_ids = []
    if cart_item_info:
        shopcart_ids = [k for k, v in cart_item_info.items()]
        service_ids = Shopcart.objects.filter(id__in=shopcart_ids).values_list('service_id', flat=True)
    elif service_item_ids:
        service_ids = ServiceItem.objects.filter(
            id__in=service_item_ids, parent_id=0).values_list('service_id', flat=True)
    elif service_id:
        service_ids = [service_id]
    # 1、拿到可领的券 2、把券塞给用户 3、走下单流程
    from api.manager.coupon_manager import get_coupon_gift_info_for_service_detail_with_type
    from api.view.coupon import get_unclaimed_coupons_dict_for_coupon_gift, get_forge_couponinfo
    # 价格竞争力3期
    # 根据 user service_id 获得美券礼包的信息
    coupon_gifts_info_list = []
    for service_id in service_ids:
        coupon_gifts_info = get_coupon_gift_info_for_service_detail_with_type(service_id, user=user, count=500)
        for coupon_gift_info in coupon_gifts_info:
            coupon_gift_info['service_id'] = service_id
        coupon_gifts_info_list.extend(coupon_gifts_info)
    # 从美券大礼包里拿出 去重后的未领取的美券的信息 以及张数和
    coupons_dict, _ = get_unclaimed_coupons_dict_for_coupon_gift(coupon_gifts_info_list)
    # 把未领取的美券塞进CouponInfo表，伪造CouponInfo表的信息
    now = datetime.datetime.now()
    forge_cps = get_forge_couponinfo(coupons_dict, user, now)

    couponinfos = CouponInfo.valid_coupons(
        user, service_item_id_to_ordering_info, only_show_platform_coupon,
        device_id=device_id, promotion_ids=promotion_ids)

    add_forge_couponinfos = couponinfos + CouponInfo.get_vaild_coupons(
        service_item_id_to_ordering_info, forge_cps, promotion_ids=promotion_ids)

    _is_merchant_user = is_merchant_user(user)
    if _is_merchant_user:
        result.update({
            "is_can_discount": False,  # 是否可用抵扣
        })
        couponinfos = []

    coupon_deduction_info = get_couponinfo_deduction(
        couponinfos, service_item_id_to_ordering_info,
        use_platform_and_doctor_coupon,
        coupon_id, auto_select_best_coupon,
        platform_coupon_id, auto_select_platform,
        doctor_coupon_id, auto_select_doctor
    )

    add_forge_coupon_deduction_info = get_couponinfo_deduction(
        add_forge_couponinfos, service_item_id_to_ordering_info,
        use_platform_and_doctor_coupon,
        coupon_id, auto_select_best_coupon,
        platform_coupon_id, auto_select_platform,
        doctor_coupon_id, auto_select_doctor
    )
    # 先计算摊分
    shared_for_prepay = coupon_deduction_info['shared_for_prepay']
    shared_for_final = coupon_deduction_info['shared_for_final']
    best_shared_for_prepay = coupon_deduction_info['best_shared_for_prepay']
    best_shared_for_final = coupon_deduction_info['best_shared_for_final']

    result['coupon_count'] = len(coupon_deduction_info['sorted_dtos'])
    result['platform_coupon_count'] = len(coupon_deduction_info['platform_dtos'])
    result['doctor_coupon_count'] = len(coupon_deduction_info['doctor_dtos'])
    result['used_coupon_info'] = coupon_deduction_info['used_coupon_info']
    result['used_platform_coupon_info'] = coupon_deduction_info['used_platform_coupon_info']
    result['used_doctor_coupon_info'] = coupon_deduction_info['used_doctor_coupon_info']
    result['is_best_coupon'] = coupon_deduction_info['is_best_coupon']
    result['is_best_platform_coupon'] = coupon_deduction_info['is_best_platform_coupon']
    result['is_best_doctor_coupon'] = coupon_deduction_info['is_best_doctor_coupon']
    result['coupon_pre_payment_deduction'] = coupon_deduction_info['coupon_pre_payment_deduction']
    result['coupon_final_price_deduction'] = coupon_deduction_info['coupon_final_price_deduction']
    result['best_coupon_pre_payment_deduction'] = coupon_deduction_info['best_coupon_pre_payment_deduction']
    result['best_coupon_final_payment_deduction'] = coupon_deduction_info['best_coupon_final_payment_deduction']

    # 初始化券后价
    result.update({
        'not_best_platform_coupon': False,
        'not_best_doctor_coupon': False,
        'get_pre_gift_url': '',
        'get_final_gift_url': '',
    })

    if not use_coupon_grey:
        if coupon_deduction_info['used_platform_coupon_info'].get('value') < add_forge_coupon_deduction_info['used_platform_coupon_info'].get('value') \
                and auto_select_platform\
                and _is_merchant_user == False:
            result['not_best_platform_coupon'] = True
            best_platform_coupon_id = add_forge_coupon_deduction_info['used_platform_coupon_info'].get('coupon_id')
            choice_service_id = coupons_dict.get(best_platform_coupon_id, {}).get('service_id')
            if choice_service_id:
                result['get_pre_gift_url'] = 'gengmei://get_gift?gift_type=1&service_id={}&is_gray=1'.format(choice_service_id)

        if coupon_deduction_info['used_doctor_coupon_info'].get('value') < add_forge_coupon_deduction_info['used_doctor_coupon_info'].get('value') \
                and auto_select_doctor\
                and _is_merchant_user == False:
            result['not_best_doctor_coupon'] = True
            best_doctor_coupon_id = add_forge_coupon_deduction_info['used_doctor_coupon_info'].get('coupon_id')
            choice_service_id = coupons_dict.get(best_doctor_coupon_id, {}).get('service_id')
            if choice_service_id:
                result['get_final_gift_url'] = 'gengmei://get_gift?gift_type=2&service_id={}&is_gray=1'.format(choice_service_id)
    else:
        if coupon_deduction_info['used_platform_coupon_info'].get('value') < add_forge_coupon_deduction_info['used_platform_coupon_info'].get('value') \
                and _is_merchant_user == False:
            result['not_best_platform_coupon'] = True
            best_platform_coupon_id = add_forge_coupon_deduction_info['used_platform_coupon_info'].get('coupon_id')
            choice_service_id = coupons_dict.get(best_platform_coupon_id, {}).get('service_id')
            if choice_service_id:
                result['get_pre_gift_url'] = 'gengmei://get_gift?gift_type=1&service_id={}&is_gray=1'.format(choice_service_id)

        if coupon_deduction_info['used_doctor_coupon_info'].get('value') < add_forge_coupon_deduction_info['used_doctor_coupon_info'].get('value') \
                and _is_merchant_user == False:
            result['not_best_doctor_coupon'] = True
            best_doctor_coupon_id = add_forge_coupon_deduction_info['used_doctor_coupon_info'].get('coupon_id')
            choice_service_id = coupons_dict.get(best_doctor_coupon_id, {}).get('service_id')
            if choice_service_id:
                result['get_final_gift_url'] = 'gengmei://get_gift?gift_type=2&service_id={}&is_gray=1'.format(choice_service_id)

    # 用来表示秒杀
    is_seckill_flag = False

    hids = {v.service_obj.doctor.hospital_id for siid, v in service_item_id_to_ordering_info.items()}
    period_hospital_ids = set(PeriodHospital.get_hospital_ids_in_list(hospital_ids=hids))

    for service_item_id, v in service_item_id_to_ordering_info.items():
        service_obj = v.service_obj
        price_info = v.ordering_price_info
        service_item_ordering_dict = v.service_item_ordering_dict
        serivce_item_number = v.number

        support_insurance = support_insurance or bool(int(service_obj.yinuo_type))

        if serivce_item_number > 1:
            support_insurance = False

        # service_detail = obj.get_service_info(service_item_id=service_item_id)

        is_seckill = price_info and price_info.get('price_type') == SERVICE_ITEM_PRICE_TYPE.SECKILL
        if is_seckill:
            is_seckill_flag = True

        # 如果是拼团则需要有拼团价格和默认价格(划线价)
        is_groupbuy = price_info and price_info.get('price_type') == SERVICE_ITEM_PRICE_TYPE.GROUPBUY
        default_price = price_info['gengmei_price']
        if is_groupbuy:
            sip = ServiceItemPrice.objects.filter(is_enable=True, is_default_price=True).first()
            if sip and sip.gengmei_price > price_info.get('gengmei_price'):
                default_price = sip.gengmei_price

        if shared_for_final:
            coupon_final_deduction = shared_for_final.get(service_item_id)[0] if shared_for_final.get(service_item_id) else 0
        elif best_shared_for_final:
            coupon_final_deduction = best_shared_for_final.get(service_item_id)[0] if best_shared_for_final.get(service_item_id) else 0
        else:
            coupon_final_deduction = 0
        if shared_for_prepay:
            coupon_pre_deduction = shared_for_prepay.get(service_item_id)[0] if shared_for_prepay.get(service_item_id) else 0
        elif best_shared_for_prepay:
            coupon_pre_deduction = best_shared_for_prepay.get(service_item_id)[0] if best_shared_for_prepay.get(service_item_id) else 0
        else:
            coupon_pre_deduction = 0

        service_name = "【{}】{}".format(
            service_item_ordering_dict["project_type_name"], service_obj.short_description
        )

        ordering_sku_info = {
            "service_id": service_obj.id,
            "service_name": service_name,
            "service_image": service_item_ordering_dict["image_url"],
            'order_sku_name': ServiceItem.get_order_show_name(service_item_id),

            "payment_type": service_obj.payment_type,
            "insurance": service_obj.insurance if service_obj.insurance else None,

            "service_item_id": service_item_id,
            "number": serivce_item_number,

            "is_seckill": is_seckill,
            "is_groupbuy": is_groupbuy,

            # 下面的列是service_item里面的冗余，等这一波上线完成之后所以使用者修改代码不再从service_item读取对应的列
            "items_name": service_item_ordering_dict["items_name"],
            "more_buy_count": service_item_ordering_dict["more_buy_count"],
            "gengmei_price": price_info["gengmei_price"],
            "pre_payment_price": price_info['pre_payment_price'],
            "default_price": default_price,
            'coupon_pre_deduction': coupon_pre_deduction,
            'coupon_final_deduction': coupon_final_deduction,

            "service_item": {
                "items_name": service_item_ordering_dict["items_name"],
                "gengmei_price": price_info["gengmei_price"],
                "pre_payment_price": price_info['pre_payment_price'],
            }
        }

        gengmei_price = price_info["gengmei_price"]
        pre_payment_price = price_info["pre_payment_price"]
        final_price = int(gengmei_price) - int(pre_payment_price)

        if service_item_id in shared_for_final:
            sff = shared_for_final.get(service_item_id)
            assert len(sff) == serivce_item_number
            payment_final_price_list = [final_price - p for p in sff]
        else:
            payment_final_price_list = [final_price for i in range(serivce_item_number)]

        fp_lt_zero = sum(payment_final_price_list) > 0
        hospital_id = service_obj.doctor.hospital_id
        result['support_installment'] = fp_lt_zero and service_obj.is_stage and hospital_id in period_hospital_ids

        # 旧版字段，暂时留着避免炸了
        result['is_support_renmai_payment'] = False
        result['renmai_period_price'] = 0
        result['is_support_yirendai_payment'] = False
        result['yirendai_period_price'] = 0

        sum_payment_final_price += final_price * serivce_item_number
        payment_final_price += sum(payment_final_price_list)

        sum_service_prepayment += pre_payment_price * serivce_item_number
        sum_gengmei_price += gengmei_price * serivce_item_number

        result['services'].append(ordering_sku_info)

    result['gengmei_price_total_price'] = sum_gengmei_price
    result['pre_payment_total_price'] = sum_service_prepayment
    result['payment_final_price_total_price'] = sum_payment_final_price

    service_item_id_to_points_deduction = settlement_manager.compute_points_deduction_per_service_item(
        service_item_id_to_ordering_info=service_item_id_to_ordering_info,
        user_total_points_yuan=user.person.points_deduction_yuan,
        service_item_coupon_shared_list_dict=shared_for_prepay
    )

    sum_points_deduction = 0
    for k, v in service_item_id_to_points_deduction.items():
        sum_points_deduction += sum(v)

    result['points_deduction'] = sum_points_deduction

    if len(service_item_id_to_ordering_info) > 1 or is_seckill_flag:
        result['support_installment'] = False

    payment_final_price = int(payment_final_price)  # 去掉末尾的小数部分，确保返回的数据是int类型

    support_installment_info = {}
    if result['support_installment'] is True and payment_final_price >= 1:
        huabei_info = Service.get_huabei_period_info(payment_final_price)
        period = "12"
        if period in huabei_info:
            huabei = {
                "fee": huabei_info[period]["fee"],
                "pay": huabei_info[period]["pay"],
                "period": huabei_info[period]["period"],
            }
            support_installment_info["huabei"] = huabei

    result["support_installment_info"] = support_installment_info

    result['payment_final_price'] = payment_final_price
    result['payment_final_price_renmai_period_price'] = Service.get_renmai_period_price(payment_final_price)

    # 老版本保险信息
    result['support_insurance'] = support_insurance
    result['premium'] = settings.YINUO_PREMIUM
    result['has_bought_insurance'] = Order.objects.filter(user=user, yinuo_order__isnull=False).exists()

    # 新版本所有保险信息全部在insurance里面
    result['insurance'] = get_insurance_info(service_item_id_to_ordering_info)
    return result


def get_couponinfo_deduction(
        couponinfos, service_item_id_to_ordering_info,
        use_platform_and_doctor_coupon=False,
        coupon_id=None, auto_select_best_coupon=False,
        platform_coupon_id=None, auto_select_platform=False,
        doctor_coupon_id=None, auto_select_doctor=False,
):
    """
    :param service_item_id_to_ordering_info: 分摊services
    :param couponinfos: 可用券列表
    :param use_platform_and_doctor_coupon: 是否可同时使用平台券和医生券, 7630及以后是
    :param coupon_id: 7630 以前老版本选择的券id
    :param auto_select_best_coupon: 老版本是否选择最优券
    :param platform_coupon_id: 7630 版本及以后使用的平台券id
    :param auto_select_platform: 是否选择最优平台券
    :param doctor_coupon_id: 7630 版本及以后使用的医生券id
    :param auto_select_doctor: 是否选择最优医生券
    :return:
    """
    from api.view.coupon import to_coupon_info_dto
    from gm_types.gaia import BENEFIT_TYPE
    couponinfo_dict = {x.id: x for x in couponinfos}
    coupon_info_dtos = to_coupon_info_dto(couponinfos)
    items = service_item_id_to_ordering_info.values()
    sum_pre_payment_price = CouponInfo._sum_price(items, 'pre_payment_price')
    sum_gengmei_price = CouponInfo._sum_price(items, 'gengmei_price')
    sum_final_payment_price = sum_gengmei_price - sum_pre_payment_price
    for dto in coupon_info_dtos:
        if dto['benefit_type'] == BENEFIT_TYPE.DISCOUNT:
            dto['value'] = int(sum_pre_payment_price - (sum_pre_payment_price * dto['discount_rate'] // 100))
    sorted_info = CouponInfo.sort_coupons(coupon_info_dtos)

    def get_shared(couponinfo_id):
        return CouponInfo.get_shared(service_item_id_to_ordering_info, couponinfo_dict.get(couponinfo_id))

    def filter_coupons(t):
        return [x for x in sorted_info if x['coupon_type'] == t]

    platform_dtos = filter_coupons(COUPON_TYPES.PLATFORM)
    doctor_dtos = filter_coupons(COUPON_TYPES.DOCTOR)

    def select_couponinfo_dto(_coupon_id, coupon_type, dtos, auto_select):
        dto = {}
        if dtos and auto_select:
            dto = dtos[0]
        _coupon_id = _coupon_id and int(_coupon_id)
        couponinfo = couponinfo_dict.get(_coupon_id) if _coupon_id else None
        if couponinfo:
            if coupon_type and couponinfo.coupon.coupon_type == coupon_type:
                dto = [x for x in dtos if x['id'] == couponinfo.id][0]
            elif coupon_type is None:
                dto = [x for x in dtos if x['id'] == couponinfo.id][0]
        return dto

    platform_dto = select_couponinfo_dto(platform_coupon_id, COUPON_TYPES.PLATFORM, platform_dtos, auto_select_platform)
    doctor_dto = select_couponinfo_dto(doctor_coupon_id, COUPON_TYPES.DOCTOR, doctor_dtos, auto_select_doctor)
    used_coupon_info_dto = select_couponinfo_dto(coupon_id, None, sorted_info, auto_select_best_coupon)

    if use_platform_and_doctor_coupon:
        shared_for_prepay = get_shared(platform_dto.get('id'))
        shared_for_final = get_shared(doctor_dto.get('id'))
        coupon_pre_payment_deduction = platform_dto.get('value', 0)
        coupon_final_price_deduction = doctor_dto.get('value', 0)
    else:
        shared_for_prepay, shared_for_final = {}, {}
        coupon_pre_payment_deduction, coupon_final_price_deduction = 0, 0
        if used_coupon_info_dto:
            if used_coupon_info_dto['coupon_type'] == COUPON_TYPES.PLATFORM:
                shared_for_prepay = get_shared(used_coupon_info_dto.get('id'))
                coupon_pre_payment_deduction = used_coupon_info_dto.get('value', 0)
            else:
                shared_for_final = get_shared(used_coupon_info_dto.get('id'))
                coupon_final_price_deduction = used_coupon_info_dto.get('value', 0)

    def select_best_couponinfo_dto(dtos):
        if not dtos:
            return {}

        _dtos_sorted = sorted(dtos, key=lambda x: x.get('value'), reverse=True)
        return _dtos_sorted[0]

    if not platform_dto:
        best_coupon_pre_obj = select_best_couponinfo_dto(platform_dtos)
        best_coupon_pre_payment_deduction = best_coupon_pre_obj.get('value', 0)
        best_shared_for_prepay = get_shared(best_coupon_pre_obj.get('id'))
    else:
        best_coupon_pre_payment_deduction = 0
        best_shared_for_prepay = {}
    if not doctor_dto:
        best_coupon_final_obj = select_best_couponinfo_dto(doctor_dtos)
        best_coupon_final_payment_deduction = best_coupon_final_obj.get('value', 0)
        best_shared_for_final = get_shared(best_coupon_final_obj.get('id'))
    else:
        best_coupon_final_payment_deduction = 0
        best_shared_for_final = {}

    return {
        'used_coupon_info': used_coupon_info_dto,
        'used_platform_coupon_info': platform_dto,
        'used_doctor_coupon_info': doctor_dto,

        'is_best_coupon': sorted_info and used_coupon_info_dto == sorted_info[0],
        'is_best_platform_coupon': platform_dtos and platform_dto == platform_dtos[0],
        'is_best_doctor_coupon': doctor_dtos and doctor_dto == doctor_dtos[0],

        'shared_for_prepay': shared_for_prepay,
        'shared_for_final': shared_for_final,

        'best_shared_for_prepay': best_shared_for_prepay,
        'best_shared_for_final': best_shared_for_final,

        'coupon_pre_payment_deduction': coupon_pre_payment_deduction,
        'coupon_final_price_deduction': coupon_final_price_deduction,

        'best_coupon_pre_payment_deduction': best_coupon_pre_payment_deduction,
        'best_coupon_final_payment_deduction': best_coupon_final_payment_deduction,

        'sorted_dtos': sorted_info,
        'platform_dtos': platform_dtos,
        'doctor_dtos': doctor_dtos,
    }



def get_insurance_info(service_item_id_to_ordering_info):
    if len(service_item_id_to_ordering_info) != 1:
        return []
    service = service_item_id_to_ordering_info.items()[0][1].service_obj
    if bool(int(service.yinuo_type)):
        return [{
            'type': INSURANCE_TYPE.YINUO,
            'fee': settings.YINUO_PREMIUM,
        }]
    if service.zhongan_type:
        return [{
            'type': INSURANCE_TYPE.ZHONGAN,
            'zhongan_type': service.zhongan_type,
        }]
    return []


@bind_context('api/settlement/detail', login_required=True)
def settlement_detail(ctx, id):
    user = get_user_from_context(ctx)
    points = point.get_point_for(user)
    try:
        settlement = Settlement.objects.get(pk=id, person=user.person)
    except Settlement.DoesNotExist:
        return gen(CODES.SETTLEMENT_DOES_NOT_EXIST)

    if settlement.person.user != user:
        return gen(CODES.NO_PERMISSION)

    orders_data = []
    orders = [item.order for item in settlement.items.all().select_related(
        "order__servicesnapshot").select_related("order__service__doctor")]

    # 黑名单限制
    can_buy = _can_buy(user=user, service_ids=[item.service_id for item in orders])
    if not can_buy:
        return gen(CODES.SETTLEMENT_CREATE_FAIL)

    pre_payment_total_price = 0
    hospital_payment_total_price = 0

    service_item_id_to_order_count = {}

    installment_payment_final_price = 0

    hids = {o.service.doctor.hospital_id for o in orders}
    period_hospital_ids = set(PeriodHospital.get_hospital_ids_in_list(hospital_ids=hids))
    is_contains_groupbuy_order = False

    for order in orders:
        siid = order.service_item_id

        hp = order.hospital_payment
        if type(hp) != unicode:
            # 处理历史问题...
            hospital_payment_total_price += hp

        if siid and siid in service_item_id_to_order_count:
            service_item_id_to_order_count[siid] += 1
        else:
            if siid:
                service_item_id_to_order_count[siid] = 1

            od = order.order_data()
            orders_data.append({
                'service_id': od['service_id'],
                'image': od['image'],
                'order_name': od['order_name'],
                'gengmei_price': od['gengmei_price'],
                'points': od['points'],
                'total_price': od['total_price'],
                'user_name': order.name,
                'items': od['order_multiattribute'],
                'pre_payment_price': od['pre_payment_price'],
                'hospital_payment': od['hospital_payment'],
                'payment': od['payment'],
                'order_id': od['order_id'],
                'status_code': od['status_code'],
                'password': od['password'],
                'order_multiattribute': od['order_multiattribute'],
                'coupon_name': od['coupon_name'],
                'is_seckill': od['is_seckill'],
                'user_phone': od['user_phone'],
                'is_groupbuy': od['groupbuy_status'] != GROUPBUY_STATUS.NOT_GROUPBUY,
                'real_payment': od['real_payment'],
                'service_item_id': siid or None,  # 这个字段暂时没有计划输出
            })

        if type(order.hospital_payment) != unicode and order.hospital_payment > 0 and order.service.is_stage \
                and order.service.doctor.hospital_id in period_hospital_ids:
            installment_payment_final_price += order.hospital_payment

        pre_payment_total_price += order.servicesnapshot.pre_payment_price

    for order in orders_data:
        siid = order.get('service_item_id', None)
        number = 1
        if siid in service_item_id_to_order_count:
            number = service_item_id_to_order_count[siid]
        order['number'] = number
        # order.pop('service_item_id', None)

        # 判断是否包含拼团订单
        if order['is_groupbuy'] is True:
            is_contains_groupbuy_order = True

    user_extra = get_user_extra_by_user_id(user.id)

    order = SettlementItem.objects.filter(settlement=settlement).first().order
    service = order.service

    insurance_info = order.get_insurance_info()
    if insurance_info:
        insurance_price = insurance_info['premium']
    else:
        insurance_price = 0

    insurance_total_price = insurance_price  # 临时代码...之后应该要把一次结算单的所有买了保险的订单都算和

    support_installment = installment_payment_final_price > 0

    now = datetime.datetime.now()

    timeout = settings.SETTLEMENT_HARD_TIMEOUT if settlement.status == SETTLEMENT_STATUS.PAYING else settings.SETTLEMENT_SOFT_TIMEOUT
    pay_end_time = settlement.created_at + datetime.timedelta(seconds=timeout)
    pay_countdown = int((pay_end_time - now).total_seconds()) if pay_end_time > now else 0

    result = {
        'id': settlement.id,
        'status': settlement.status,
        'name': settlement.name,
        'create_time': get_timestamp(settlement.created_at),
        'pay_countdown': pay_countdown,
        'cancel_reason': settlement.cancel_reason,
        'phone': user_extra.phone,
        'point_deduction': settlement.points_deduction,
        'coupon_deduction': settlement.coupon_deduction,
        'address': user_extra.address,
        'orders': orders_data,
        'points': points,
        'is_exchange': True if settlement.service_payment_type == SERVICE_PAYMENT_TYPE.EXCHANGE_GIFT else False,
        'payment': settlement.payment,
        'pre_payment_total_price': pre_payment_total_price,
        'hospital_payment_total_price': hospital_payment_total_price,
        'coupon_name': settlement.get_coupon_name(),
        'is_stage': service.is_stage,
        'support_installment': support_installment,
        'is_support_renmai': False,  # 业务上已经无用字段，兼容留着先
        'is_support_renmai_payment': False,  # 业务上已经无用字段，兼容留着先
        'is_support_yirendai_payment': False,  # 业务上已经无用字段，兼容留着先
        'insurance_total_price': insurance_total_price,
        'payment_final_price': installment_payment_final_price if support_installment else 0,
        'order_coupon_infos': [
            {
                'coupon_id': x.coupon_id,
                'coupon_info_id': x.coupon_info_id,
                'coupon_name': x.coupon_name,
                'coupon_value': x.coupon_value,
                'coupon_type': x.coupon_type,
            } for x in OrderCouponInfo.objects.filter(coupon_info_id__in=settlement.get_coupon_info_ids())
        ],
        'is_contains_groupbuy_order': is_contains_groupbuy_order,
    }

    return result


@bind_context('api/settlement/buy_again', login_required=True)
def settlement_buy_again(ctx, id, buy_left):
    user = get_user_from_context(ctx)

    try:
        settlement = Settlement.objects.get(pk=id, person=user.person)
    except Settlement.DoesNotExist:
        return gen(CODES.SETTLEMENT_DOES_NOT_EXIST)

    if settlement.person.user != user:
        return gen(CODES.NO_PERMISSION)

    service_item_ids = list(SettlementItem.objects.filter(
        settlement_id=settlement.id
    ).values_list('order__service_item_id', flat=True))

    service_item_ids_to_count = {}
    for sku_id in service_item_ids:
        if sku_id not in service_item_ids_to_count:
            service_item_ids_to_count[sku_id] = 0
        service_item_ids_to_count[sku_id] += 1

    to_add_service_item_ids_to_count = {}
    all_service_item_ids = [k for k, v in service_item_ids_to_count.items()]

    service_item_id_to_obj = {si.id: si for si in
                              ServiceItem.objects.select_related('service').filter(id__in=all_service_item_ids)}

    all_service_item_can_sold = True

    for service_item_id, count in service_item_ids_to_count.items():
        service_item = service_item_id_to_obj[service_item_id]

        price_info = service_item.get_current_price_info() or {}
        service_can_sold = service_item.service.is_can_be_sold()
        service_item_has_stock = service_item.sku_stock > 0
        service_item_not_delete = not service_item.is_delete
        service_item_can_sold = bool(
            service_can_sold and service_item_has_stock and price_info and service_item_not_delete
        )

        if service_item_can_sold:
            to_add_service_item_ids_to_count[service_item_id] = count
        else:
            all_service_item_can_sold = False

    add_service_item_to_shopcart = False
    sku_infos = []

    if len(to_add_service_item_ids_to_count) == 0:
        buy_result = SETTLEMENT_BUY_AGAIN_RESULT.ALL_SOLD_OUT
        message = "您选择的美购都卖完了，去看看其他的美购吧~"

    elif all_service_item_can_sold:
        buy_result = SETTLEMENT_BUY_AGAIN_RESULT.SUCCESS
        add_service_item_to_shopcart = True
        message = "商品已加入购物车"

    elif buy_left:
        buy_result = SETTLEMENT_BUY_AGAIN_RESULT.SUCCESS
        add_service_item_to_shopcart = True
        message = "商品已加入购物车"

    else:
        buy_result = SETTLEMENT_BUY_AGAIN_RESULT.OUT_OF_STOCK
        message = "部分美购卖光啦，将下列可购买的美购加入购物车？"

        for service_item_id, count in to_add_service_item_ids_to_count.items():
            service_item = service_item_id_to_obj[service_item_id]

            items = []
            if service_item.service.is_multiattribute:
                items = service_item.items_name

            info = {
                "image": service_item.service.image_header,
                "name": service_item.service.name,
                "items": items,
            }
            sku_infos.append(info)

    buy_sku_ids = []
    if add_service_item_to_shopcart:
        now = datetime.datetime.now()

        with transaction.atomic():
            for service_item_id, count in to_add_service_item_ids_to_count.items():
                service_item = service_item_id_to_obj[service_item_id]
                Shopcart.add_or_create(user.person.id, service_item.service.id, service_item.id, count,
                                       service_item.gengmei_price, now)
                buy_sku_ids.append(service_item_id)

    result = {
        "buy_result": buy_result,
        "buy_sku_ids": buy_sku_ids,
        "sku_infos": sku_infos,
        "message": message,
    }

    return result


@bind_context("api/settlement/pay_jump", login_required=True)
def api_settlement_pay_jump(ctx, settlement_id):
    """
    收到客户端点击支付事件后，设置结算单极其下属的订单
    状态为付款中。(超时取消订单时，如果是付款中，按照策略增加过期时间)
    """
    user = get_user_from_context(ctx)
    person = Person.objects.get(user=user)
    gm_url = gm_protocol.get_order_list()
    result = {"gm_url": gm_url, "is_groupbuy": False}

    with transaction.atomic():
        settlement = Settlement.objects.select_for_update().filter(id=settlement_id).first()
        if not settlement:
            gen(CODES.SETTLEMENT_DOES_NOT_EXIST)

        # 获取支付成功后跳转的更美协议
        orders = settlement.orders.only('groupbuy_status', 'id')
        if len(orders) == 1 and orders[0].groupbuy_status != GROUPBUY_STATUS.NOT_GROUPBUY:
            gm_url = gm_protocol.get_order_detail(orders[0].id)
            # 埋点添加groupbuy_status 参数
            gm_url = gm_url + '&groupbuy_status=2'
            result['gm_url'] = gm_url
            result['is_groupbuy'] = True

        if settlement.person_id != person.id:
            return gen(CODES.SETTLEMENT_DOES_NOT_EXIST)

        if settlement.status == SETTLEMENT_STATUS.PAID:
            return gen(CODES.SETTLEMENT_PAID)
        elif settlement.status == SETTLEMENT_STATUS.CANCEL:
            return gen(CODES.SETTLEMENT_CANCELED)
        elif settlement.status == SETTLEMENT_STATUS.PAYING:
            if settlement.is_installment:
                try:
                    orders = [item.order for item in settlement.items.all()]
                    ctx.gaia_local['pay/installment/refund'](partner=1, order_id=orders[0].id).unwrap()
                except Exception as e:
                    logging_exception()

            return result

        if settlement.status == SETTLEMENT_STATUS.NOT_PAID:
            for item in settlement.items.all():
                order = item.order
                order.operate(
                    order.user.person, ORDER_OPERATION_TYPE.PAYING, ORDER_OPERATION_ROLE.USER)
                order.save()

            settlement.status = SETTLEMENT_STATUS.PAYING
            settlement.save()

    return result


@bind_context('api/renmai/get_product_info')
def api_get_product_info(ctx, settlement_id):
    settlement = Settlement.objects.get(id=settlement_id)
    dict = {
        "product_name": settlement.name,
        "busi_name": settlement.items.first().order.service.doctor.hospital.name,
        "order_id": settlement.items.first().order.id,
        "busi_id": hashlib.md5(settlement.items.first().order.service.doctor.hospital.id).hexdigest(),
        "price": settlement.items.first().order.service_price,
        "amount": settlement.items.first().order.hospital_payment,
    }
    return dict


@bind_context('api/renmai/get_product_info_by_order')
def api_get_product_info(ctx, order_id):
    if not str(order_id).startswith('md'):
        order = Order.objects.get(id=order_id)
        dict = {
            "product_name": order.service.name,
            "busi_name": order.service.doctor.hospital.name,
            "busi_id": hashlib.md5(order.service.doctor.hospital.id).hexdigest(),
            "price": order.service_price,
            "amount": order.hospital_payment,
            "type_name": order.service.project_type.name,
        }
    else:
        maidn_order_id = order_id[3:]
        maidan = MaidanOrder.objects.get(pk=maidn_order_id)
        dict = {
            "product_name": maidan.maidan_name,
            "busi_name": maidan.doctor.hospital.name,
            "busi_id": hashlib.md5(maidan.doctor.hospital.id).hexdigest(),
            "price": maidan.order_data()['gengmei_price'],
            "amount": maidan.order_data()['gengmei_price'],
        }
    return dict
