package com.xxfc.platform.order.biz;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.github.wxiaoqi.security.common.exception.BaseException;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.github.wxiaoqi.security.common.util.IntervalUtil;
import com.github.wxiaoqi.security.common.util.process.ResultCode;
import com.xxfc.platform.activity.feign.ActivityFeign;
import com.xxfc.platform.order.biz.inner.OrderCalculateBiz;
import com.xxfc.platform.order.biz.inner.OrderMsgBiz;
import com.xxfc.platform.order.contant.enumerate.*;
import com.xxfc.platform.order.entity.*;
import com.xxfc.platform.order.mapper.OrderAccountMapper;
import com.xxfc.platform.order.pojo.DedDetailDTO;
import com.xxfc.platform.order.pojo.QueryCriteria;
import com.xxfc.platform.order.pojo.ReturnOrderAmount;
import com.xxfc.platform.order.pojo.Term;
import com.xxfc.platform.order.pojo.account.OrderAccountBo;
import com.xxfc.platform.order.pojo.account.OrderAccountDTO;
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.mq.OrderMQDTO;
import com.xxfc.platform.order.pojo.order.VehicleItemDTO;
import com.xxfc.platform.order.pojo.price.CancelStartedVO;
import com.xxfc.platform.order.pojo.price.CostDetailExtend;
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.vo.OrderRefundVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.jexl2.MapContext;
import org.mockito.internal.util.collections.Sets;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;

import static com.github.wxiaoqi.security.common.constant.CommonConstants.SYS_FALSE;
import static com.github.wxiaoqi.security.common.constant.CommonConstants.SYS_TRUE;
import static com.xxfc.platform.order.contant.enumerate.AccountTypeEnum.OUT_RESIDUE_ORDER;
import static com.xxfc.platform.order.pojo.account.OrderAccountDeduction.ORIGIN_DEPOSIT;
import static com.xxfc.platform.order.pojo.account.OrderAccountDeduction.ORIGIN_ORDER_DEPOSIT;
import static com.xxfc.platform.order.pojo.pay.NotifyUrlDTO.PAY_WAY_ALI;
import static com.xxfc.platform.universal.constant.DictionaryKey.APP_ORDER;
import static com.xxfc.platform.universal.constant.DictionaryKey.RENT_PERSON_INSURANCE;

/**
 * 订单帐目
 *
 * @author zhoujw
 * @email 18178966185@163.com
 * @date 2019-09-09 15:51:16
 */
@Service
@Slf4j
public class OrderAccountBiz extends BaseBiz<OrderAccountMapper,OrderAccount> {

    @Autowired
    BaseOrderBiz baseOrderBiz;

    @Autowired
    OrderRentVehicleBiz orderRentVehicleBiz;

    @Autowired
    OrderVehicleCrosstownBiz crosstownBiz;

    @Autowired
    OrderMsgBiz orderMsgBiz;

    @Autowired
    OrderItemBiz orderItemBiz;

    @Autowired
    OrderDepositRefundRecordBiz orderDepositRefundRecordBiz;

    @Autowired
    OrderCalculateBiz orderCalculateBiz;

    @Autowired
    OrderViolationBiz orderViolationBiz;

    @Autowired
    ThirdFeign thirdFeign;

    @Autowired
    UserFeign userFeign;

    @Autowired
    ActivityFeign activityFeign;


