package com.xxfc.platform.order.biz.inner;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.xxfc.platform.activity.entity.Coupon;
import com.xxfc.platform.activity.feign.ActivityFeign;
import com.xxfc.platform.app.feign.ConfigFeign;
import com.xxfc.platform.order.biz.BaseOrderBiz;
import com.xxfc.platform.order.biz.OrderAccountBiz;
import com.xxfc.platform.order.biz.OrderItemBiz;
import com.xxfc.platform.order.biz.OrderRentVehicleBiz;
import com.xxfc.platform.order.contant.enumerate.DeductionTypeEnum;
import com.xxfc.platform.order.contant.enumerate.ItemTypeEnum;
import com.xxfc.platform.order.contant.enumerate.OrderStatusEnum;
import com.xxfc.platform.order.contant.enumerate.OrderTypeEnum;
import com.xxfc.platform.order.entity.BaseOrder;
import com.xxfc.platform.order.entity.OrderItem;
import com.xxfc.platform.order.entity.OrderRentVehicleDetail;
import com.xxfc.platform.order.pojo.account.OrderAccountDeduction;
import com.xxfc.platform.order.pojo.account.OrderAccountDetail;
import com.xxfc.platform.order.pojo.calculate.InProgressVO;
import com.xxfc.platform.order.pojo.calculate.OrderRefundPriceVO;
import com.xxfc.platform.order.pojo.order.OrderPageVO;
import com.xxfc.platform.order.pojo.order.VehicleItemDTO;
import com.xxfc.platform.order.pojo.price.DelayAddPriceVO;
import com.xxfc.platform.universal.constant.DictionaryKey;
import com.xxfc.platform.universal.entity.Dictionary;
import com.xxfc.platform.universal.feign.ThirdFeign;
import com.xxfc.platform.universal.inter.CalculateInterface;
import com.xxfc.platform.vehicle.entity.Vehicle;
import com.xxfc.platform.vehicle.feign.VehicleFeign;
import com.xxfc.platform.vehicle.pojo.dto.VehicleModelCalendarPriceDTO;
import com.xxfc.platform.vehicle.pojo.dto.order.VMCalendarPriceCostDTO;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.util.Lists;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Map;

import static com.github.wxiaoqi.security.common.constant.CommonConstants.SYS_TRUE;
import static com.xxfc.platform.order.pojo.calculate.InProgressVO.VIOLATE_TYPE_ADVANCE;
import static com.xxfc.platform.order.pojo.calculate.InProgressVO.VIOLATE_TYPE_DELAY;
import static com.xxfc.platform.universal.constant.DictionaryKey.*;

/**
 * 订单退款记录表
 *
 * @author zjw
 * @email nishijjo@qq.com
 * @date 2019-06-18 11:08:09
 */
@Slf4j
@Service
public class OrderCalculateBiz implements CalculateInterface {

    @Autowired
    UserFeign userFeign;

    @Autowired
    ThirdFeign thirdFeign;

    @Autowired
    VehicleFeign vehicleFeign;

    @Autowired
    ConfigFeign configFeign;

    @Autowired
    ActivityFeign activityFeign;

    @Autowired
    OrderItemBiz orderItemBiz;

    @Autowired
    OrderAccountBiz orderAccountBiz;

    @Autowired
    BaseOrderBiz baseOrderBiz;

    @Autowired
    OrderRentVehicleBiz orderRentVehicleBiz;

    @Override
    public ThirdFeign getThirdFeign() {
        return thirdFeign;
    }

    public InProgressVO inProgressCalculate(BaseOrder baseOrder, VehicleItemDTO vehicleItemDTO, OrderRentVehicleDetail orvd, Integer useDays, OrderAccountDetail oad, Boolean isCancel) {
        if(Vehicle.PRICE_TYPE_DAY == orvd.getPriceType()) {
            return inProgressCalculateDay(baseOrder, vehicleItemDTO, orvd, useDays, oad, isCancel);
        }else {
            return inProgressCalculateHour(baseOrder, vehicleItemDTO, orvd, useDays, oad, isCancel);
        }
    }

