package com.jsowell.pile.service.programlogic;

import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.jsowell.adapay.service.AdapayService;
import com.jsowell.adapay.vo.PaymentInfo;
import com.jsowell.common.constant.CacheConstants;
import com.jsowell.common.constant.Constants;
import com.jsowell.common.core.domain.ykc.RealTimeMonitorData;
import com.jsowell.common.core.domain.ykc.TransactionRecordsData;
import com.jsowell.common.core.redis.RedisCache;
import com.jsowell.common.enums.ykc.*;
import com.jsowell.common.util.DateUtils;
import com.jsowell.common.util.StringUtils;
import com.jsowell.pile.domain.*;
import com.jsowell.pile.dto.*;
import com.jsowell.pile.service.*;
import com.jsowell.pile.transaction.service.TransactionService;
import com.jsowell.pile.vo.uniapp.customer.MemberVO;
import com.jsowell.pile.vo.uniapp.customer.PersonalPileInfoVO;
import com.jsowell.pile.vo.uniapp.customer.PileConnectorDetailVO;
import com.jsowell.pile.vo.web.*;
import com.jsowell.wxpay.service.WxAppletRemoteService;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 描述订单逻辑
 */
public abstract class AbstractProgramLogic implements InitializingBean {
    protected Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${adapay.callback}")
    protected String ADAPAY_CALLBACK_URL;

    @Resource
    protected OrderBasicInfoService orderBasicInfoService;

    @Autowired
    protected PileBasicInfoService pileBasicInfoService;

    @Autowired
    protected PileStationInfoService pileStationInfoService;

    @Autowired
    protected PileBillingTemplateService pileBillingTemplateService;

    @Autowired
    protected MemberBasicInfoService memberBasicInfoService;

    @Autowired
    protected MemberAdapayRecordService memberAdapayRecordService;

    @Autowired
    protected OrderPayRecordService orderPayRecordService;

    @Autowired
    protected MemberTransactionRecordService memberTransactionRecordService;

    @Autowired
    protected AdapayService adapayService;

    @Autowired
    protected AdapayMemberAccountService adapayMemberAccountService;

    @Autowired
    protected PileMerchantInfoService pileMerchantInfoService;

    @Autowired
    protected TransactionService transactionService;

    @Autowired
    protected AdapayCallbackRecordService adapayCallbackRecordService;

    @Autowired
    protected PileAuthCardService pileAuthCardService;

    @Autowired
    protected MemberPlateNumberRelationService memberPlateNumberRelationService;

    @Autowired
    protected WxAppletRemoteService wxAppletRemoteService;

    @Autowired
    protected OrderMonitorDataService orderMonitorDataService;

    @Autowired
    protected MemberWalletInfoService memberWalletInfoService;

    @Autowired
    protected MemberGroupService memberGroupService;

    @Autowired
    protected PileStationWhitelistService pileStationWhitelistService;

    @Autowired
    protected PersonalChargingRecordService personalChargingRecordService;

    @Autowired
    protected RedisCache redisCache;

    /**
     * 生成订单
     */
    public abstract OrderBasicInfo generateOrder(GenerateOrderDTO dto) throws ParseException;

    /**
     * 支付订单
     *
     * @param dto
     * @return
     */
    public abstract Map<String, Object> payOrder(PayOrderDTO dto);

    /**
     * 个人桩启动充电
     * @param dto
     * @return
     */
    public abstract String startPersonalPileCharging(StartPersonPileDTO dto);

    /**
     * 订单结算/结算订单逻辑/订单结算逻辑
     */
    public abstract void settleOrder(TransactionRecordsData data, OrderBasicInfo orderBasicInfo);

    /**
     * 第三方平台结算订单（目前给华为用）
     *
     * @param data
     * @param orderBasicInfo
     */
    public abstract void settleOrderForThirdParty(TransactionRecordsData data, OrderBasicInfo orderBasicInfo);

    /**
     * 订单退款
     */
    public abstract void refundOrder(OrderBasicInfo orderBasicInfo);

    /**
     * 余额充值
     */
    public abstract Map<String, Object> rechargeBalance(WeixinPayDTO dto);

    /**
     * 余额退款
     *
     * @param dto
     */
    public abstract void refundBalance(ApplyRefundDTO dto);