    /**
     * 租车退款流程
     * @param baseOrder
     * @param orderDeductSource 订单款扣除费用源，主要商品（租车费用、旅游费用等）
     * @param timeLag 与开始时间的时间差
     * @param dicParentKey 计算违约金的dickey
     * @param depositAmount 押金款
     * @param depositDeductSource 押金款扣除费用源
     * @param oad 账单信息
     * @param topAmount 扣款封顶值
     * @param orderViolateCoverAmount 订单违约金覆盖值
     * @param depositViolateCoverAmount 押金违约金覆盖值
     */
    public BigDecimal rentRefundProcessCancel(BaseOrder baseOrder, BigDecimal orderDeductSource, Long timeLag
            , String dicParentKey, BigDecimal depositAmount, BigDecimal depositDeductSource
            , OrderAccountDetail oad, BigDecimal topAmount, BigDecimal orderViolateCoverAmount, BigDecimal depositViolateCoverAmount
            , Boolean noDeduct) {
        //原来退款 和 最终退款
        BigDecimal originalRefundAmount = BigDecimal.ZERO;
        BigDecimal refundAmount = BigDecimal.ZERO;

        StringBuilder orderRefundDescBuilder = new StringBuilder("");
        StringBuilder depositRefundDescBuilder = new StringBuilder("");
        BigDecimal totalDeductAmount = BigDecimal.ZERO;
        if(null == oad) {
            oad = new OrderAccountDetail();
        }
        // 订单款 原订单退款、最终订单退款
        BigDecimal originalOrderRefundAmount = BigDecimal.ZERO.add(baseOrder.getGoodsAmount().subtract(baseOrder.getCouponAmount()));
        BigDecimal orderRefundAmount = BigDecimal.ZERO.add(baseOrder.getGoodsAmount().subtract(baseOrder.getCouponAmount()));

        if(null != orderDeductSource && BigDecimal.ZERO.compareTo(orderDeductSource) < 0) {
            BigDecimal orderDeductAmount = calculateDeduction(orderDeductSource, timeLag, dicParentKey, orderRefundDescBuilder);
            //如果属于免扣费情况
            if(noDeduct) {
                orderDeductAmount = BigDecimal.ZERO;
                orderRefundDescBuilder = new StringBuilder("");
            }
            orderDeductAmount = orderDeductAmount.setScale(2, RoundingMode.HALF_UP);
            if(null != orderViolateCoverAmount) {
                orderDeductAmount = orderViolateCoverAmount;
            }
            if(topAmount.compareTo(orderDeductAmount) > 0) {
                totalDeductAmount = totalDeductAmount.add(orderDeductAmount);
                topAmount = topAmount.subtract(orderDeductAmount);
            }else {
                totalDeductAmount = totalDeductAmount.add(topAmount);
                orderDeductAmount = topAmount;
                topAmount = BigDecimal.ZERO;
            }

            //订单退款
            orderRefundAmount = orderRefundAmount.subtract(orderDeductAmount);
            if(orderRefundAmount.compareTo(BigDecimal.ZERO) < 0) {
                orderRefundAmount = BigDecimal.ZERO;
            }
        }

        //设置金额
        oad.setOriginOrderAmount(originalOrderRefundAmount);
        oad.setOrderAmount(orderRefundAmount);
        originalRefundAmount = originalRefundAmount.add(oad.getOriginOrderAmount());
        refundAmount = refundAmount.add(oad.getOrderAmount());

        // 押金 原押金退款、最终押金退款
        BigDecimal originalDepositRefundAmount = BigDecimal.ZERO.add(depositAmount);
        BigDecimal depositRefundAmount = BigDecimal.ZERO.add(depositAmount);

        if(null != depositDeductSource && BigDecimal.ZERO.compareTo(depositDeductSource) < 0) {
            //通过原扣除款 计算剩余款
            BigDecimal depositDeductAmount = calculateDeduction(depositDeductSource, timeLag, dicParentKey, depositRefundDescBuilder);
            //如果属于免扣费情况
            if(noDeduct) {
                depositDeductAmount = BigDecimal.ZERO;
                depositRefundDescBuilder = new StringBuilder("");
            }
            depositDeductAmount = depositDeductAmount.setScale(2, RoundingMode.HALF_UP);
            if(null != orderViolateCoverAmount) {
                depositDeductAmount = orderViolateCoverAmount;
            }
            if (null != depositViolateCoverAmount) {
                depositDeductAmount = depositViolateCoverAmount;
            }
            if(topAmount.compareTo(depositDeductAmount) > 0) {
                totalDeductAmount = totalDeductAmount.add(depositDeductAmount);
                topAmount = topAmount.subtract(depositDeductAmount);
            }else {
                totalDeductAmount = totalDeductAmount.add(topAmount);
                depositDeductAmount = topAmount;
                topAmount = BigDecimal.ZERO;
            }

            //返回押金
            depositRefundAmount = originalDepositRefundAmount.subtract(depositDeductAmount);
        }

        //设置违章款账单
        StringBuilder stringBuilder = new StringBuilder("");
        if(totalDeductAmount.compareTo(BigDecimal.ZERO) > 0) {
            int originType = 0;
            if(orderRefundDescBuilder.length() > 0) {
                stringBuilder = orderRefundDescBuilder;
                originType = OrderAccountDeduction.ORIGIN_ORDER;
            }else {
                stringBuilder = depositRefundDescBuilder;
                originType = ORIGIN_DEPOSIT;
            }
            oad.getDeductions().add(initDeduction(totalDeductAmount, stringBuilder.toString(), DeductionTypeEnum.VIOLATE_CANCEL, originType));
        }

        //设置订单押金金额
        oad.setOriginDepositAmount(originalDepositRefundAmount);
        oad.setDepositAmount(depositRefundAmount);
        originalRefundAmount = originalRefundAmount.add(originalDepositRefundAmount);
        refundAmount = refundAmount.add(depositRefundAmount);

        //退款子流程: 订单基础，退款描述, 款金额
        refundSubProcess(baseOrder, stringBuilder.toString(), originalRefundAmount, refundAmount, AccountTypeEnum.OUT_ORDER_FUND.getCode(), RefundStatusEnum.ALL.getCode(), oad);
        return refundAmount;
    }


    /**
     * 调车发布 退款流程
     * @param baseOrder 基础订单信息
     * @param refundDesc 退款描述
     * @param originalRefundAmount 原退款金额
     * @param deductAmount 扣除金额
     */
    public void publishRefundProcess(BaseOrder baseOrder, String refundDesc, BigDecimal originalRefundAmount, BigDecimal deductAmount) {
        BigDecimal refundAmount = BigDecimal.ZERO.add(originalRefundAmount);

        if(null != deductAmount) {
            refundAmount = originalRefundAmount.subtract(deductAmount);
        }

        BigDecimal finalRefundAmount = refundAmount;
        OrderAccountDetail oad = new OrderAccountDetail(){{
           setOriginOrderAmount(originalRefundAmount);
           setOrderAmount(finalRefundAmount);
        }};
        OrderAccountDeduction violateDeduction = initDeduction(deductAmount, refundDesc, DeductionTypeEnum.OTHER_PUBLISH_REJECTED, OrderAccountDeduction.ORIGIN_ORDER);
        oad.getDeductions().add(violateDeduction);
        refundSubProcess(baseOrder, refundDesc, originalRefundAmount, refundAmount, OUT_RESIDUE_ORDER.getCode(), RefundStatusEnum.ALL.getCode(), oad);
    }