    public InProgressVO inProgressCalculateDay(BaseOrder baseOrder, VehicleItemDTO vehicleItemDTO, OrderRentVehicleDetail orvd, Integer useDays, OrderAccountDetail oad, Boolean isCancel){
        BigDecimal refundAmount = BigDecimal.ZERO;
        BigDecimal consumeAmount = BigDecimal.ZERO;
        BigDecimal topViolateAmount = BigDecimal.ZERO;
        BigDecimal itemChangeAmount = BigDecimal.ZERO;
        //免费天数
        Integer freeDays = (null == vehicleItemDTO.getCutNum())?0 :vehicleItemDTO.getCutNum();

        //融入日期价格
        List<VMCalendarPriceCostDTO> vmcpds = vehicleItemDTO.getVehicleDetail();
        VehicleItemDTO.ParamDTO paramDTO = vehicleItemDTO.initParam(freeDays, vmcpds);
        List<VMCalendarPriceCostDTO> useAmountList = CollUtil.newArrayList();

        //抵消的天数
        Integer offsetDays = paramDTO.getOffsetNum();

        //商品真实价格
        BigDecimal goodsRealAmount = baseOrder.getGoodsAmount().subtract(baseOrder.getCouponAmount());
        //其他消费金额 = 商品真实价格 - 主要商品真实价格 = （商品价格 - 优惠价格）- 主要商品真实价格
        BigDecimal otherItemRealAmount = goodsRealAmount.subtract(vehicleItemDTO.getRealAmount());

        InProgressVO inProgressVO = new InProgressVO();
        inProgressVO.setUsedDays(useDays);

        //处理不记免赔违约金
        if(useDays > 0) {
            inProgressVO.setDelayAddPriceVO(orvd.obtainDelayAddDetail());
            OrderAccountDeduction violateDeduction = orderAccountBiz.initDeduction(orvd.obtainDelayAddDetail().getDelayDamageSafeAmount(), "", DeductionTypeEnum.OTHER_DELAY_SAFE, OrderAccountDeduction.ORIGIN_DEPOSIT);
            oad.getDeductions().add(violateDeduction);
        }

        inProgressVO.setMetaOrderUsedAmount(vehicleItemDTO.getUsedAmount(useDays));
        //查看是否有增加的延期天数-->添加分别原订单和延期的使用金额
        if(orvd.getDelayAddDays() > 0 && useDays > vehicleItemDTO.getTotalNum()) {
            inProgressVO.setDelayAddUsedAmount(orvd.obtainDelayAddDetail().delayAddUseAmount(useDays - vehicleItemDTO.getTotalNum()));
        }
        inProgressVO.setUsedAmount(inProgressVO.getMetaOrderUsedAmount().add(inProgressVO.getDelayAddUsedAmount()));


        //使用的天数对应的免费天数
        Integer useDaysMapFreeDays = vehicleItemDTO.mapFreeDays(useDays);

        //计算：剩余免费天数
        Integer backFreeDays = freeDays - useDaysMapFreeDays;
        //待返还的优惠券编号
        List<String> backCouponNos = Lists.newArrayList();
        //剩余天数
        Integer realResidueDays = orvd.obtainRealDayNum() - useDays;

        //过了出发时间取消订单 ，优先使用免费天数
        if(backFreeDays <= 0) {

            //设置免费天数
            inProgressVO.setUsedfreeDays(freeDays);
            inProgressVO.setUsedFreeDaysAmount(vehicleItemDTO.getFreeAmount(null));

            //消费天数
            Integer consumeDays = useDays - offsetDays;
            //如果使用天数 大于 总天数
            if(useDays > vehicleItemDTO.getTotalNum()) {
                //消费天数
                consumeDays = vehicleItemDTO.getTotalNum() - offsetDays;
            }

            //需要扣除订单费用
            //判断是否达到优惠券条件 不符合则返还优惠券
            //计算使用天数的费用
            //融入日期价格
            //consumeAmount = orderItem.getUnitPrice().multiply(new BigDecimal(consumeDays+""));
            for(int i = offsetDays; i < (offsetDays+ consumeDays); i++) {
                consumeAmount = consumeAmount.add(vmcpds == null || vmcpds.size() <=0 ? new BigDecimal(0) : vmcpds.get(i).getConsumeAmount());
            }

            if(StrUtil.isNotBlank(baseOrder.getCouponTickerNos())) {
                List<BigDecimal> couponAmounts = Lists.newArrayList();
                for(String tickerNo : baseOrder.getCouponTickerNos().split(",")) {
                    //如果优惠券已经使用了，则会返回 0 元
                    BigDecimal couponAmount = activityFeign.use(baseOrder.getUserId(), Lists.newArrayList(tickerNo), baseOrder.getNo(), Coupon.CHANNEL_RENT, consumeAmount, ActivityFeign.TYPE_CHECK);
                    if(couponAmount.compareTo(BigDecimal.ZERO) > 0) {
                        //能够使用优惠券，则不返还
                        couponAmounts.add(couponAmount);

                        //叠加优惠券金额
                        inProgressVO.setCouponAmount(inProgressVO.getCouponAmount().add(couponAmount));

                        //叠加优惠券描述
                        List<Coupon> ableUsedCoupons = activityFeign.couponsByTickerNoList(CollUtil.newArrayList(tickerNo));
                        if(null != ableUsedCoupons && ableUsedCoupons.size() > 0) {
                            inProgressVO.setCouponDesc(inProgressVO.getCouponDesc()+ ableUsedCoupons.get(0).getTitle());
                        }
                    } else {
                        backCouponNos.add(tickerNo);
                    }
                }

                for(BigDecimal couponAmount : couponAmounts) {
                    consumeAmount = consumeAmount.subtract(couponAmount);
                }
            }

            //设置消费金额，添加租车以外的消费金额
            consumeAmount = consumeAmount.add(otherItemRealAmount);
            inProgressVO.setConsumeAmount(consumeAmount);

            refundAmount = handleConsumeAmount(oad, refundAmount, consumeAmount, goodsRealAmount, inProgressVO);

            //查看是否有增加的延期天数-->添加分别原订单和延期的返回天数
            if(orvd.getDelayAddDays() > 0 && realResidueDays > 0) {
                inProgressVO.setDelayAddBackFreeDays(orvd.obtainDelayAddDetail().residueDelayFreeDays(realResidueDays));
                inProgressVO.setBackFreeDays(inProgressVO.getDelayAddBackFreeDays());
            }

            //查看是否有增加的延期天数-->添加延期的已使用免费天数 和 已使用免费天数金额
            if(orvd.getDelayAddDays() > 0 && useDays > vehicleItemDTO.getTotalNum()) {
                inProgressVO.setUsedfreeDays(inProgressVO.getUsedfreeDays() + (orvd.getDelayAddFreeDays() - inProgressVO.getDelayAddBackFreeDays()));
                inProgressVO.setUsedFreeDaysAmount(inProgressVO.getUsedFreeDaysAmount().add(orvd.obtainDelayAddDetail().delayAddFreeAmount(useDays - vehicleItemDTO.getTotalNum())));
            }

        }else {
            //设置免费天数
            inProgressVO.setUsedfreeDays(useDaysMapFreeDays);
            inProgressVO.setUsedFreeDaysAmount(vehicleItemDTO.getFreeAmount(useDays));

            //融入日期价格
            Integer trueBackFreeDays = backFreeDays;

            inProgressVO.setMetaOrderBackFreeDays(trueBackFreeDays);
            //查看是否有增加的延期天数-->添加分别原订单和延期的返回天数
            if(orvd.getDelayAddDays() > 0) {
                inProgressVO.setDelayAddBackFreeDays(orvd.getDelayAddFreeDays());
            }
            inProgressVO.setBackFreeDays(inProgressVO.getMetaOrderBackFreeDays()+ inProgressVO.getDelayAddBackFreeDays());

            //查看是否有增加的延期天数-->添加延期的已使用免费天数 和 已使用免费天数金额
            //不需要

            //返回优惠券
            if(StrUtil.isNotBlank(baseOrder.getCouponTickerNos())) {
                //没有租车消费金额，所以返回所有优惠券
                backCouponNos.addAll(StrUtil.split(baseOrder.getCouponTickerNos(), ','));
            }

            //设置消费金额，添加租车以外的消费金额
            consumeAmount = consumeAmount.add(otherItemRealAmount);
            inProgressVO.setConsumeAmount(consumeAmount);

            refundAmount = handleConsumeAmount(oad, refundAmount, consumeAmount, goodsRealAmount, inProgressVO);
        }

        inProgressVO.setRefundOrderAmount(refundAmount);
        inProgressVO.setBackCoupons(backCouponNos);


        List<VMCalendarPriceCostDTO> realVmcpds = Convert.toList(VMCalendarPriceCostDTO.class, vmcpds);
        //查看是否有增加的延期天数-->添加延期的费用列表
        if(orvd.getDelayAddDays() > 0) {
            realVmcpds.addAll(orvd.obtainDelayAddDetail().getDelayAmountList());
        }

        //计算违约金
        //residueDays * 身份价格
        if(realResidueDays > 0) { //提前还车

            //设置消耗费用列表
            for(int i = 0; i < useDays; i++) {
                useAmountList.add(BeanUtil.toBean(realVmcpds.get(i), VMCalendarPriceCostDTO.class));
            }

            //"{}元/天 x{}天"
            BigDecimal residueAmount = BigDecimal.ZERO;
            for(int i = useDays; i < realVmcpds.size(); i++) {
                residueAmount = residueAmount.add(realVmcpds.get(i).getPrice());
                inProgressVO.getViolateAmountList().add(realVmcpds.get(i));
            }
            String violateDesc = StrUtil.format("{}元", residueAmount.toString());

            if(realResidueDays >= 2) {
                realResidueDays = 2;
                residueAmount = BigDecimal.ZERO;
                inProgressVO.setViolateAmountList(CollUtil.newArrayList());
                for(int i = useDays; i < (useDays + realResidueDays); i++) {
                    residueAmount = residueAmount.add(realVmcpds.get(i).getPrice());
                    inProgressVO.getViolateAmountList().add(realVmcpds.get(i));
                }
                violateDesc += StrUtil.format("（封顶{}元）", residueAmount);
            }

            inProgressVO.setViolateType(VIOLATE_TYPE_ADVANCE);
            inProgressVO.setViolateAmount(residueAmount);
            inProgressVO.setViolateDesc(" 提前还车违约金："+ violateDesc);
            //提前还车 修改ORIGIN_DEPOSIT 为 ORIGIN_ORDER_DEPOSIT
            OrderAccountDeduction violateDeduction = orderAccountBiz.initDeduction(inProgressVO.getViolateAmount(), violateDesc, DeductionTypeEnum.VIOLATE_ADVANCE, OrderAccountDeduction.ORIGIN_ORDER_DEPOSIT);
            oad.getDeductions().add(violateDeduction);
        }else if (realResidueDays.equals(0)) { //准时还车
            //设置消耗费用列表
            useAmountList.addAll(Convert.toList(VMCalendarPriceCostDTO.class, realVmcpds));
        }else if(realResidueDays < 0 && !isCancel){
            //isCancel 表示是否为取消，取消则不计算延期还车（因为没有出车）
            //设置消耗费用列表
            useAmountList.addAll(Convert.toList(VMCalendarPriceCostDTO.class, realVmcpds));
            //如果订单 出发中 或者 已完成 或者定损中
            if(OrderStatusEnum.ORDER_WAIT.getCode().equals(baseOrder.getStatus()) ||
                    OrderStatusEnum.ORDER_FINISH.getCode().equals(baseOrder.getStatus()) ||
                    OrderStatusEnum.ORDER_FIXED_LOSS.getCode().equals(baseOrder.getStatus())) {
                Integer overDays = 0 - realResidueDays;
                String violateDesc = StrUtil.format(" 延迟{}天", overDays);

                List<VMCalendarPriceCostDTO> overAmountList = orderItemBiz.getOverAmountList(realVmcpds.get(realVmcpds.size() - 1).getDate(), overDays, vehicleItemDTO.getGoodId(), baseOrder.getUserId());
                BigDecimal overAmount = overAmountList.parallelStream()
                        .map(VMCalendarPriceCostDTO::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add).multiply(new BigDecimal(2+ ""));

                //超过的天数 价格 * 200%
                inProgressVO.setViolateType(VIOLATE_TYPE_DELAY);
                inProgressVO.setViolateAmount(overAmount);
                inProgressVO.setViolateDesc(" 延期还车违约金："+ violateDesc);
                inProgressVO.setOverAmountList(overAmountList);
                inProgressVO.setViolateAmountList(overAmountList);

                OrderAccountDeduction violateDeduction = orderAccountBiz.initDeduction(inProgressVO.getViolateAmount(), violateDesc, DeductionTypeEnum.VIOLATE_DELAY, OrderAccountDeduction.ORIGIN_DEPOSIT);
                oad.getDeductions().add(violateDeduction);
            }
        }
        //设置消耗费用列表
        inProgressVO.setUseAmountList(useAmountList);

        return inProgressVO;
    }