    /**
     * 查询退款信息
     */
    public abstract List<OrderDetailInfoVO.OrderRefundInfo> getOrderRefundInfoList(OrderBasicInfo orderBasicInfo);


    /**
     * 订单支付成功 支付回调
     * 支付成功后掉用这个方法
     * 1. 修改订单支付状态
     * 2. 发送启动充电指令
     */
    protected void payOrderSuccessCallback(PayOrderSuccessCallbackDTO dto) {
        orderBasicInfoService.payOrderSuccessCallback(dto); // AbstractProgramLogic 订单支付成功 支付回调
    }

    protected List<BalanceDeductionAmountVO> calculateTheBalanceDeductionAmount(String memberId, BigDecimal amount) {
        return orderBasicInfoService.calculateTheBalanceDeductionAmount(memberId, amount);
    }


    /**
     * 返回更新后的OrderBasicInfo对象
     * 专用方法，其他地方如果要用请仔细检查
     * 【公共方法】
     */
    protected void returnUpdateOrderBasicInfo(OrderBasicInfo orderBasicInfo, TransactionRecordsData data) {
        // 订单编号
        String orderCode = orderBasicInfo.getOrderCode();
        // 消费金额就是订单总金额/交易记录传过来的消费金额, 四舍五入保留两位小数
        BigDecimal orderAmount = new BigDecimal(data.getConsumptionAmount()).setScale(2, RoundingMode.HALF_UP);
        // 付款金额 - 实际消费金额，如果有剩余，需要走退款操作 当使用余额支付时payAmount = principalPay + giftPay
        BigDecimal payAmount = orderBasicInfo.getPayAmount();

        // 有时候充电桩到达金额停止充电会多出一点金额，比如实际需要充50元的电，充电桩传来的消费金额为50.01元，在后台记录的时候需要舍去
        if (orderAmount.compareTo(payAmount) > 0) {
            logger.info("结算订单:【{}】充电桩传来的消费金额:【{}】大于付款金额:【{}】, 消费金额设置为付款金额相等数据", orderCode, orderAmount, payAmount);
            orderAmount = payAmount;
        }
        orderBasicInfo.setOrderAmount(orderAmount); // 订单总金额

        // 虚拟金额 指订单消费中不参与结算的部分
        BigDecimal virtualAmount = BigDecimal.ZERO;
        if (OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue().equals(orderBasicInfo.getPayMode())
                || OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue().equals(orderBasicInfo.getPayMode())) {
            // 白名单支付所消费的金额，都属于虚拟金额，不参与结算对账
            virtualAmount = new BigDecimal(orderAmount.toString());
        }

        // 退款金额 = 支付金额 - 订单消费金额 剩余需要退回的金额 residue
        BigDecimal residue = payAmount.subtract(orderAmount);

        // 结算金额 = 消费金额 - 虚拟金额 - 优惠金额
        BigDecimal settleAmount = orderAmount.subtract(virtualAmount);

        // 把交易记录中的用电量，金额等信息 更新到orderBasicInfo和orderDetail
        orderBasicInfo.setVirtualAmount(virtualAmount); // 虚拟金额
        orderBasicInfo.setSettleAmount(settleAmount); // 结算金额
        orderBasicInfo.setOrderStatus(OrderStatusEnum.ORDER_COMPLETE.getValue());
        orderBasicInfo.setReason(data.getStopReasonMsg()); // 充电停止原因
        if (Objects.isNull(orderBasicInfo.getSettlementTime())) {
            // 如果结算时间为空，设置当前时间为结算时间
            orderBasicInfo.setSettlementTime(DateUtils.getNowDate()); // 结算时间
        }
        orderBasicInfo.setRefundAmount(residue); // 结算退款金额
    }