    /**
     * 退款子流程
     * @param baseOrder 基础订单信息
     * @param refundDesc 退款描述
     * @param originalRefundAmount 原退款金额
     * @param refundAmount 退款金额
     * @param refundType 退款类型
     * @param refundStatus 退款状态
     * @param oad 账单信息
     */
    public void refundSubProcess(BaseOrder baseOrder, String refundDesc, BigDecimal originalRefundAmount, BigDecimal refundAmount, Integer refundType, Integer refundStatus, OrderAccountDetail oad) {
        String refundTradeNo = null;
        //退款金额 = 0 不退款，把refundTradeNo设置为 -1
        if(BigDecimal.ZERO.compareTo(refundAmount) == 0) {
            refundTradeNo = "-1";
        }else if(BigDecimal.ZERO.compareTo(refundAmount) < 0) {
            OrderRefundVo orv = new OrderRefundVo();
            orv.setAmount(baseOrder.getRealAmount().multiply(new BigDecimal("100")).intValue());
            orv.setOrderNo(baseOrder.getNo());
            orv.setRefundDesc(refundDesc+ refundAmount.toString());
            orv.setRefundAmount(refundAmount.multiply(new BigDecimal("100")).intValue());

            // originalRefundAmount - refundAmount
            Integer freeze2PayAmount = originalRefundAmount.subtract(refundAmount).multiply(new BigDecimal("100")).intValue();
            if(AccountTypeEnum.OUT_PART_DEPOSIT.getCode().equals(refundType)) {

                // freeze2PayAmount - 违章保证金
                for(OrderAccountDeduction deduction : oad.getDeductions()) {
                    if(DeductionTypeEnum.VIOLATE_TRAFFIC_KEEP.getCode().equals(deduction.getType())) {
                        freeze2PayAmount -= deduction.getAmount().multiply(new BigDecimal("100")).intValue();
                        break;
                    }
                }
            }

            orv.setFreeze2PayAmount(freeze2PayAmount);
            orv.setFreeze2PayDesc("冻结转支付订单号: "+ baseOrder.getNo());

            ObjectRestResponse<String> result = thirdFeign.refund(orv);
            refundTradeNo = result.getData();
            if(null == refundTradeNo) {
                log.error("退款没有refundTradeNo，订单号为："+ baseOrder.getNo()+ ", 微服务调用结果为"+ JSONUtil.toJsonStr(result));
            }
        }

        //记录订单退款记录
        Integer flag = addOrderAccount(baseOrder.getId(), refundDesc, originalRefundAmount, refundAmount, refundTradeNo, refundType, JSONUtil.toJsonStr(oad));

        //更新订单的退款状态和退款时间
        BaseOrder updateBaseOrder = new BaseOrder();
        updateBaseOrder.setId(baseOrder.getId());
        updateBaseOrder.setRefundStatus(refundStatus);
        updateBaseOrder.setRefundTime(System.currentTimeMillis());
        updateBaseOrder.setVersion(baseOrder.getVersion());
        if(SYS_TRUE.equals(flag) && null != refundStatus) {
            BeanUtil.copyProperties(baseOrderBiz.updateSelectiveByIdReT(updateBaseOrder, Boolean.TRUE), baseOrder);
        }
    }

    public BigDecimal calculateDeduction(BigDecimal goodsAmount, Long timeLag, String dicParentKey, StringBuilder refundDescBuilder) {
        BigDecimal refundGoodsAmount = goodsAmount;
        BigDecimal deductGoodsAmount = BigDecimal.ZERO;

        //根据时间处理goodsAmount
        //获取出发时间 到现在 距离时间
        Integer hourLag = Long.valueOf(timeLag/(1000L * 60L * 60L)).intValue();

        Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
        Set<Dictionary> rentRefunds = dictionaryMap.get(dicParentKey).getChildrens();

        for(com.xxfc.platform.universal.entity.Dictionary dic : rentRefunds) {
            if(StrUtil.isBlank(dic.getName())) {
                continue;
            }
            String[] names = dic.getName().split("\\|");
            if(names.length < 2) {
                continue;
            }

            //符合范围
            if(IntervalUtil.staticIsInTheInterval(hourLag.toString(), names[0])){
                refundGoodsAmount = new BigDecimal((IntervalUtil.evaluate(dic.getDetail(), new MapContext(){{
                    //ga : goodsAmount
                    set("ga", goodsAmount);
                }})+ "").toString());
                refundGoodsAmount = refundGoodsAmount.setScale(2, RoundingMode.HALF_UP);
                refundDescBuilder = refundDescBuilder.insert(0, names[1]);
                if(goodsAmount.subtract(refundGoodsAmount).compareTo(BigDecimal.ZERO) > 0) {
                    deductGoodsAmount = goodsAmount.subtract(refundGoodsAmount);
                    //orderAccountDetail.getDeductions().add(initDeduction(goodsAmount.subtract(refundGoodsAmount), names[1], DeductionTypeEnum.VIOLATE_CANCEL, OrderAccountDeduction.ORIGIN_DEPOSIT));
                }
                break;
            }
        }

        return deductGoodsAmount;
    }