    public InProgressVO inProgressCalculateHour(BaseOrder baseOrder, VehicleItemDTO vehicleItemDTO, OrderRentVehicleDetail orvd, Integer useDays, OrderAccountDetail oad, Boolean isCancel){
        BigDecimal refundAmount = BigDecimal.ZERO;
        BigDecimal consumeAmount = BigDecimal.ZERO;
        BigDecimal topViolateAmount = BigDecimal.ZERO;
        BigDecimal itemChangeAmount = BigDecimal.ZERO;
        //免费天数
        Integer freeDays = (null == vehicleItemDTO.getCutNum())?0 :vehicleItemDTO.getCutNum();

        //融入日期价格
        List<VMCalendarPriceCostDTO> vmcpds = vehicleItemDTO.getVehicleDetail();
        VehicleItemDTO.ParamDTO paramDTO = vehicleItemDTO.initParam(freeDays, vmcpds);
        List<VMCalendarPriceCostDTO> useAmountList = CollUtil.newArrayList();

        //抵消的天数
        Integer offsetDays = paramDTO.getOffsetNum();

        //商品真实价格
        BigDecimal goodsRealAmount = baseOrder.getGoodsAmount().subtract(baseOrder.getCouponAmount());
        //其他消费金额 = 商品真实价格 - 主要商品真实价格 = （商品价格 - 优惠价格）- 主要商品真实价格
        BigDecimal otherItemRealAmount = goodsRealAmount.subtract(vehicleItemDTO.getRealAmount());

        InProgressVO inProgressVO = new InProgressVO();
        inProgressVO.setUsedDays(useDays);

        //处理不记免赔违约金
        if(useDays > 0) {
            inProgressVO.setDelayAddPriceVO(orvd.obtainDelayAddDetail());
            OrderAccountDeduction violateDeduction = orderAccountBiz.initDeduction(orvd.obtainDelayAddDetail().getDelayDamageSafeAmount(), "", DeductionTypeEnum.OTHER_DELAY_SAFE, OrderAccountDeduction.ORIGIN_DEPOSIT);
            oad.getDeductions().add(violateDeduction);
        }

        inProgressVO.setMetaOrderUsedAmount(vehicleItemDTO.getUsedAmount(useDays));
        //查看是否有增加的延期天数-->添加分别原订单和延期的使用金额
        if(orvd.getDelayAddDays() > 0 && useDays > vehicleItemDTO.getTotalNum()) {
            inProgressVO.setDelayAddUsedAmount(orvd.obtainDelayAddDetail().delayAddUseAmount(useDays - vehicleItemDTO.getTotalNum()));
        }
        inProgressVO.setUsedAmount(inProgressVO.getMetaOrderUsedAmount().add(inProgressVO.getDelayAddUsedAmount()));


        //使用的天数对应的免费天数
        Integer useDaysMapFreeDays = vehicleItemDTO.mapFreeDays(useDays);

        //计算：剩余免费天数
        Integer backFreeDays = freeDays - useDaysMapFreeDays;
        //待返还的优惠券编号
        List<String> backCouponNos = Lists.newArrayList();
        //剩余天数
        Integer realResidueDays = orvd.obtainRealDayNum() - useDays;

        //过了出发时间取消订单 ，优先使用免费天数
        if(backFreeDays <= 0) {

            //设置免费天数
            inProgressVO.setUsedfreeDays(freeDays);
            inProgressVO.setUsedFreeDaysAmount(vehicleItemDTO.getFreeAmount(null));

            //消费天数
            Integer consumeDays = useDays - offsetDays;
            //如果使用天数 大于 总天数
            if(useDays > vehicleItemDTO.getTotalNum()) {
                //消费天数
                consumeDays = vehicleItemDTO.getTotalNum() - offsetDays;
            }

            //需要扣除订单费用
            //判断是否达到优惠券条件 不符合则返还优惠券
            //计算使用天数的费用
            //融入日期价格
            //consumeAmount = orderItem.getUnitPrice().multiply(new BigDecimal(consumeDays+""));
            for(int i = offsetDays; i < (offsetDays+ consumeDays); i++) {
                consumeAmount = consumeAmount.add(vmcpds == null || vmcpds.size() <=0 ? new BigDecimal(0) : vmcpds.get(i).getConsumeAmount());
            }

            if(StrUtil.isNotBlank(baseOrder.getCouponTickerNos())) {
                List<BigDecimal> couponAmounts = Lists.newArrayList();
                for(String tickerNo : baseOrder.getCouponTickerNos().split(",")) {
                    //如果优惠券已经使用了，则会返回 0 元
                    BigDecimal couponAmount = activityFeign.use(baseOrder.getUserId(), Lists.newArrayList(tickerNo), baseOrder.getNo(), Coupon.CHANNEL_RENT, consumeAmount, ActivityFeign.TYPE_CHECK);
                    if(couponAmount.compareTo(BigDecimal.ZERO) > 0) {
                        //能够使用优惠券，则不返还
                        couponAmounts.add(couponAmount);

                        //叠加优惠券金额
                        inProgressVO.setCouponAmount(inProgressVO.getCouponAmount().add(couponAmount));

                        //叠加优惠券描述
                        List<Coupon> ableUsedCoupons = activityFeign.couponsByTickerNoList(CollUtil.newArrayList(tickerNo));
                        if(null != ableUsedCoupons && ableUsedCoupons.size() > 0) {
                            inProgressVO.setCouponDesc(inProgressVO.getCouponDesc()+ ableUsedCoupons.get(0).getTitle());
                        }
                    } else {
                        backCouponNos.add(tickerNo);
                    }
                }

                for(BigDecimal couponAmount : couponAmounts) {
                    consumeAmount = consumeAmount.subtract(couponAmount);
                }
            }

            //设置消费金额，添加租车以外的消费金额
            consumeAmount = consumeAmount.add(otherItemRealAmount);
            inProgressVO.setConsumeAmount(consumeAmount);

            refundAmount = handleConsumeAmount(oad, refundAmount, consumeAmount, goodsRealAmount, inProgressVO);

            //查看是否有增加的延期天数-->添加分别原订单和延期的返回天数
            if(orvd.getDelayAddDays() > 0 && realResidueDays > 0) {
                inProgressVO.setDelayAddBackFreeDays(orvd.obtainDelayAddDetail().residueDelayFreeDays(realResidueDays));
                inProgressVO.setBackFreeDays(inProgressVO.getDelayAddBackFreeDays());
            }

            //查看是否有增加的延期天数-->添加延期的已使用免费天数 和 已使用免费天数金额
            if(orvd.getDelayAddDays() > 0 && useDays > vehicleItemDTO.getTotalNum()) {
                inProgressVO.setUsedfreeDays(inProgressVO.getUsedfreeDays() + (orvd.getDelayAddFreeDays() - inProgressVO.getDelayAddBackFreeDays()));
                inProgressVO.setUsedFreeDaysAmount(inProgressVO.getUsedFreeDaysAmount().add(orvd.obtainDelayAddDetail().delayAddFreeAmount(useDays - vehicleItemDTO.getTotalNum())));
            }

        }else {
            //设置免费天数
            inProgressVO.setUsedfreeDays(useDaysMapFreeDays);
            inProgressVO.setUsedFreeDaysAmount(vehicleItemDTO.getFreeAmount(useDays));

            //融入日期价格
            Integer trueBackFreeDays = backFreeDays;

            inProgressVO.setMetaOrderBackFreeDays(trueBackFreeDays);
            //查看是否有增加的延期天数-->添加分别原订单和延期的返回天数
            if(orvd.getDelayAddDays() > 0) {
                inProgressVO.setDelayAddBackFreeDays(orvd.getDelayAddFreeDays());
            }
            inProgressVO.setBackFreeDays(inProgressVO.getMetaOrderBackFreeDays()+ inProgressVO.getDelayAddBackFreeDays());

            //查看是否有增加的延期天数-->添加延期的已使用免费天数 和 已使用免费天数金额
            //不需要

            //返回优惠券
            if(StrUtil.isNotBlank(baseOrder.getCouponTickerNos())) {
                //没有租车消费金额，所以返回所有优惠券
                backCouponNos.addAll(StrUtil.split(baseOrder.getCouponTickerNos(), ','));
            }

            //设置消费金额，添加租车以外的消费金额
            consumeAmount = consumeAmount.add(otherItemRealAmount);
            inProgressVO.setConsumeAmount(consumeAmount);

            refundAmount = handleConsumeAmount(oad, refundAmount, consumeAmount, goodsRealAmount, inProgressVO);
        }

        inProgressVO.setRefundOrderAmount(refundAmount);
        inProgressVO.setBackCoupons(backCouponNos);


        List<VMCalendarPriceCostDTO> realVmcpds = Convert.toList(VMCalendarPriceCostDTO.class, vmcpds);
        //查看是否有增加的延期天数-->添加延期的费用列表
        if(orvd.getDelayAddDays() > 0) {
            realVmcpds.addAll(orvd.obtainDelayAddDetail().getDelayAmountList());
        }

        //计算违约金
        //residueDays * 身份价格
        if(realResidueDays > 0) { //提前还车

            //设置消耗费用列表
            for(int i = 0; i < useDays; i++) {
                useAmountList.add(BeanUtil.toBean(realVmcpds.get(i), VMCalendarPriceCostDTO.class));
            }

            //"{}元/天 x{}天"
            BigDecimal residueAmount = BigDecimal.ZERO;
            for(int i = useDays; i < realVmcpds.size(); i++) {
                residueAmount = residueAmount.add(realVmcpds.get(i).getPrice());
                inProgressVO.getViolateAmountList().add(realVmcpds.get(i));
            }
            String violateDesc = StrUtil.format("{}元", residueAmount.toString());

            if(realResidueDays >= 2) {
                realResidueDays = 2;
                residueAmount = BigDecimal.ZERO;
                inProgressVO.setViolateAmountList(CollUtil.newArrayList());
                for(int i = useDays; i < (useDays + realResidueDays); i++) {
                    residueAmount = residueAmount.add(realVmcpds.get(i).getPrice());
                    inProgressVO.getViolateAmountList().add(realVmcpds.get(i));
                }
                violateDesc += StrUtil.format("（封顶{}元）", residueAmount);
            }

            inProgressVO.setViolateType(VIOLATE_TYPE_ADVANCE);
            inProgressVO.setViolateAmount(residueAmount);
            inProgressVO.setViolateDesc(" 提前还车违约金："+ violateDesc);
            //提前还车 修改ORIGIN_DEPOSIT 为 ORIGIN_ORDER_DEPOSIT
            OrderAccountDeduction violateDeduction = orderAccountBiz.initDeduction(inProgressVO.getViolateAmount(), violateDesc, DeductionTypeEnum.VIOLATE_ADVANCE, OrderAccountDeduction.ORIGIN_ORDER_DEPOSIT);
            oad.getDeductions().add(violateDeduction);
        }else if (realResidueDays.equals(0)) { //准时还车
            //设置消耗费用列表
            useAmountList.addAll(Convert.toList(VMCalendarPriceCostDTO.class, realVmcpds));
        }else if(realResidueDays < 0 && !isCancel){
            //isCancel 表示是否为取消，取消则不计算延期还车（因为没有出车）
            //设置消耗费用列表
            useAmountList.addAll(Convert.toList(VMCalendarPriceCostDTO.class, realVmcpds));
            //如果订单 出发中 或者 已完成 或者定损中
            if(OrderStatusEnum.ORDER_WAIT.getCode().equals(baseOrder.getStatus()) ||
                    OrderStatusEnum.ORDER_FINISH.getCode().equals(baseOrder.getStatus()) ||
                    OrderStatusEnum.ORDER_FIXED_LOSS.getCode().equals(baseOrder.getStatus())) {
                Integer overDays = 0 - realResidueDays;
                String violateDesc = StrUtil.format(" 延迟{}天", overDays);

                List<VMCalendarPriceCostDTO> overAmountList = orderItemBiz.getOverAmountList(realVmcpds.get(realVmcpds.size() - 1).getDate(), overDays, vehicleItemDTO.getGoodId(), baseOrder.getUserId());
                BigDecimal overAmount = overAmountList.parallelStream()
                        .map(VMCalendarPriceCostDTO::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add).multiply(new BigDecimal(2+ ""));

                //超过的天数 价格 * 200%
                inProgressVO.setViolateType(VIOLATE_TYPE_DELAY);
                inProgressVO.setViolateAmount(overAmount);
                inProgressVO.setViolateDesc(" 延期还车违约金："+ violateDesc);
                inProgressVO.setOverAmountList(overAmountList);
                inProgressVO.setViolateAmountList(overAmountList);

                OrderAccountDeduction violateDeduction = orderAccountBiz.initDeduction(inProgressVO.getViolateAmount(), violateDesc, DeductionTypeEnum.VIOLATE_DELAY, OrderAccountDeduction.ORIGIN_DEPOSIT);
                oad.getDeductions().add(violateDeduction);
            }
        }
        //设置消耗费用列表
        inProgressVO.setUseAmountList(useAmountList);

        return inProgressVO;
    }