    /**
     * 获取更新数据后的orderDetail对象
     * 专用方法，其他地方如果要用请仔细检查
     * 【公共方法】
     *
     * @return 查询并更新过数据的orderDetail
     */
    protected OrderDetail returnUpdateOrderDetail(OrderBasicInfo orderBasicInfo, TransactionRecordsData data) {
        String orderCode = orderBasicInfo.getOrderCode();
        BigDecimal orderAmount = orderBasicInfo.getOrderAmount();
        // 更新订单详情 查询订单详情 修改订单数据
        OrderDetail orderDetail = orderBasicInfoService.getOrderDetailByOrderCode(orderCode);
        try {
            // 总电费金额
            BigDecimal totalElectricityAmount = BigDecimal.ZERO;
            // 总服务费金额
            BigDecimal totalServiceAmount = BigDecimal.ZERO;

            // 尖时段用电量
            BigDecimal sharpUsedElectricity = StringUtils.isNotBlank(data.getSharpUsedElectricity())
                    ? new BigDecimal(data.getSharpUsedElectricity())
                    : BigDecimal.ZERO;
            orderDetail.setSharpUsedElectricity(sharpUsedElectricity);
            if (sharpUsedElectricity.compareTo(BigDecimal.ZERO) > 0) {
                if (data.getSharpPrice() != null) {
                    orderDetail.setSharpPrice(new BigDecimal(data.getSharpPrice()));
                }
                if (data.getSharpAmount() != null) {
                    orderDetail.setSharpAmount(new BigDecimal(data.getSharpAmount()));
                }
                // 计算该时段电费 = 电费单价 x 用电量
                BigDecimal sharpElectricityAmount = orderDetail.getSharpElectricityPrice()
                        .multiply(sharpUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                totalElectricityAmount = totalElectricityAmount.add(sharpElectricityAmount);
                // 计算该时段服务费
                BigDecimal sharpServiceAmount = orderDetail.getSharpServicePrice()
                        .multiply(sharpUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                totalServiceAmount = totalServiceAmount.add(sharpServiceAmount);
            }

            // 峰时段用电量
            BigDecimal peakUsedElectricity = StringUtils.isNotBlank(data.getPeakUsedElectricity())
                    ? new BigDecimal(data.getPeakUsedElectricity())
                    : BigDecimal.ZERO;
            orderDetail.setPeakUsedElectricity(peakUsedElectricity);
            if (peakUsedElectricity.compareTo(BigDecimal.ZERO) > 0) {
                if (data.getPeakPrice() != null) {
                    orderDetail.setPeakPrice(new BigDecimal(data.getPeakPrice()));
                }
                if (data.getPeakAmount() != null) {
                    orderDetail.setPeakAmount(new BigDecimal(data.getPeakAmount()));
                }
                // 计算该时段电费 = 电费单价 x 用电量
                BigDecimal peakElectricityAmount = orderDetail.getPeakElectricityPrice()
                        .multiply(peakUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                totalElectricityAmount = totalElectricityAmount.add(peakElectricityAmount);
                // 计算该时段服务费
                BigDecimal peakServiceAmount = orderDetail.getPeakServicePrice()
                        .multiply(peakUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                totalServiceAmount = totalServiceAmount.add(peakServiceAmount);
            }

            // 平时段用电量
            BigDecimal flatUsedElectricity = StringUtils.isNotBlank(data.getFlatUsedElectricity())
                    ? new BigDecimal(data.getFlatUsedElectricity())
                    : BigDecimal.ZERO;
            orderDetail.setFlatUsedElectricity(flatUsedElectricity);
            if (flatUsedElectricity.compareTo(BigDecimal.ZERO) > 0) {
                if (data.getFlatPrice() != null) {
                    orderDetail.setFlatPrice(new BigDecimal(data.getFlatPrice()));
                }
                if (data.getFlatAmount() != null) {
                    orderDetail.setFlatAmount(new BigDecimal(data.getFlatAmount()));
                }
                // 计算该时段电费 = 电费单价 x 用电量
                BigDecimal flatElectricityAmount = orderDetail.getFlatElectricityPrice()
                        .multiply(flatUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                totalElectricityAmount = totalElectricityAmount.add(flatElectricityAmount);
                // 计算该时段服务费
                BigDecimal flatServiceAmount = orderDetail.getFlatServicePrice()
                        .multiply(flatUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                totalServiceAmount = totalServiceAmount.add(flatServiceAmount);
            }

            // 谷时段用电量
            BigDecimal valleyUsedElectricity = StringUtils.isNotBlank(data.getValleyUsedElectricity())
                    ? new BigDecimal(data.getValleyUsedElectricity())
                    : BigDecimal.ZERO;
            orderDetail.setValleyUsedElectricity(valleyUsedElectricity);
            if (valleyUsedElectricity.compareTo(BigDecimal.ZERO) > 0) {
                if (data.getValleyPrice() != null) {
                    orderDetail.setValleyPrice(new BigDecimal(data.getValleyPrice()));
                }
                if (data.getValleyAmount() != null) {
                    orderDetail.setValleyAmount(new BigDecimal(data.getValleyAmount()));
                }
                // 计算该时段电费 = 电费单价 x 用电量
                BigDecimal valleyElectricityAmount = orderDetail.getValleyElectricityPrice()
                        .multiply(valleyUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                totalElectricityAmount = totalElectricityAmount.add(valleyElectricityAmount);
                // 计算该时段服务费
                BigDecimal valleyServiceAmount = orderDetail.getValleyServicePrice()
                        .multiply(valleyUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                totalServiceAmount = totalServiceAmount.add(valleyServiceAmount);
            }

            // 如果算出来的 电费金额 + 服务费金额 != 总消费金额，则电费金额等于总消费金额 - 服务费金额
            if (totalElectricityAmount.add(totalServiceAmount).compareTo(orderAmount) != 0) {
                totalElectricityAmount = orderAmount.subtract(totalServiceAmount);
            }

            // 电费总金额
            orderDetail.setTotalElectricityAmount(totalElectricityAmount);
            // 服务费总金额
            orderDetail.setTotalServiceAmount(totalServiceAmount);
            // 尖峰平谷用电量汇总
            BigDecimal sumUsedElectricity = sharpUsedElectricity.add(peakUsedElectricity)
                    .add(flatUsedElectricity).add(valleyUsedElectricity);
            // data中的总用电量
            BigDecimal totalElectricity = new BigDecimal(data.getTotalElectricity());
            if (sumUsedElectricity.compareTo(totalElectricity) != 0) {
                // 汇总数据和data中数据不相等，取汇总的数据
                totalElectricity = sumUsedElectricity;
            }
            orderDetail.setTotalUsedElectricity(totalElectricity);
            // 订单总金额
            orderDetail.setTotalOrderAmount(orderAmount);
        } catch (Exception e) {
            logger.error("orderCode:{}, 设置订单详情参数发生异常", orderCode, e);
        }
        return orderDetail;
    }


    /**
     * 获取更新数据后的orderDetail对象(给第三方平台结算订单用)
     *
     * @param orderBasicInfo
     * @param data
     * @return
     */
    protected OrderDetail returnUpdateOrderDetailForThirdParty(OrderBasicInfo orderBasicInfo, TransactionRecordsData data) {
        String orderCode = orderBasicInfo.getOrderCode();
        BigDecimal orderAmount = orderBasicInfo.getOrderAmount();
        // 更新订单详情 查询订单详情 修改订单数据
        OrderDetail orderDetail = orderBasicInfoService.getOrderDetailByOrderCode(orderCode);
        // 总用电量
        orderDetail.setTotalUsedElectricity(new BigDecimal(data.getTotalElectricity()));
        // 总电费
        orderDetail.setTotalElectricityAmount(new BigDecimal(data.getTotalElectricityAmount()));
        // 总服务费
        orderDetail.setTotalServiceAmount(new BigDecimal(data.getTotalServiceAmount()));
        // 总订单金额
        orderDetail.setTotalOrderAmount(orderAmount);

        return orderDetail;
    }

    /**
     * 余额支付 计算需要退回的金额
     * 【公共方法】
     *
     * @param principalPay 本金支付金额
     * @param giftPay      赠送金额支付的金额
     * @param orderAmount  订单消费金额
     * @return
     */
    protected Map<String, BigDecimal> calculateReturnAmount(BigDecimal principalPay, BigDecimal giftPay, BigDecimal orderAmount) {
        return calculateReturnAmount(principalPay, giftPay, orderAmount, BigDecimal.ZERO);
    }

    /**
     * 余额支付 计算需要退回的金额
     * 【公共方法】
     *
     * @param principalPay   本金支付金额
     * @param giftPay        赠送金额支付的金额
     * @param orderAmount    订单消费金额
     * @param discountAmount 订单折扣金额
     * @return
     */
    protected Map<String, BigDecimal> calculateReturnAmount(BigDecimal principalPay, BigDecimal giftPay, BigDecimal orderAmount, BigDecimal discountAmount) {
        Map<String, BigDecimal> resultMap = Maps.newHashMap();

        // 消费金额优先使用本金
        BigDecimal returnPrincipal = null; // 退回本金金额
        BigDecimal returnGift = null; // 退回赠送金额

        if (discountAmount == null) {
            discountAmount = BigDecimal.ZERO;
        }
        // 实际消费金额 = 订单消费金额 - 订单折扣金额
        BigDecimal amountDue = orderAmount.subtract(discountAmount);

        // 余额支付 有3种情况
        if (principalPay != null && giftPay == null) {
            // 只有本金支付
            BigDecimal subtract = principalPay.subtract(amountDue);
            if (subtract.compareTo(BigDecimal.ZERO) > 0) {
                returnPrincipal = subtract;
            }
        }
        if (principalPay == null && giftPay != null) {
            // 只有赠送金额支付
            BigDecimal subtract = giftPay.subtract(amountDue);
            if (subtract.compareTo(BigDecimal.ZERO) > 0) {
                returnGift = subtract;
            }
        }
        if (principalPay != null && giftPay != null) {
            // 本金+赠送支付
            BigDecimal subtract = principalPay.subtract(amountDue);
            if (subtract.compareTo(BigDecimal.ZERO) > 0) {
                // 本金减掉订单金额后还有剩余，那就把剩余的退回，赠送原封不动退回
                returnPrincipal = subtract;
                returnGift = giftPay;
            } else if (subtract.compareTo(BigDecimal.ZERO) == 0) {
                // 本金刚好够，那赠送金额支付的原封不动退回
                returnGift = giftPay;
            } else {
                returnGift = giftPay.subtract(subtract.negate());
            }
        }

        resultMap.put("returnPrincipal", returnPrincipal);
        resultMap.put("returnGift", returnGift);
        return resultMap;
    }

    /**
     * 计算解冻金额
     *
     * @param orderAmount   订单消费金额
     * @param payRecordList 订单支付记录
     */
    public List<Map<String, Object>> calculateUnfreezeAmount(BigDecimal orderAmount, List<OrderPayRecord> payRecordList) {
        List<Map<String, Object>> resultList = Lists.newArrayList();

        BigDecimal tempAmount = new BigDecimal(orderAmount.toString()); // 临时金额
        for (OrderPayRecord record : payRecordList) {
            if (!StringUtils.equals(record.getPayMode(), OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue())) {
                // 不是本金支付, 进行下一个循环
                continue;
            }
            List<PaymentInfo> paymentInfos = orderPayRecordService.parseDeductionRecord(record.getDeductionRecord());
            for (PaymentInfo object : paymentInfos) {
                String paymentId = object.getPaymentId();
                BigDecimal payAmount = new BigDecimal(object.getAmount()); // 此交易单支付的金额

                // 该笔支付解冻金额
                BigDecimal unfreezeAmount;

                if (tempAmount.compareTo(BigDecimal.ZERO) <= 0) {
                    unfreezeAmount = payAmount;
                    Map<String, Object> map = Maps.newHashMap();
                    map.put("paymentId", paymentId);
                    map.put("unfreezeAmount", unfreezeAmount);
                    resultList.add(map);
                } else {
                    BigDecimal min = tempAmount.min(payAmount);
                    unfreezeAmount = payAmount.subtract(min);
                    tempAmount = tempAmount.subtract(min);
                    Map<String, Object> map = Maps.newHashMap();
                    map.put("paymentId", paymentId);
                    map.put("unfreezeAmount", unfreezeAmount);
                    resultList.add(map);
                }
            }
        }
        return resultList;
    }

    /**
     * 从redis中取出实时记录保存到表中
     * 当订单完成的时候调用
     * 【公共方法】
     */
    protected void realTimeMonitorDataRedis2DB(String transactionCode, String orderCode) {
        try {
            if (StringUtils.isBlank(transactionCode) || StringUtils.isBlank(orderCode)) {
                return;
            }
            // 校验有没有保存过
            OrderMonitorData orderMonitorData = orderMonitorDataService.selectByOrderCode(orderCode);
            if (orderMonitorData != null) {
                return;
            }
            List<RealTimeMonitorData> chargingRealTimeData = orderBasicInfoService.getChargingRealTimeData(transactionCode);
            if (CollectionUtils.isEmpty(chargingRealTimeData)) {
                return;
            }
            List<RealTimeMonitorData> list = Lists.newArrayList();
            for (RealTimeMonitorData data : chargingRealTimeData) {
                RealTimeMonitorData build = RealTimeMonitorData.builder()
                        .outputCurrent(data.getOutputCurrent())
                        .outputCurrent(data.getOutputCurrent()) // 电流
                        .outputVoltage(data.getOutputVoltage()) // 电压
                        .outputPower(data.getOutputPower())  // 功率
                        .SOC(data.getSOC()) // soc
                        .dateTime(data.getDateTime())  // 时间
                        .batteryMaxTemperature(data.getBatteryMaxTemperature())
                        .chargingAmount(data.getChargingAmount())
                        .chargingDegree(data.getChargingDegree())
                        .sumChargingTime(data.getSumChargingTime())
                        .timeRemaining(data.getTimeRemaining())
                        .gunLineTemperature(data.getGunLineTemperature())
                        .build();
                list.add(build);
            }

            OrderMonitorData record = new OrderMonitorData();
            record.setOrderCode(orderCode);
            record.setTransactionCode(transactionCode);
            record.setMonitorData(JSON.toJSONString(list));
            int insert = orderMonitorDataService.insertSelective(record);
            if (insert > 0) {
                // 删除redis中缓存
                String pileConnectorCode = transactionCode.substring(0, 16);
                String redisKey = CacheConstants.PILE_REAL_TIME_MONITOR_DATA + pileConnectorCode + "_" + transactionCode;
                redisCache.deleteObject(redisKey);
            }
        } catch (Exception e) {
            logger.info("redis中取出实时记录保存到表发生异常", e);
        }
    }

    /**
     * 卡状态解锁
     * 【公共方法】
     */
    protected void cardStatusUnlocked(String logicCard) {
        try {
            // 根据物理卡号查出当前为锁定状态的卡
            PileAuthCard cardInfo = pileAuthCardService.selectSomeStatusCardInfo(CardStatusEnum.START_LOCK.getCode(), logicCard);
            if (cardInfo != null) {
                // 将此卡状态改为正常
                cardInfo.setStatus(CardStatusEnum.NORMAL.getCode());
                pileAuthCardService.updatePileAuthCard(cardInfo);
            }
        } catch (Exception e) {
            logger.error("解锁卡状态 error,", e);
        }
    }

    /**
     * 解锁vin状态
     * 【公共方法】
     *
     * @param vinCode
     */
    protected void vinStatusUnlocked(String vinCode) {
        try {
            MemberPlateNumberRelation plateInfo = memberPlateNumberRelationService.getMemberPlateInfoByVinCode(vinCode);
            if (plateInfo != null) {
                plateInfo.setVinStatus(Constants.ONE);
                memberPlateNumberRelationService.updateMemberPlateNumberRelation(plateInfo);
            }
        } catch (Exception e) {
            logger.error("解锁vin状态 error,", e);
        }
    }

    /**
     * 计算订单折扣V2
     * 使用优惠计费模板计算优惠金额
     *
     * @param orderBasicInfo 订单主表
     * @param orderDetail    订单详情
     */
    protected void calculateOrderDiscountsV2(OrderBasicInfo orderBasicInfo, OrderDetail orderDetail) {
        String memberId = orderBasicInfo.getMemberId(); // 会员id
        String stationId = orderBasicInfo.getStationId(); // 站点id
        String merchantId = orderBasicInfo.getMerchantId(); // 运营商id

        // 原始电费金额
        BigDecimal originalTotalElectricityAmount = orderDetail.getTotalElectricityAmount();
        // 原始服务费金额
        BigDecimal originalTotalServiceAmount = orderDetail.getTotalServiceAmount();
        // 电费折扣金额 初始值为0
        BigDecimal electricityAmountDiscount = BigDecimal.ZERO;
        // 服务费折扣金额 初始值为0
        BigDecimal serviceAmountDiscount = BigDecimal.ZERO;
        // 优惠后电费金额 初始值为优惠前金额
        BigDecimal afterDiscountElectricityAmount = BigDecimal.ZERO;
        // 优惠后服务费金额 初始值为优惠前金额
        BigDecimal afterDiscountServiceAmount = BigDecimal.ZERO;

        // 查询会员在该站点享受的优惠
        MemberDiscountVO memberDiscountVO = memberGroupService.queryMemberDiscountV2(merchantId, stationId, memberId);
        if (memberDiscountVO == null) {
            // 如果没有优惠，则优惠后金额 等于优惠前金额
            afterDiscountServiceAmount = orderDetail.getTotalServiceAmount();
            afterDiscountElectricityAmount = orderDetail.getTotalElectricityAmount();
        } else {
            // 集团会员的订单，记录集团编号
            orderBasicInfo.setGroupCode(memberDiscountVO.getGroupCode());

            // 集团会员的优惠计费模板
            BillingTemplateVO billingTemplateVO = memberDiscountVO.getBillingTemplateVO();

            // 尖时段用电量
            BigDecimal sharpUsedElectricity = orderDetail.getSharpUsedElectricity();
            if (sharpUsedElectricity != null) {
                // 计算该时段电费 = 电费单价 x 用电量
                BigDecimal sharpElectricityAmount = billingTemplateVO.getSharpElectricityPrice()
                        .multiply(sharpUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                // 计算该时段服务费
                BigDecimal sharpServiceAmount = billingTemplateVO.getSharpServicePrice()
                        .multiply(sharpUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);

                // 汇总
                afterDiscountElectricityAmount = afterDiscountElectricityAmount.add(sharpElectricityAmount);
                afterDiscountServiceAmount = afterDiscountServiceAmount.add(sharpServiceAmount);
            }

            // 峰时段用电量
            BigDecimal peakUsedElectricity = orderDetail.getPeakUsedElectricity();
            if (peakUsedElectricity != null) {
                // 计算该时段电费 = 电费单价 x 用电量
                BigDecimal peakElectricityAmount = billingTemplateVO.getPeakElectricityPrice()
                        .multiply(peakUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                // 计算该时段服务费
                BigDecimal peakServiceAmount = billingTemplateVO.getPeakServicePrice()
                        .multiply(peakUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);

                // 汇总
                afterDiscountElectricityAmount = afterDiscountElectricityAmount.add(peakElectricityAmount);
                afterDiscountServiceAmount = afterDiscountServiceAmount.add(peakServiceAmount);
            }

            // 平时段用电量
            BigDecimal flatUsedElectricity = orderDetail.getFlatUsedElectricity();
            if (flatUsedElectricity != null) {
                // 计算该时段电费 = 电费单价 x 用电量
                BigDecimal flatElectricityAmount = billingTemplateVO.getFlatElectricityPrice()
                        .multiply(flatUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                // 计算该时段服务费
                BigDecimal flatServiceAmount = billingTemplateVO.getFlatServicePrice()
                        .multiply(flatUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);

                // 汇总
                afterDiscountElectricityAmount = afterDiscountElectricityAmount.add(flatElectricityAmount);
                afterDiscountServiceAmount = afterDiscountServiceAmount.add(flatServiceAmount);
            }

            // 谷时段用电量
            BigDecimal valleyUsedElectricity = orderDetail.getValleyUsedElectricity();
            if (valleyUsedElectricity != null) {
                // 计算该时段电费 = 电费单价 x 用电量
                BigDecimal valleyElectricityAmount = billingTemplateVO.getValleyElectricityPrice()
                        .multiply(valleyUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);
                // 计算该时段服务费
                BigDecimal valleyServiceAmount = billingTemplateVO.getValleyServicePrice()
                        .multiply(valleyUsedElectricity)
                        .setScale(2, RoundingMode.DOWN);

                // 汇总
                afterDiscountElectricityAmount = afterDiscountElectricityAmount.add(valleyElectricityAmount);
                afterDiscountServiceAmount = afterDiscountServiceAmount.add(valleyServiceAmount);
            }

            // 计算优惠了多少钱
            // 电费折扣金额
            electricityAmountDiscount = originalTotalElectricityAmount.subtract(afterDiscountElectricityAmount);
            // 服务费折扣金额
            serviceAmountDiscount = originalTotalServiceAmount.subtract(afterDiscountServiceAmount);
        }

        /*
        更新 数据
         */
        // 订单折扣金额
        BigDecimal discountAmount = electricityAmountDiscount.add(serviceAmountDiscount);
        orderBasicInfo.setDiscountAmount(discountAmount);

        // 更新结算金额  结算金额 = 消费金额 - 虚拟金额 - 优惠金额
        BigDecimal newSettleAmount = orderBasicInfo.getSettleAmount().subtract(discountAmount);
        if (newSettleAmount.compareTo(BigDecimal.ZERO) < 0) {
            // 如果结算金额为0，减去优惠金额后为负数，还设置为0
            newSettleAmount = BigDecimal.ZERO;
        }
        orderBasicInfo.setSettleAmount(newSettleAmount);

        // 优惠后总消费金额 = 折扣后电费 + 折扣后服务费
        BigDecimal totalConsumeAmount = afterDiscountServiceAmount.add(afterDiscountElectricityAmount);

        // 更新退款金额 = 支付金额 - 优惠后总消费金额
        BigDecimal refundAmount = orderBasicInfo.getPayAmount().subtract(totalConsumeAmount).setScale(2, RoundingMode.DOWN);
        orderBasicInfo.setRefundAmount(refundAmount);
        // 电费折扣金额
        orderDetail.setDiscountElectricityAmount(electricityAmountDiscount);
        // 服务费折扣金额
        orderDetail.setDiscountServiceAmount(serviceAmountDiscount);
        logger.info("计算订单折扣, orderCode:{}, memberId:{}, 订单折扣金额:{}, 电费折扣金额:{}, 服务费折扣金额:{}, 优惠后总消费金额:{}",
                orderBasicInfo.getOrderCode(), memberId, discountAmount, electricityAmountDiscount, serviceAmountDiscount, totalConsumeAmount);
    }

    /**
     * 计算余额支付下发金额
     */
    protected Map<String, BigDecimal> calculateTheAmount(MemberVO memberVO) {
        BigDecimal principalBalancePay;
        BigDecimal giftBalancePay;
        BigDecimal totalAccountAmount = memberVO.getTotalBalance();
        // 余额支付最大下发200
        BigDecimal defaultAmount = Constants.BALANCE_PAY_MAX_AMOUNT;
        if (totalAccountAmount.compareTo(defaultAmount) < 0) {
            principalBalancePay = memberVO.getPrincipalBalance();
            giftBalancePay = memberVO.getGiftBalance();
        } else {
            principalBalancePay = defaultAmount.min(memberVO.getPrincipalBalance());
            giftBalancePay = defaultAmount.subtract(principalBalancePay);
        }
        Map<String, BigDecimal> resultMap = Maps.newHashMap();
        resultMap.put("principalBalancePay", principalBalancePay);
        resultMap.put("giftBalancePay", giftBalancePay);
        return resultMap;
    }

    /**
     * 是否有白名单权限
     */
    protected boolean hasWhitelistPermission(String pileConnectorCode, String memberId) {
        // 查询桩信息
        PileConnectorDetailVO pileConnectorDetailVO = pileBasicInfoService.queryPileConnectorDetail(pileConnectorCode);

        // 查询会员在站点是否是白名单用户
        PileStationWhitelist whitelist = pileStationWhitelistService.queryWhitelistByMemberId(pileConnectorDetailVO.getStationId(), memberId);

        // 查询是否为平台测试员
        PlatformTesterVO platformTesterVO = memberBasicInfoService.selectPlatformTesterStatus(memberId);

        // 查询个人桩
        List<PersonalPileInfoVO> personalPileInfoVOListList = pileBasicInfoService.getPileInfoByMemberId(memberId);
        List<String> personalPileSnList = personalPileInfoVOListList.stream()
                .map(PersonalPileInfoVO::getPileSn)
                .collect(Collectors.toList());
        String pileSn = pileConnectorCode.substring(0, pileConnectorCode.length() - 2);

        /*
        满足一下三种情况任意一种，就可以使用白名单支付
        1、该会员是站点白名单
        2、该会员是平台测试员
        3、该会员的个人桩列表包含目标桩
         */
        boolean result = whitelist != null
                || StringUtils.equals(platformTesterVO.getStatus(), Constants.ONE)
                || personalPileSnList.contains(pileSn);
        logger.info("会员:{}是否有枪口编号:{}的白名单权限, whitelist:{}, platformTesterVO:{}, personalPileSnList:{}, 校验结果:{}",
                memberId, pileConnectorCode, whitelist, platformTesterVO, personalPileSnList, result);
        return result;
    }
}