    public BigDecimal calculatePersonInsurance(Long timeLag) {
        BigDecimal amount = BigDecimal.ZERO;

        //获取天
        Integer remainder = Long.valueOf(timeLag%(1000L * 60L * 60L * 24L)).intValue();
        Integer dayLag = Long.valueOf(timeLag/(1000L * 60L * 60L * 24L)).intValue() + remainder > 0? 1: 0;

        Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
        Set<Dictionary> personInsurances = dictionaryMap.get(RENT_PERSON_INSURANCE).getChildrens();

        for(com.xxfc.platform.universal.entity.Dictionary dic : personInsurances) {
            if(StrUtil.isBlank(dic.getName())) {
                continue;
            }

            //符合范围
            if(IntervalUtil.staticIsInTheInterval(dayLag.toString(), dic.getName())){
                amount = new BigDecimal(dic.getDetail());
                break;
            }
        }

        return amount;
    }

    /**
     * 初始化deduction
     * @param subtract
     * @param name
     * @return
     */
    public OrderAccountDeduction initDeduction(BigDecimal subtract, String name, DeductionTypeEnum dte, Integer origin) {
        OrderAccountDeduction orderAccountDeduction = new OrderAccountDeduction();
        orderAccountDeduction.setType(dte.getCode());
        orderAccountDeduction.setName(name);
        orderAccountDeduction.setAmount(subtract);
        orderAccountDeduction.setOrigin(origin);
        return orderAccountDeduction;
    }

    /**
     * 记录订单退款记录
     * @param orderId
     * @param accountDesc
     * @param accountAmount
     * @param tradeNo
     */
    public Integer addOrderAccount(Integer orderId, String accountDesc, BigDecimal originalAmount, BigDecimal accountAmount, String tradeNo, Integer accountType, String detail) {
        //如果返回的流水为空，则当做失败
        Integer refundStatus = SYS_TRUE;
        if(StrUtil.isBlank(tradeNo)) {
            refundStatus = SYS_FALSE;
        }

        //创建订单退款记录
        OrderAccount orderAccount = new OrderAccount(){{
            setOrderId(orderId);
            setAccountTime(System.currentTimeMillis());
            setTradeNo(tradeNo);
            setAccountType(RefundTypeEnum.ORDER_FUND.getCode());
        }};
        orderAccount.setOriginalAmount(originalAmount);
        orderAccount.setDeductAmount(originalAmount.subtract(accountAmount));
        orderAccount.setAccountAmount(accountAmount);
        orderAccount.setAccountDesc(accountDesc);
        orderAccount.setAccountStatus(refundStatus);
        orderAccount.setAccountType(accountType);
        orderAccount.setAccountDetail(detail);
        insertSelective(orderAccount);

        return refundStatus;
    }


    public void refundTrigger(BaseOrder baseOrder, OrderRentVehicleDetail orvd, BigDecimal residueAmount, BigDecimal originalRefundAmount, BigDecimal refundAmont, String refundDesc, Integer refundStatus, AccountTypeEnum accountTypeEnum, OrderAccountDetail oad) {
        //退款子流程: 订单基础，退款描述，退款金额
        refundSubProcess(baseOrder, refundDesc, originalRefundAmount, refundAmont, accountTypeEnum.getCode(), refundStatus, oad);
        //设置剩余没有返还的钱
        orderRentVehicleBiz.updateSelectiveById(new OrderRentVehicleDetail(){{
            setId(orvd.getId());
            setReturnPayResidue(residueAmount);
        }});
    }