    private BigDecimal handleConsumeAmount(OrderAccountDetail oad, BigDecimal refundAmount, BigDecimal consumeAmount, BigDecimal goodsRealAmount, InProgressVO inProgressVO) {
        if (consumeAmount.compareTo(BigDecimal.ZERO) > 0) {
            //消费金额 大于0 增加额外费用
            OrderAccountDeduction consumeDeduction = orderAccountBiz.initDeduction(consumeAmount, "消费金额", DeductionTypeEnum.CONSUME, OrderAccountDeduction.ORIGIN_ORDER_DEPOSIT);
            oad.getDeductions().add(consumeDeduction);
        }

        //消费金额 小于商品真实的金额 返回钱
        if (consumeAmount.compareTo(goodsRealAmount) <= 0) {
            //设置返回钱
            refundAmount = refundAmount.add(goodsRealAmount.subtract(consumeAmount));
        } else {
            //设置额外扣减（押金里面扣）
            inProgressVO.setExtraAmount(consumeAmount.subtract(goodsRealAmount));
        }
        return refundAmount;
    }

    public InProgressVO inProgressCalculate(BaseOrder baseOrder, Integer useDays) {
        VehicleItemDTO vehicleItemDTO = BeanUtil.toBean(orderItemBiz.selectOne(new OrderItem(){{
            setType(ItemTypeEnum.VEHICLE_MODEL.getCode());
            setOrderId(baseOrder.getId());
        }}), VehicleItemDTO.class);

        OrderRentVehicleDetail orvd = orderRentVehicleBiz.selectOne(new OrderRentVehicleDetail() {{
            setOrderId(baseOrder.getId());
        }});
        return inProgressCalculate(baseOrder, vehicleItemDTO, orvd, useDays, new OrderAccountDetail(), Boolean.FALSE);
    }

    public InProgressVO calculateOrderComplete(BaseOrder baseOrder, OrderRentVehicleDetail orvd, OrderAccountDetail oad, VehicleItemDTO vehicleItemDTO, Integer useDays, Boolean isCancel) {
        InProgressVO inProgressVO = inProgressCalculate(baseOrder, vehicleItemDTO, orvd, useDays, oad, isCancel);

        //后修改为提前还车 订单款扣，延期还车 押金扣
        if(InProgressVO.VIOLATE_TYPE_ADVANCE == inProgressVO.getViolateType()) {
            //定金 - （额外消费金额）
            oad.setDepositAmount(orvd.getDeposit().subtract(inProgressVO.getExtraAmount()));
            //订单款 - 违约金
            oad.setOrderAmount(inProgressVO.getRefundOrderAmount().subtract(inProgressVO.getViolateAmount()));
        }else {
            //定金 - （额外消费金额 + 违约金）
            oad.setDepositAmount(orvd.getDeposit().subtract(inProgressVO.getExtraAmount().add(inProgressVO.getViolateAmount())));
            oad.setOrderAmount(inProgressVO.getRefundOrderAmount());
        }
        oad.setOriginDepositAmount(orvd.getDeposit());
        oad.setOriginOrderAmount(baseOrder.getGoodsAmount().subtract(baseOrder.getCouponAmount()));
        return inProgressVO;
    }

//    /**
//     * 计算包含多少天
//     * @param startLong
//     * @param endLong
//     * @return
//     */
//    public Integer getIncludeDays(Long startLong, Long endLong) {
//        Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
//        Long hourLong = (60L * 60L * 1000L);
//        Long dayLong = hourLong * 24;
//        Long bufferLong = Long.valueOf(dictionaryMap.get(APP_ORDER+ "_"+ DictionaryKey.RENT_TIME_BUFFER).getDetail()) * hourLong;
//
//        //计算：使用天数 当前时间 - 开始时间的0时0分0秒
//        Long bookTimeLag = endLong - startLong;
//
//        //默认 已使用了一天
//        if(bookTimeLag <= 0) {
//            return 1;
//        }
//
//        log.info("bookTimeLag {}", new BigDecimal(bookTimeLag + ""));
//        log.info("divide {}", new BigDecimal((24 * 60 * 60 * 1000)+ ""));
//        Integer bookDays = new BigDecimal(bookTimeLag + "").divide(new BigDecimal(dayLong+ ""), 0, RoundingMode.DOWN).intValue();
//        Long excess = bookTimeLag%dayLong;
//        if(excess > bufferLong) {
//            bookDays += 1;
//        }
//
//        if(0 == bookDays) {
//            bookDays = 1;
//        }
//        return bookDays;
//    }
//
//    /**
//     * 计算包含多少小时
//     * @param startLong
//     * @param endLong
//     * @return
//     */
//    public Integer getIncludeHours(Long startLong, Long endLong) {
//        Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
//        Long hourLong = (60L * 60L * 1000L);
//        //Long dayLong = hourLong * 24;
//        Long bufferLong = 0L;
//
//        //计算：使用天数 当前时间 - 开始时间的0时0分0秒
//        Long bookTimeLag = endLong - startLong;
//
//        //默认 已使用了1小时
//        if(bookTimeLag <= 0) {
//            return 1;
//        }
//
//        log.info("bookTimeLag {}", new BigDecimal(bookTimeLag + ""));
//        log.info("divide {}", new BigDecimal(hourLong+ ""));
//        Integer hourDays = new BigDecimal(bookTimeLag + "").divide(new BigDecimal(hourLong+ ""), 0, RoundingMode.DOWN).intValue();
//        Long excess = bookTimeLag%hourLong;
//        if(excess > bufferLong) {
//            hourDays += 1;
//        }
//
//        if(0 == hourDays) {
//            hourDays = 1;
//        }
//        return hourDays;
//    }