    /**
     * 退还部分押金
     * @param orderMQDTO
     */
    public void refundPartDeposit(OrderMQDTO orderMQDTO){
//        Map<String, Dictionary> dictionaryMap = thirdFeign.dictionaryGetAll4Map().getData();
//        BigDecimal illegalReserve = new BigDecimal(dictionaryMap.get(APP_ORDER+ "_"+ DictionaryKey.ILLEGAL_RESERVE).getDetail());

        BigDecimal illegalReserve = orderMQDTO.getOrderRentVehicleDetail().getTrafficDeposit();

        //未退还， 进行挂起保留违章预备金 的退还
        if(RefundStatusEnum.NONE.getCode().equals(orderMQDTO.getRefundStatus())) {
            Integer crosstownTypeEnum;
            DepositRefundStatus depositRefundRecordStatus;
            //生成额外的费用明细
            CancelStartedVO csv = new CancelStartedVO();

            //判断是否定损过
            if(SYS_TRUE.equals(orderMQDTO.getOrderRentVehicleDetail().getFixedLossStatus())) {
                crosstownTypeEnum = CrosstownTypeEnum.FIXED_LOSS.getCode();
                depositRefundRecordStatus = DepositRefundStatus.FIXLOSSREFUNDARRIVAL;
            }else {
                crosstownTypeEnum = CrosstownTypeEnum.ARRIVE.getCode();
                depositRefundRecordStatus = DepositRefundStatus.REFUNDARRIVAL;
            }
            OrderVehicleCrosstown crosstown = crosstownBiz.selectOne(new OrderVehicleCrosstown(){{
                setOrderId(orderMQDTO.getId());
                setType(crosstownTypeEnum);
            }});

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

            if(null == crosstown) {
                //设置等待时间 5秒
                try {
                    Thread.sleep(5000L);
                    crosstown = crosstownBiz.selectOne(new OrderVehicleCrosstown(){{
                        setOrderId(orderMQDTO.getId());
                        setType(crosstownTypeEnum);
                    }});
                    if(null == crosstown) {
                        throw new BaseException(ResultCode.PARAM_ILLEGAL_CODE, Sets.newSet(StrUtil.format("退款第一部分押金失败，获取不了还车/定损记录，订单号：{}, crosstownTypeEnum: {}",  orderMQDTO.getId(), crosstownTypeEnum)));
                    }
                } catch (InterruptedException e) {
                    log.error(e.getMessage(), e);
                    throw new BaseException(ResultCode.PARAM_ILLEGAL_CODE, Sets.newSet(StrUtil.format("退款第一部分押金失败，获取不了还车/定损记录，订单号：{}, crosstownTypeEnum: {}",  orderMQDTO.getId(), crosstownTypeEnum)));
                }
            }

            if(null == crosstown.getRestDeposit()
                    || crosstown.getRestDeposit().subtract(illegalReserve).compareTo(BigDecimal.ZERO) < 0 ){
                throw new BaseException(ResultCode.PARAM_ILLEGAL_CODE, Sets.newSet("退第一笔押金剩余金额异常，异常记录为："+ crosstown.getId()));
            }

            OrderAccountDetail oad = new OrderAccountDetail();
            InProgressVO inProgressVO = orderCalculateBiz.calculateOrderComplete(orderMQDTO, orderMQDTO.getOrderRentVehicleDetail(), oad, vehicleItemDTO, orderMQDTO.getOrderRentVehicleDetail().getUsedDay(), Boolean.FALSE);

            //设置定损金额
            String handleDedRefundDesc = "";
            handleDedRefundDesc = handleDed(crosstown, handleDedRefundDesc, csv);
            if(csv.getDamagesAmount().compareTo(BigDecimal.ZERO) > 0) {
                oad.getDeductions().add(
                        initDeduction(csv.getDamagesAmount(), handleDedRefundDesc, DeductionTypeEnum.DAMAGES, ORIGIN_DEPOSIT)
                );
            }

            //还车扣除款 剩余的 钱，再减去违章预备金
            oad.getDeductions().add(
                    initDeduction(illegalReserve, "违章保证金", DeductionTypeEnum.VIOLATE_TRAFFIC_KEEP, ORIGIN_DEPOSIT)
            );

            //剩余押金 = 押金 - 违章保证金 - 定损金额
            oad.setDepositAmount(oad.getDepositAmount().subtract(illegalReserve).subtract(csv.getDamagesAmount()));

            //设置违约金
            //设置原来算出的违约金及描述
            csv.setViolateAmount(inProgressVO.getViolateAmount());
            csv.setViolateDesc("");
            //处理更改之后的违约金及描述
            handleCrosstownDetail(crosstown, oad, csv);
            handleCrosstownDelayDetail(crosstown, oad);

            if(StrUtil.isBlank(csv.getViolateDesc())) {
                csv.setViolateDesc(inProgressVO.getViolateDesc());
            }

            //退款
            BigDecimal refundAmont = oad.getDepositAmount().add(oad.getOrderAmount());
            //crosstown.getRestDeposit().subtract(illegalReserve);
            //原退款的钱
            BigDecimal originalRefundAmount = oad.getOriginDepositAmount().add(oad.getOriginOrderAmount());
            //crosstown.getRestDeposit().add(crosstown.getDeductionCost()).subtract(illegalReserve);

            String refundDesc = "退还车辆押金："+ refundAmont.toString();

            refundTrigger(orderMQDTO, orderMQDTO.getOrderRentVehicleDetail(), illegalReserve, originalRefundAmount, refundAmont, refundDesc, RefundStatusEnum.RESIDUE_ILLEGAL.getCode(), AccountTypeEnum.OUT_PART_DEPOSIT, oad);

            //修改押金退还记录状态
            orderDepositRefundRecordBiz.completeRecordStatus(crosstown.getId(), depositRefundRecordStatus);

            //返回优惠券和免费天数
            //取消租车免费天数使用
            if(inProgressVO.getBackFreeDays() > 0) {
                int result = userFeign.memberDays(orderMQDTO.getUserId(), inProgressVO.getBackFreeDays(), UserFeign.MEMBER_DAYS_WITHDRAW);
                if(result < 0) {
                    throw new BaseException(ResultCode.FAILED_CODE);
                }
            }

            //设置订单参数
            BaseOrder updateBaseOrder = new BaseOrder();
            updateBaseOrder.setId(orderMQDTO.getId());

            //返还优惠券
            if(inProgressVO.getBackCoupons().size() > 0) {
                //设置订单参数
                updateBaseOrder.setBackCoupon(CollUtil.join(inProgressVO.getBackCoupons(), ","));
                for(String backCoupon : inProgressVO.getBackCoupons()) {
                    activityFeign.cancelUse(backCoupon);
                }
            }

            //设置订单参数
            updateBaseOrder.setDamagesAmount(csv.getDamagesAmount());
            updateBaseOrder.setViolateAmount(csv.getViolateAmount());
            updateBaseOrder.setExtraAmount(inProgressVO.getExtraAmount());
            baseOrderBiz.updateSelectiveByIdReT(updateBaseOrder, Boolean.TRUE);

            //生成额外的费用明细
            csv.setConsumeAmount(inProgressVO.getConsumeAmount());
            csv.setCouponAmount(inProgressVO.getCouponAmount());
            csv.setCouponDesc(inProgressVO.getCouponDesc());
            csv.setModelAmount(vehicleItemDTO.getUnitPrice());
            csv.setUsedAmount(inProgressVO.getUsedAmount());
            csv.setUsedDayNum(inProgressVO.getUsedDays());
            csv.setUsedfreeDayNum(inProgressVO.getUsedfreeDays());
            csv.setUsedFreeAmount(inProgressVO.getUsedFreeDaysAmount());
            csv.setHadConpon((StrUtil.isNotBlank(orderMQDTO.getCouponTickerNos())? Boolean.TRUE: Boolean.FALSE));

            csv.setAbleUsedDayNum(inProgressVO.getUseAmountList().size());
            csv.setSameUseUnitPriceFromList(inProgressVO.getUseAmountList());
            csv.setSameOverUnitPriceFromList(inProgressVO.getOverAmountList());
            csv.setUseAmountList(inProgressVO.getUseAmountList());
            csv.setOverAmountList(inProgressVO.getOverAmountList());
            csv.setViolateAmountList(inProgressVO.getViolateAmountList());

            if(csv.getUsedDayNum() == orderMQDTO.getOrderRentVehicleDetail().obtainRealDayNum()) {
                csv.setType(CostDetailExtend.FINLISH_ONTIME);

            }else if(csv.getUsedDayNum() > orderMQDTO.getOrderRentVehicleDetail().obtainRealDayNum()){
                csv.setType(CostDetailExtend.FINLISH_DELAY);

            }else if(csv.getUsedDayNum() < orderMQDTO.getOrderRentVehicleDetail().obtainRealDayNum()){
                csv.setType(CostDetailExtend.FINLISH_ADVANCE);

            }

            csv.setDeductionList(oad.getDeductions());
            csv.initParamJson();
            //设置明细
            orderRentVehicleBiz.updateSelectiveByIdRe(new OrderRentVehicleDetail(){{
                setId(orderMQDTO.getDetailId());
                handelCostDetailExtend(csv);
                setBackFreeDays(inProgressVO.getBackFreeDays());
            }});

            orderMQDTO.setOrderRentVehicleDetail(orderRentVehicleBiz.selectById(orderMQDTO.getDetailId()));

            //根据商品消费金额 设置changeAmount
            vehicleItemDTO.setChangeAmount(inProgressVO.getConsumeAmount().subtract(vehicleItemDTO.getRealAmount()));
            orderItemBiz.updateSelectiveByIdRe(vehicleItemDTO);

            //发送押金退还完成队列消息
            baseOrderBiz.sendOrderMq(orderMQDTO.getOrderRentVehicleDetail(), null, null, orderMQDTO, OrderMQDTO.ORDER_PART_DEPOSIT);

            //捕捉异常
            try {
                orderMsgBiz.handelMsgDeposit(orderMQDTO.getOrderRentVehicleDetail(), orderMQDTO, userFeign.userDetailById(orderMQDTO.getUserId()).getData());
            }catch (Exception e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    private String handleDed(OrderVehicleCrosstown crosstown, String refundDesc, CancelStartedVO csv) {
        try{
            if(null != crosstown.getDedDetail()) {
                List<DedDetailDTO> dddList = JSONUtil.toList(JSONUtil.parseArray(crosstown.getDedDetail()), DedDetailDTO.class);
                for(DedDetailDTO ddd : dddList) {
                    refundDesc += ", "+ ddd.getDeductions()+ ":"+ ddd.getCost();
                    csv.setDamagesAmount(csv.getDamagesAmount().add(ddd.getCost()));
                }
            }
        }catch (Exception e) {
            log.error("crosstown.getDedDetail() crosstown id :"+crosstown.getId() +" 转换失败");
        }
        return refundDesc;
    }

    private void handleCrosstownDetail(OrderVehicleCrosstown crosstown, OrderAccountDetail oad, CancelStartedVO csv) {
        try{
            if(null != crosstown.getViolateDetail()) {
                //修改代码
                List<DedDetailDTO> vios = JSONUtil.toList(JSONUtil.parseArray(crosstown.getViolateDetail()), DedDetailDTO.class);
                //DedDetailDTO vio = JSONUtil.toBean(crosstown.getViolateDetail(), DedDetailDTO.class);

                for(DedDetailDTO vio : vios) {
                    if(OrderViolateEnum.BEFORE.getCode().equals(vio.getType())) {
                        handleViolateDetail(DeductionTypeEnum.VIOLATE_ADVANCE, oad, vio, csv);
                    }else if(OrderViolateEnum.AFTER.getCode().equals(vio.getType())) {
                        handleViolateDetail(DeductionTypeEnum.VIOLATE_DELAY, oad, vio, csv);
                    }else if(OrderViolateEnum.CHANGE.getCode().equals(vio.getType())) {
                        handleViolateDetail(DeductionTypeEnum.VIOLATE_CHANGE_C, oad, vio, csv);
                    }
                }
            }
        }catch (Exception e) {
            log.error("crosstown.getViolateDetail() crosstown id :"+crosstown.getId() +" 转换失败");
        }
    }

    private void handleCrosstownDelayDetail(OrderVehicleCrosstown crosstown, OrderAccountDetail oad) {
        try{
            if(null != crosstown.getDelayVehicleDetail()) {
                //修改代码
                List<DedDetailDTO> vios = JSONUtil.toList(JSONUtil.parseArray(crosstown.getDelayVehicleDetail()), DedDetailDTO.class);

                for(DedDetailDTO vio : vios) {
                    if(OrderViolateEnum.DELAY_VEHICLE.getCode().equals(vio.getType())) {
                        Boolean flag = Boolean.FALSE;
                        for(OrderAccountDeduction deduction : oad.getDeductions()) {
                            if(DeductionTypeEnum.OTHER_DELAY_SAFE.getCode().equals(deduction.getType())) {
                                deduction.setName(vio.getDeductions());
                                deduction.setAmount(vio.getCost());
                                //修改归还押金金额
                                resetDeposit(oad);

                                flag = Boolean.TRUE;
                                break;
                            }
                        }

                        //如果没有修改，则添加
                        if(Boolean.FALSE.equals(flag)) {
                            OrderAccountDeduction oadNew = initDeduction(vio.getCost(), vio.getDeductions(), DeductionTypeEnum.OTHER_DELAY_SAFE, ORIGIN_DEPOSIT);
                            oad.getDeductions().add(oadNew);
                            //修改归还押金金额
                            resetDeposit(oad);

                        }
                    }
                }
            }
        }catch (Exception e) {
            log.error("crosstown.getDelayVehicleDetail() crosstown id :"+crosstown.getId() +" 转换失败");
        }
    }

    private void resetDeposit(OrderAccountDetail oad) {
        //修改归还押金金额

        //扣费（押金源）
        BigDecimal toDeduction = oad.getDeductions().parallelStream().filter(oadTemp ->
                (Integer.valueOf(ORIGIN_DEPOSIT).equals(oadTemp.getOrigin()))
        ).map(OrderAccountDeduction::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

        //扣费（订单_押金源）
        BigDecimal toOdDeduction = oad.getDeductions().parallelStream().filter(oadTemp ->
                (Integer.valueOf(ORIGIN_ORDER_DEPOSIT).equals(oadTemp.getOrigin()))
        ).map(OrderAccountDeduction::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal toExtendDeduction = oad.getOriginOrderAmount().subtract(toOdDeduction);
        BigDecimal residueOrderAmount = oad.getOriginOrderAmount().subtract(toOdDeduction);

        //如果订单原款 - 扣费 小于零，则押金有额外扣费
        if(toExtendDeduction.compareTo(BigDecimal.ZERO) < 0) {
            //置反 去用押金抵扣
            toExtendDeduction = BigDecimal.ZERO.subtract(toExtendDeduction);
            residueOrderAmount = BigDecimal.ZERO;
        }else {
            //否则 则置为零
            toExtendDeduction = BigDecimal.ZERO;
        }

        oad.setDepositAmount(oad.getOriginDepositAmount().subtract(toDeduction).subtract(toExtendDeduction));
        oad.setOrderAmount(residueOrderAmount);
    }

    private void handleViolateDetail(DeductionTypeEnum dte, OrderAccountDetail oad, DedDetailDTO vio, CancelStartedVO csv) {
        Boolean flag = Boolean.FALSE;
        for(OrderAccountDeduction deduction : oad.getDeductions()) {
            if(dte.getCode().equals(deduction.getType())) {
                deduction.setName(vio.getDeductions());
                BigDecimal diff = vio.getCost().subtract(deduction.getAmount());
                deduction.setAmount(vio.getCost());
                //修改归还押金金额
                resetDeposit(oad);

                //设置订单明细参数
                csv.setViolateAmount(csv.getViolateAmount().add(diff));
                csv.setViolateDesc(csv.getViolateDesc()+ deduction.getName());
                flag = Boolean.TRUE;
                break;
            }
        }

        //如果没有修改，则添加
        if(Boolean.FALSE.equals(flag)) {
            OrderAccountDeduction oadNew = initDeduction(vio.getCost(), vio.getDeductions(), dte, ORIGIN_DEPOSIT);
            oad.getDeductions().add(oadNew);
            //修改归还押金金额
            resetDeposit(oad);

            //设置订单明细参数
            csv.setViolateAmount(csv.getViolateAmount().add(oadNew.getAmount()));
            csv.setViolateDesc(csv.getViolateDesc()+ oadNew.getName());
        }
    }

    /**
     * 获取每日订单账目,用于统计
     * @param term
     * @return
     */
    public List<OrderAccountDTO> getOrderAccountByOrderType(Term term) {
        return mapper.getOrderAccountByOrderType(term);
    }

    public void handleRentDepositMarginV2(BaseOrder baseOrder, OrderRentVehicleDetail orvd){
        Integer crosstownTypeEnum;
        //判断是否定损过
        if(SYS_TRUE.equals(orvd.getFixedLossStatus())) {
            crosstownTypeEnum = CrosstownTypeEnum.FIXED_LOSS.getCode();
        }else {
            crosstownTypeEnum = CrosstownTypeEnum.ARRIVE.getCode();
        }
        OrderVehicleCrosstown crosstown = crosstownBiz.selectOne(new OrderVehicleCrosstown(){{
            setOrderId(baseOrder.getId());
            setType(crosstownTypeEnum);
        }});

        handleRentDepositMargin(baseOrder, orvd, crosstown);
    }


    public void handleRentDepositMargin(BaseOrder baseOrder, OrderRentVehicleDetail orvd, OrderVehicleCrosstown crosstown) {
        OrderViolation orderViolation = orderViolationBiz.selectOne(new OrderViolation(){{
            setDetailId(orvd.getId());
            setIsDel(SYS_FALSE);
        }});

        OrderAccountDetail oad = new OrderAccountDetail();

        String refundDesc = "退还违章押金：";
        BigDecimal refundAmont;
        if(null != orderViolation) {
            //设置扣款项
            oad.getDeductions().add(
                    initDeduction(orderViolation.getPrice(), DeductionTypeEnum.VIOLATE_TRAFFIC_DEDUCT.getDesc(), DeductionTypeEnum.VIOLATE_TRAFFIC_DEDUCT, ORIGIN_DEPOSIT)
            );

            //还车扣除款 剩余的 钱，再减去违章预备金
            refundAmont = orvd.getReturnPayResidue().subtract(orderViolation.getPrice());
            refundDesc += StrUtil.format("(扣除{}: {})", DeductionTypeEnum.VIOLATE_TRAFFIC_DEDUCT.getDesc(), orderViolation.getPrice().toString());
        }else {
            refundAmont = orvd.getReturnPayResidue();
        }
        oad.setOriginDepositAmount(orvd.getReturnPayResidue());
        oad.setDepositAmount(refundAmont);
        refundTrigger(baseOrder, orvd, BigDecimal.ZERO, orvd.getReturnPayResidue(), refundAmont, refundDesc, RefundStatusEnum.REFUND_DEPOSIT.getCode(), AccountTypeEnum.OUT_RESIDUE_DEPOSIT, oad);
        orderDepositRefundRecordBiz.completeRecordStatus(crosstown.getId(), DepositRefundStatus.VIOLATIONARRIVAL);
        orderMsgBiz.handelMsgDeposit(orvd, baseOrder, userFeign.userDetailById(baseOrder.getUserId()).getData());
        //发送押金退还完成队列消息
        baseOrderBiz.sendOrderMq(orvd, null, null, baseOrder, OrderMQDTO.ORDER_TRAFFIC_DEPOSIT);
    }

    /**
     *根据开始与结束时间查询账目
     * @param startDate
     * @param endDate
     * @return
     */
    public List<OrderAccountBo> selectByTypeAndDate(Integer orderType, Date startDate, Date endDate) {
        List<OrderAccountBo> accountBos = mapper.selectOrderAccountByOrderTypeAndStartTimeAndEndTime(orderType,startDate.getTime(),endDate.getTime());
        return CollectionUtils.isEmpty(accountBos)? Collections.EMPTY_LIST:accountBos;
    }

    /**
     * 根据创建时间与员工id查询
     * @param startDate
     * @param endDate
     * @param staffUserIds
     * @return
     */
    public List<OrderAccountBo> selectByDateAndStatffIds(Date startDate, Date endDate,List<Integer> staffUserIds) {
        List<OrderAccountBo> orderAccountBos = mapper.selectByDateAndStatffIds(startDate,endDate,staffUserIds);
        return CollectionUtils.isNotEmpty(orderAccountBos)?Collections.EMPTY_LIST:orderAccountBos;
    }


    public List<ReturnOrderAmount> outStatisticalData(QueryCriteria queryCriteria) {
        Long startTime=null;
        Long endTime= null;
        if (queryCriteria.getStartDate()!=null) {
            startTime=queryCriteria.getStartDate().getTime();
        }
        if (queryCriteria.getEndDate()!=null) {
            endTime= queryCriteria.getEndDate().getTime();
        }
        return  mapper.outStatisticalData(queryCriteria,startTime,endTime);
    }

    //订单完成时，payway为 支付宝，则转支付
    public void finishFreeze2PayAmount(BaseOrder baseOrder) {
        try {
            if (baseOrder.getPayWay().equals(PAY_WAY_ALI)) {
                OrderRefundVo orv = new OrderRefundVo();
                orv.setAmount(baseOrder.getRealAmount().multiply(new BigDecimal("100")).intValue());
                orv.setOrderNo(baseOrder.getNo());
                orv.setRefundAmount(0);
                orv.setRefundDesc("非退款");
                orv.setFreeze2PayAmount(baseOrder.getRealAmount().multiply(new BigDecimal("100")).intValue());
                orv.setFreeze2PayDesc(StrUtil.format("冻结转支付{}", baseOrder.getRealAmount()));
                ObjectRestResponse<String> result = thirdFeign.refund(orv);
                String refundTradeNo = result.getData();
                if(null == refundTradeNo) {
                    log.error("退款没有refundTradeNo，订单号为：{}, 微服务调用结果为{}", baseOrder.getNo(), JSONUtil.toJsonStr(result));
                }else {
                    log.info("转支付成功，订单号为：{}, 微服务调用结果为{}", baseOrder.getNo(), JSONUtil.toJsonStr(result));
                }
            }
        }catch (Exception ex) {
            log.error(StrUtil.format("退款异常{}，订单号为：{}", ex.getMessage(), baseOrder.getNo()), ex);
        }
    }

    public List<OrderAccount> getByOrderId(Integer orderId) {
        Example example = new Example(OrderAccount.class);
        example.createCriteria().andEqualTo("orderId", orderId).andGreaterThan("accountType", 200);
        return mapper.selectByExample(example);
    }

    public double getAllOrderCost(Integer orderId) {
        List<OrderAccount> list = getByOrderId(orderId);
        double amount = 0;
        if (list != null && list.size() > 0) {
            for (OrderAccount orderAccount : list) {
                if(orderAccount.getAccountAmount() != null && orderAccount.getAccountStatus() == 1) {
                    amount += orderAccount.getAccountAmount().doubleValue();
                }
            }
            log.info("amount: {}", amount);
        }
        return amount;
    }
}