    public OrderRefundPriceVO getPriceCalculate (String no, OrderPageVO orderPageVO) {
        //根据no 查订单
        OrderTypeEnum orderTypeEnum = OrderTypeEnum.get(orderPageVO.getType());

        BigDecimal totalRefundAmount = BigDecimal.ZERO;
        BigDecimal totalDeductAmount = BigDecimal.ZERO;
        StringBuilder refundDescBuilder = new StringBuilder("");
        String refundDesc = "";
        InProgressVO inProgressVO = new InProgressVO();
        BigDecimal topAmount = BigDecimal.ZERO;

        if(SYS_TRUE.equals(orderPageVO.getHasPay()) && null != orderPageVO.getPayTime()) {
            //缓冲取消时间(一个小时)
            Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
            Long autoCancelTime = Long.valueOf(dictionaryMap.get(APP_ORDER+ "_"+ DictionaryKey.CANCEL_TIME_BUFFER).getDetail());
            autoCancelTime = autoCancelTime * 60L * 1000L;

            //判断是否支付超过缓冲取消时间(一个小时)
            Long orderCrtLag = System.currentTimeMillis() - orderPageVO.getPayTime();
            Boolean overCancelBuffer = (orderCrtLag > autoCancelTime)? Boolean.TRUE: Boolean.FALSE;

            switch (orderTypeEnum) {
                case RENT_VEHICLE:

                    Long timeLag = orderPageVO.getOrderRentVehicleDetail().getStartTime() - System.currentTimeMillis();

                    VehicleItemDTO vehicleItemDTO = BeanUtil.toBean(orderItemBiz.selectOne(new OrderItem(){{
                        setType(ItemTypeEnum.VEHICLE_MODEL.getCode());
                        setOrderId(orderPageVO.getId());
                    }}), VehicleItemDTO.class);

                    //融入日期价格
                    List<VehicleModelCalendarPriceDTO> vmcpds = JSONUtil.toList(JSONUtil.parseArray(vehicleItemDTO.getDetail()), VehicleModelCalendarPriceDTO.class);

                    if(timeLag < 0 && overCancelBuffer) {
                        Integer useDays = getIncludeDays(orderPageVO.getOrderRentVehicleDetail().getStartTime(), DateTime.now().getMillis());
                        OrderAccountDetail oad = new OrderAccountDetail();
                        inProgressVO = inProgressCalculate(orderPageVO, vehicleItemDTO, orderPageVO.getOrderRentVehicleDetail(), useDays, oad, Boolean.FALSE);

                        topAmount = vehicleItemDTO.getTopAmount(useDays);
                        totalDeductAmount = oad.realTotalDeduct();
                        totalRefundAmount = oad.getOrderAmount().add(oad.getDepositAmount());
                        refundDesc = inProgressVO.getViolateDesc();
                    }else {

                        String key = RENT_REFUND;

                        BigDecimal deductionAmount = orderAccountBiz.calculateDeduction(vehicleItemDTO.getBuyAmount()
                                , orderPageVO.getOrderRentVehicleDetail().getStartTime() - System.currentTimeMillis()
                                , DictionaryKey.APP_ORDER+ "_"+ key
                                , refundDescBuilder);

                        if(!overCancelBuffer) {
                            deductionAmount = BigDecimal.ZERO;
                            refundDescBuilder = new StringBuilder("");
                        }

                        topAmount = vehicleItemDTO.getTopAmount(0);
                        totalDeductAmount = (topAmount.compareTo(deductionAmount) < 0) ? topAmount: deductionAmount;
                        totalRefundAmount = orderPageVO.getRealAmount().subtract(totalDeductAmount);
                        refundDesc = refundDescBuilder.toString();
                    }

                    break;
                case TOUR:
                    //判断是省内还是省外
                    String key = TOUR_IN_REFUND;
                    if(SYS_TRUE.equals(orderPageVO.getOrderTourDetail().getIsOutside())) {
                        key = TOUR_REFUND;
                    }

                    OrderItem adultItem = orderItemBiz.selectOne(new OrderItem(){{
                        setType(ItemTypeEnum.TOUR_ADULT.getCode());
                        setOrderId(orderPageVO.getId());
                    }});

                    OrderItem childItem = orderItemBiz.selectOne(new OrderItem(){{
                        setType(ItemTypeEnum.TOUR_CHILD.getCode());
                        setOrderId(orderPageVO.getId());
                    }});

                    BigDecimal adultItemAmount = (null == adultItem)? BigDecimal.ZERO: adultItem.getRealAmount();
                    BigDecimal childItemAmount = (null == childItem)? BigDecimal.ZERO: childItem.getRealAmount();

                    BigDecimal deductionAmount = orderAccountBiz.calculateDeduction(adultItemAmount.add(childItemAmount)
                            , orderPageVO.getOrderTourDetail().getStartTime() - System.currentTimeMillis()
                            , DictionaryKey.APP_ORDER+ "_"+ key
                            , refundDescBuilder);

                    if(!overCancelBuffer) {
                        deductionAmount = BigDecimal.ZERO;
                        refundDescBuilder = new StringBuilder("");
                    }

                    topAmount = adultItemAmount.add(childItemAmount);
                    totalDeductAmount = deductionAmount;
                    totalRefundAmount = orderPageVO.getRealAmount().subtract(deductionAmount);
                    refundDesc = refundDescBuilder.toString();
                    break;
                default:
                    break;
            }
        }


        OrderRefundPriceVO orpv = new OrderRefundPriceVO();
        orpv.setRealAmount(orderPageVO.getRealAmount());
        orpv.setRefundAmount(totalRefundAmount);
        orpv.setCutAmount(totalDeductAmount);
        orpv.setTopAmount(topAmount);
        return orpv;
    }

    public static void main(String[] args) {
        System.out.println(DateTime.now().getMillis());
    }
}