package com.jsowell.pile.service.orderlogic;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.huifu.adapay.core.exception.BaseAdaPayException;
import com.huifu.adapay.model.Payment;
import com.jsowell.adapay.common.CreateAdaPaymentParam;
import com.jsowell.adapay.config.AbstractAdapayConfig;
import com.jsowell.adapay.factory.AdapayConfigFactory;
import com.jsowell.adapay.response.PaymentReverseResponse;
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.TransactionRecordsData;
import com.jsowell.common.enums.AcquirerEnum;
import com.jsowell.common.enums.DelFlagEnum;
import com.jsowell.common.enums.MemberWalletEnum;
import com.jsowell.common.enums.adapay.MerchantDelayModeEnum;
import com.jsowell.common.enums.ykc.*;
import com.jsowell.common.exception.BusinessException;
import com.jsowell.common.util.AdapayUtil;
import com.jsowell.common.util.DateUtils;
import com.jsowell.common.util.StringUtils;
import com.jsowell.common.util.id.IdUtils;
import com.jsowell.common.util.id.SnowflakeIdWorker;
import com.jsowell.pile.domain.*;
import com.jsowell.pile.dto.*;
import com.jsowell.pile.transaction.dto.OrderTransactionDTO;
import com.jsowell.pile.vo.uniapp.MemberVO;
import com.jsowell.pile.vo.web.BalanceDeductionAmountVO;
import com.jsowell.pile.vo.web.BillingTemplateVO;
import com.jsowell.pile.vo.web.UpdateMemberBalanceDTO;
import com.jsowell.wxpay.dto.WechatSendMsgDTO;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.ParseException;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 设置延时分账的运营商订单逻辑
 */
@Service
public class DelayMerchantOrderLogic extends AbstractOrderLogic {

    @Override
    public void afterPropertiesSet() throws Exception {
        // 注册为 延时分账
        OrderLogicFactory.register(MerchantDelayModeEnum.DELAY.getValue(), this);
    }

    /**
     * 生成订单
     *
     * @param dto
     */
    @Override
    public OrderBasicInfo generateOrder(GenerateOrderDTO dto) throws ParseException {
        logger.info("【{}】-生成订单start, param:{}", this.getClass().getSimpleName(), JSONObject.toJSONString(dto));
        // 处理前端传的参数
        analysisPileParameter(dto);

        // 校验充电桩相关的信息
        checkPileInfo(dto);

        // 保存订单到数据库 saveOrder2Database
        return saveOrder2Database(dto);
    }

    /**
     * 生成订单编号
     *
     * @return
     */
    private String generateNewOrderCode() {
        while (true) {
            String orderCode = "C" + IdUtils.getOrderCode();
            // 通过orderCode查询是否已经存在
            OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
            if (orderBasicInfo == null) {
                return orderCode;
            }
        }
    }

    /**
     * 保存订单信息到数据库
     *
     * @param dto
     * @return
     */
    protected OrderBasicInfo saveOrder2Database(GenerateOrderDTO dto) throws ParseException {
        String orderCode = generateNewOrderCode();
        String transactionCode = IdUtils.generateTransactionCode(dto.getPileSn(), dto.getConnectorCode());

        if (StringUtils.isBlank(dto.getStartType())) {
            dto.setStartType(StartTypeEnum.NOW.getValue());
        }

        String stationId = dto.getPileConnector().getStationId();
        // 查询站点信息
        PileStationInfo pileStationInfo = pileStationInfoService.selectPileStationInfoById(Long.valueOf(stationId));
        String merchantId = pileStationInfo != null ? String.valueOf(pileStationInfo.getMerchantId()) : "";
        String plateNumber = dto.getPlateNumber() != null ? dto.getPlateNumber() : "";
        // 订单基本信息
        OrderBasicInfo orderBasicInfo = OrderBasicInfo.builder()
                .orderCode(orderCode)
                .transactionCode(transactionCode)
                .orderStatus(OrderStatusEnum.NOT_START.getValue())
                .memberId(dto.getMemberId())
                .stationId(stationId)
                .merchantId(merchantId)
                .pileSn(dto.getPileSn())
                .connectorCode(dto.getConnectorCode())
                .pileConnectorCode(dto.getPileSn() + dto.getConnectorCode())
                .startMode(dto.getStartMode())
                .payStatus(Constants.ZERO)
                .payAmount(dto.getChargeAmount())
                .payMode(dto.getPayMode())
                .plateNumber(plateNumber)
                .orderAmount(BigDecimal.ZERO)
                .virtualAmount(BigDecimal.ZERO)
                .settleAmount(BigDecimal.ZERO)
                .startType(dto.getStartType())
                .build();
        if (StringUtils.equals(dto.getStartMode(), StartModeEnum.AUTH_CARD.getValue())) {
            // 鉴权卡启动
            orderBasicInfo.setLogicCard(dto.getPileAuthCardInfo().getLogicCard());
        }
        if (StringUtils.equals(dto.getStartMode(), StartModeEnum.VIN_CODE.getValue())) {
            // vin启动
            orderBasicInfo.setVinCode(dto.getMemberPlateNumberRelation().getVinCode());
        }

        if (StringUtils.equals(dto.getStartType(), StartTypeEnum.APPOINTMENT.getValue())) {
            orderBasicInfo.setAppointmentTime(DateUtils.parseDate(dto.getAppointmentTime(), DateUtils.YYYY_MM_DD_HH_MM_SS));
        }

        // 订单详情
        BillingTemplateVO billingTemplate = dto.getBillingTemplate();
        logger.info("订单使用的计费模板-orderCode:{}, billingTemplate:{}", orderCode, JSONObject.toJSONString(billingTemplate));
        BigDecimal sharpElectricityPrice = billingTemplate.getSharpElectricityPrice() != null ? billingTemplate.getSharpElectricityPrice() : BigDecimal.ZERO;
        BigDecimal sharpServicePrice = billingTemplate.getSharpServicePrice() != null ? billingTemplate.getSharpServicePrice() : BigDecimal.ZERO;
        BigDecimal peakElectricityPrice = billingTemplate.getPeakElectricityPrice() != null ? billingTemplate.getPeakElectricityPrice() : BigDecimal.ZERO;
        BigDecimal peakServicePrice = billingTemplate.getPeakServicePrice() != null ? billingTemplate.getPeakServicePrice() : BigDecimal.ZERO;
        BigDecimal flatElectricityPrice = billingTemplate.getFlatElectricityPrice() != null ? billingTemplate.getFlatElectricityPrice() : BigDecimal.ZERO;
        BigDecimal flatServicePrice = billingTemplate.getFlatServicePrice() != null ? billingTemplate.getFlatServicePrice() : BigDecimal.ZERO;
        BigDecimal valleyElectricityPrice = billingTemplate.getValleyElectricityPrice() != null ? billingTemplate.getValleyElectricityPrice() : BigDecimal.ZERO;
        BigDecimal valleyServicePrice = billingTemplate.getValleyServicePrice() != null ? billingTemplate.getValleyServicePrice() : BigDecimal.ZERO;

        OrderDetail orderDetail = OrderDetail.builder()
                .orderCode(orderCode)
                .sharpPrice(sharpElectricityPrice.add(sharpServicePrice))
                .sharpElectricityPrice(sharpElectricityPrice)
                .sharpServicePrice(sharpServicePrice)
                .peakPrice(peakElectricityPrice.add(peakServicePrice))
                .peakElectricityPrice(peakElectricityPrice)
                .peakServicePrice(peakServicePrice)
                .flatPrice(flatElectricityPrice.add(flatServicePrice))
                .flatElectricityPrice(flatElectricityPrice)
                .flatServicePrice(flatServicePrice)
                .valleyPrice(valleyElectricityPrice.add(valleyServicePrice))
                .valleyElectricityPrice(valleyElectricityPrice)
                .valleyServicePrice(valleyServicePrice)
                .build();

        OrderTransactionDTO createOrderTransactionDTO = OrderTransactionDTO.builder()
                .orderBasicInfo(orderBasicInfo)
                .orderDetail(orderDetail)
                .build();
        transactionService.doCreateOrder(createOrderTransactionDTO);
        return orderBasicInfo;
    }

    @Override
    public Map<String, Object> payOrder(PayOrderDTO dto) {
        logger.info("【{}】-支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
        OrderBasicInfo orderInfo = orderBasicInfoService.getOrderInfoByOrderCode(dto.getOrderCode());
        if (orderInfo == null) {
            throw new BusinessException(ReturnCodeEnum.CODE_QUERY_ORDER_INFO_IS_NULL);
        }
        if (!StringUtils.equals(orderInfo.getPayStatus(), OrderPayStatusEnum.unpaid.getValue())) {
            // 订单已支付
            throw new BusinessException(ReturnCodeEnum.CODE_ORDER_IS_NOT_TO_BE_PAID_ERROR);
        }
        Map<String, Object> resultMap = Maps.newHashMap();
        if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue())) {
            // 余额支付
            balancePayOrder(dto);
        } else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())) {
            // 2023-07-11 全部改为汇付支付
            dto.setGoodsTitle("充电费用");
            dto.setGoodsDesc("充电桩预付款金额");
            dto.setType(ScenarioEnum.ORDER.getValue());
            Map<String, Object> weixinMap = onlinePaymentOrder(dto);

            // 返回微信支付参数
            resultMap.put("weixinMap", weixinMap);
        } else if (StringUtils.equals(dto.getPayMode(), OrderPayModeEnum.PAYMENT_OF_WHITELIST.getValue())) { // 白名单支付
            // 白名单支付可以直接调支付回调方法
            dto.setPayAmount(new BigDecimal("500"));
            whitelistPaymentOrder(dto);
        }
        return resultMap;
    }

    /**
     * 余额支付订单
     */
    @Transactional(rollbackFor = Exception.class)
    public void balancePayOrder(PayOrderDTO dto) {
        logger.info("【{}】-余额支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
        // 订单编号
        String orderCode = dto.getOrderCode();
        // 支付金额
        BigDecimal chargeAmount = dto.getPayAmount();
        // 查询该会员的余额
        MemberVO memberVO = memberBasicInfoService.queryMemberInfoByMemberId(dto.getMemberId());
        BigDecimal totalAccountAmount = memberVO.getPrincipalBalance();

        if (totalAccountAmount.compareTo(chargeAmount) < 0) {
            // 总余额小于充电金额
            throw new BusinessException(ReturnCodeEnum.CODE_BALANCE_IS_INSUFFICIENT);
        }
        BigDecimal principalPay = chargeAmount;

        // 更新会员钱包
        UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
                .memberId(dto.getMemberId())
                .type(MemberWalletEnum.TYPE_OUT.getValue())
                .subType(MemberWalletEnum.SUBTYPE_PAYMENT_FOR_ORDER.getValue())
                .updatePrincipalBalance(principalPay) // 使用本金支付的金额
                .relatedOrderCode(orderCode)
                .build();
        memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);

        // 查询余额充值有剩余的记录
        List<BalanceDeductionAmountVO> list = calculateTheBalanceDeductionAmount(dto.getMemberId(), chargeAmount);

        // 记录订单支付流水
        BigDecimal payAmt = BigDecimal.ZERO;
        List<PaymentInfo> paymentInfos = Lists.newArrayList();
        for (BalanceDeductionAmountVO balanceDeductionAmountVO : list) {
            String paymentId = balanceDeductionAmountVO.getPaymentId();
            // 此交易单扣除金额
            BigDecimal deductionAmount = balanceDeductionAmountVO.getDeductionAmount();
            payAmt = payAmt.add(deductionAmount);

            PaymentInfo paymentInfo = new PaymentInfo();
            paymentInfo.setPaymentId(paymentId);
            paymentInfo.setAmount(deductionAmount.toString());
            paymentInfos.add(paymentInfo);
        }
        OrderPayRecord build = OrderPayRecord.builder()
                .orderCode(orderCode)
                .payMode(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue())
                .payAmount(payAmt)
                .acquirer(AcquirerEnum.LOCAL.getValue())
                // .deductionRecord(jsonArray.toJSONString())
                .deductionRecord(JSON.toJSONString(paymentInfos))
                .createBy(dto.getMemberId())
                .delFlag(DelFlagEnum.NORMAL.getValue())
                .build();

        // 记录支订单付流水
        List<OrderPayRecord> payRecordList = Lists.newArrayList(build);
        orderPayRecordService.batchInsert(payRecordList);

        // 把消费金额冻结
        for (OrderPayRecord record : payRecordList) {
            List<PaymentInfo> paymentInfoList = parseDeductionRecord(record.getDeductionRecord());
            // 循环冻结金额
            for (PaymentInfo paymentInfo : paymentInfoList) {
                String paymentId = paymentInfo.getPaymentId();
                BigDecimal amount = new BigDecimal(paymentInfo.getAmount());
                // 余额支付 临时冻结金额
                memberAdapayRecordService.updateFreezeAmount(paymentId, amount);
            }
        }

        // 余额支付可以直接调支付回调方法
        PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
                .orderCode(orderCode)
                .payAmount(chargeAmount)
                .payMode(dto.getPayMode())
                .startMode(dto.getStartMode())
                .acquirer(AcquirerEnum.LOCAL.getValue())
                .build();
        payOrderSuccessCallback(callbackDTO);

        // 余额支付订单 记录会员交易流水
        MemberTransactionRecord record = MemberTransactionRecord.builder()
                .orderCode(orderCode)
                .scenarioType(ScenarioEnum.ORDER.getValue())
                .memberId(memberVO.getMemberId())
                .actionType(ActionTypeEnum.FORWARD.getValue())
                .payMode(PayModeEnum.PAYMENT_OF_BALANCE.getValue())
                .paymentInstitutions(PaymentInstitutionsEnum.LOCAL_ACCOUNTS.getValue())
                .amount(dto.getPayAmount()) // 单位元
                .build();
        memberTransactionRecordService.insertSelective(record);
    }

    /**
     * 在线支付订单
     */
    public Map<String, Object> onlinePaymentOrder(PayOrderDTO dto) {
        logger.info("【{}】-在线支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
        Map<String, Object> weixinMap = adapayService.createPayment(dto);
        return weixinMap;
    }

    /**
     * 白名单支付订单
     */
    public void whitelistPaymentOrder(PayOrderDTO dto) {
        logger.info("【{}】-白名单支付订单start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
        String orderCode = dto.getOrderCode();
        BigDecimal payAmount = dto.getPayAmount();
        String payMode = dto.getPayMode();

        // 白名单直接算支付成功
        PayOrderSuccessCallbackDTO callbackDTO = PayOrderSuccessCallbackDTO.builder()
                .orderCode(orderCode)
                .payAmount(payAmount)
                .payMode(payMode)
                .startMode(dto.getStartMode())
                .acquirer(AcquirerEnum.LOCAL.getValue())
                .build();
        payOrderSuccessCallback(callbackDTO);
    }

    /**
     * 订单结算
     */
    @Override
    public void settleOrder(TransactionRecordsData data, OrderBasicInfo orderBasicInfo) {
        logger.info("【{}】-结算订单start data:{}, orderBasicInfo:{}", this.getClass().getSimpleName(), data.toString(), JSON.toJSONString(orderBasicInfo));
        // 判断订单状态
        if (StringUtils.equals(orderBasicInfo.getOrderStatus(), OrderStatusEnum.ORDER_COMPLETE.getValue())) {
            logger.info("结算订单:{}, 是订单完成状态", orderBasicInfo.getOrderCode());
            return;
        }
        // 获取更新数据后的orderBasicInfo对象
        returnUpdateOrderBasicInfo(orderBasicInfo, data);
        // 获取更新数据后的orderDetail对象/更新订单详情 查询订单详情 修改订单数据
        OrderDetail orderDetail = returnUpdateOrderDetail(orderBasicInfo, data);
        // 更新数据库
        OrderTransactionDTO dto = new OrderTransactionDTO();
        dto.setOrderBasicInfo(orderBasicInfo);
        dto.setOrderDetail(orderDetail);
        transactionService.doUpdateOrder(dto);

        // 订单退款
        refundOrder(orderBasicInfo);

        // 将卡/vin状态解锁
        if (!StringUtils.equals("0000000000000000", data.getLogicCard())) {
            cardStatusUnlocked(orderBasicInfo.getLogicCard());
        }

        // 如果是vin启动，将启动锁定状态改为正常
        if (StringUtils.equals(data.getTransactionIdentifier(), "05")) {
            vinStatusUnlocked(data.getVinCode());
        }

        // 发送停止充电订阅消息
        sendMsg(orderBasicInfo);

        // 从redis中取出实时记录保存到表中
        realTimeMonitorDataRedis2DB(orderBasicInfo.getTransactionCode(), orderBasicInfo.getOrderCode());

        logger.info("结算订单end:{} OrderTransactionDTO:{}", orderBasicInfo.getOrderCode(), JSONObject.toJSONString(dto));
    }

    // uniApp 发送停止充电订阅消息
    private void sendMsg(OrderBasicInfo orderBasicInfo) {
        try {
            WechatSendMsgDTO wechatSendMsgDTO = new WechatSendMsgDTO();
            wechatSendMsgDTO.setOrderCode(orderBasicInfo.getOrderCode());
            Map<String, String> resultMap = wxAppletRemoteService.stopChargingSendMsg(wechatSendMsgDTO);
            logger.info("小程序发送充电停止推送消息 result:{}", JSON.toJSONString(resultMap));
        } catch (Exception e) {
            logger.error("小程序发送充电停止推送消息 error", e);
        }
    }

    @Override
    public void refundOrder(OrderBasicInfo orderBasicInfo) {
        try {
            String payMode = orderBasicInfo.getPayMode();
            if (StringUtils.equals(payMode, OrderPayModeEnum.PAYMENT_OF_BALANCE.getValue())) {
                // 余额支付
                balancePaymentOrderRefund(orderBasicInfo);
            } else if (StringUtils.equals(payMode, OrderPayModeEnum.PAYMENT_OF_WECHATPAY.getValue())) {
                // 微信支付
                onlinePaymentOrderRefund(orderBasicInfo);
            } else {
                // 白名单支付
                logger.info("订单:{}使用白名单支付，不进行退款处理", orderBasicInfo.getOrderCode());
            }
        } catch (Exception e) {
            logger.error("【{}】-订单退款逻辑异常orderCode:{}", this.getClass().getSimpleName(), orderBasicInfo.getOrderCode(), e);
        }
    }

    /**
     * 余额充值
     *
     * @param dto
     */
    @Override
    public Map<String, Object> rechargeBalance(WeixinPayDTO dto) {
        logger.info("【{}】-余额充值start dto:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
        // 获取支付配置
        AbstractAdapayConfig config = AdapayConfigFactory.getConfig(dto.getWechatAppId());
        if (config == null) {
            throw new BusinessException(ReturnCodeEnum.CODE_ADAPAY_CONFIG_IS_NULL_ERROR);
        }
        logger.info("使用汇付支付充值余额 支付配置参数:{}", JSON.toJSONString(config));

        String goodsTitle = "充值余额";
        String goodsDesc = "会员充值余额";

        // 查询延时支付模式，由一级运营商配置决定
        String delayMode = pileMerchantInfoService.getDelayModeByWechatAppId(dto.getWechatAppId());
        String payMode = MerchantDelayModeEnum.getAdapayPayMode(delayMode);
        // 封装对象
        // String payMode = Constants.ADAPAY_PAY_MODE_DELAY; // 汇付延时分账
        CreateAdaPaymentParam createAdaPaymentParam = new CreateAdaPaymentParam();
        createAdaPaymentParam.setOrder_no(SnowflakeIdWorker.getSnowflakeId());
        createAdaPaymentParam.setPay_amt(AdapayUtil.formatAmount(dto.getAmount()));
        // createAdaPaymentParam.setApp_id(ADAPAY_APP_ID); // 移动到配置文件中
        createAdaPaymentParam.setApp_id(config.getAdapayAppId()); // 移动到配置文件中
        createAdaPaymentParam.setPay_channel("wx_lite"); // todo 如果以后有支付宝等别的渠道，这里需要做修改，判断是什么渠道的请求
        createAdaPaymentParam.setGoods_title(goodsTitle);
        createAdaPaymentParam.setGoods_desc(goodsDesc);
        createAdaPaymentParam.setExpend(JSONObject.toJSONString(ImmutableMap.of("open_id", dto.getOpenId())));
        // 异步通知地址，url为http/https路径，服务器POST回调，URL 上请勿附带参数
        createAdaPaymentParam.setNotify_url(ADAPAY_CALLBACK_URL);
        Map<String, String> map = Maps.newHashMap();
        map.put("type", ScenarioEnum.BALANCE.getValue());
        map.put("payMode", payMode);
        map.put("memberId", dto.getMemberId());
        createAdaPaymentParam.setDescription(JSON.toJSONString(map));
        // 延时分账
        if (StringUtils.isNotBlank(payMode)) {
            createAdaPaymentParam.setPay_mode(payMode);
        }
        try {
            logger.info("创建汇付支付参数:{}", JSONObject.toJSONString(createAdaPaymentParam));
            Map<String, Object> response = Payment.create(BeanMap.create(createAdaPaymentParam), config.getWechatAppId());
            logger.info("创建汇付支付参数反参:{}", JSONObject.toJSONString(response));
            if (response != null && !response.isEmpty()) {
                JSONObject jsonObject = JSONObject.parseObject(response.get("expend").toString());
                JSONObject pay_info = jsonObject.getJSONObject("pay_info");
                return JSONObject.parseObject(pay_info.toJSONString(), new TypeReference<Map<String, Object>>() {
                });
            }
        } catch (BaseAdaPayException e) {
            logger.error("汇付-获取支付对象发生异常", e);
        }
        return null;
    }

    /**
     * 余额退款
     *
     * @param dto
     */
    @Override
    public void refundBalance(ApplyRefundDTO dto) {
        logger.info("【{}】-余额退款start dto:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
        // 做个拦截，如果前一笔退款没有完成，就抛异常
        String redisKey = CacheConstants.MEMBER_BALANCE_REFUNDS_ARE_IN_PROGRESS + dto.getMemberId();
        PaymentReverseResponse redisResult = redisCache.getCacheObject(redisKey);
        if (Objects.nonNull(redisResult)) {
            throw new BusinessException(ReturnCodeEnum.CODE_BALANCE_REFUNDS_ARE_IN_PROGRESS_ERROR);
        }

        // 查会员余额
        MemberVO memberVO = memberBasicInfoService.queryMemberInfoByMemberId(dto.getMemberId());
        if (memberVO == null) {
            throw new BusinessException(ReturnCodeEnum.CODE_MEMBER_NOT_FOUND_ERROR);
        }

        // 校验退款金额
        BigDecimal principalBalance = memberVO.getPrincipalBalance();
        BigDecimal refundAmount = dto.getRefundAmount();
        if (refundAmount.compareTo(principalBalance) > 0) {
            throw new BusinessException(ReturnCodeEnum.CODE_REFUND_MEMBER_BALANCE_ERROR);
        }

        // 查询用户充值余额订单 过滤掉已经退款的充值订单
        List<BalanceDeductionAmountVO> list = calculateTheBalanceDeductionAmount(dto.getMemberId(), refundAmount);
        for (BalanceDeductionAmountVO vo : list) {
            String paymentId = vo.getPaymentId();
            BigDecimal deductionAmount = vo.getDeductionAmount();

            // 调汇付的交易撤销接口
            PaymentReverseResponse request = adapayService.createPaymentReverseRequest(paymentId, deductionAmount,
                    dto.getWechatAppId(), dto.getMemberId(), ScenarioEnum.BALANCE.getValue(), null);

            if (request != null && request.isNotFailed()) {
                memberAdapayRecordService.updateRefundAmount(paymentId, deductionAmount);

                // 放缓存
                redisCache.setCacheObject(redisKey, request, CacheConstants.cache_expire_time_30m);
            }
        }
    }

    /**
     * 余额支付订单退款
     */
    @Transactional(rollbackFor = Exception.class)
    public void balancePaymentOrderRefund(OrderBasicInfo orderBasicInfo) {
        logger.info("【{}】-余额支付订单退款start orderBasicInfo:{}", this.getClass().getSimpleName(), JSON.toJSONString(orderBasicInfo));
        // 订单编号
        String orderCode = orderBasicInfo.getOrderCode();
        // 订单消费金额
        BigDecimal orderAmount = orderBasicInfo.getOrderAmount();
        // 查支付记录
        List<OrderPayRecord> payRecordList = orderPayRecordService.getOrderPayRecordList(orderCode);
        // 根据payMode分组
        Map<String, List<OrderPayRecord>> payRecordMap = payRecordList.stream().collect(Collectors.groupingBy(OrderPayRecord::getPayMode));
        // 获取本金支付的记录
        List<OrderPayRecord> orderPayRecordList = payRecordMap.get(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue());
        BigDecimal principalPay = orderPayRecordList.stream().map(OrderPayRecord::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

        // 计算需要退回的金额
        Map<String, BigDecimal> returnAmountMap = calculateReturnAmount(principalPay, null, orderAmount);
        logger.info("结算订单:{}, 剩余金额退回余额, 订单消费金额:{}, 本金支付金额:{}, 赠送支付金额:{}, 退回金额map:{}",
                orderCode, orderAmount, principalPay, null, JSONObject.toJSONString(returnAmountMap));

        // 需要退回本金的金额
        BigDecimal returnPrincipal = returnAmountMap.get("returnPrincipal");

        // 更新会员钱包/余额退回到钱包
        UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
                .memberId(orderBasicInfo.getMemberId())
                .type(MemberWalletEnum.TYPE_IN.getValue()) // 进账
                .subType(MemberWalletEnum.SUBTYPE_ORDER_SETTLEMENT_REFUND.getValue()) // 订单结算退款
                .updatePrincipalBalance(returnPrincipal)
                .relatedOrderCode(orderCode)
                .build();
        memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);

        // 更新order_pay_record, 解冻部分
        List<Map<String, Object>> list = calculateUnfreezeAmount(orderAmount, payRecordList);
        for (Map<String, Object> map : list) {
            String paymentId = (String) map.get("paymentId");
            BigDecimal unfreezeAmount = (BigDecimal) map.get("unfreezeAmount");
            memberAdapayRecordService.unfreezeAmount(paymentId, unfreezeAmount);
        }
    }

    /**
     * 在线支付订单退款
     */
    private void onlinePaymentOrderRefund(OrderBasicInfo orderBasicInfo) {
        logger.info("【{}】-在线支付订单退款start orderBasicInfo:{}", this.getClass().getSimpleName(), JSON.toJSONString(orderBasicInfo));
        // 订单编号
        String orderCode = orderBasicInfo.getOrderCode();

        // 需要退款的金额
        BigDecimal refundAmount = orderBasicInfo.getRefundAmount();

        // 微信退款逻辑
        ApplyRefundDTO applyRefundDTO = new ApplyRefundDTO();
        applyRefundDTO.setOrderCode(orderCode);
        applyRefundDTO.setRefundType(Constants.ONE);
        applyRefundDTO.setRefundAmount(refundAmount);

        // 汇付退款需要一级运营商的小程序appId, 否则会退款失败
        String wechatAppId = pileMerchantInfoService.queryAppIdByMerchantId(orderBasicInfo.getMerchantId());
        if (StringUtils.isNotBlank(wechatAppId)) {
            applyRefundDTO.setWechatAppId(wechatAppId);
        }
        this.refundOrderWithAdapay(applyRefundDTO);
    }

    /**
     * 汇付支付-订单退款处理逻辑
     * 汇付支付订单退款
     *
     * @param dto
     */
    private void refundOrderWithAdapay(ApplyRefundDTO dto) {
        logger.info("【{}】-汇付支付订单:{}, 执行退款逻辑 param:{}", this.getClass().getSimpleName(), dto.getOrderCode(), JSON.toJSONString(dto));
        // 查出来原来的支付信息
        AdapayCallbackRecord callbackRecord = adapayCallbackRecordService.selectByOrderCode(dto.getOrderCode());
        if (Objects.isNull(callbackRecord)) {
            logger.error("汇付支付orderCode:{}, 订单退款处理逻辑, 查询订单微信支付记录为空!", dto.getOrderCode());
            throw new BusinessException(ReturnCodeEnum.CODE_REFUND_ORDER_CALLBACK_RECORD_ERROR);
        }

        String paymentId = callbackRecord.getPaymentId();
        // 判断支付金额和退款金额
        BigDecimal refundAmount = dto.getRefundAmount();
        BigDecimal payAmt = callbackRecord.getPayAmt();
        if (refundAmount.compareTo(payAmt) > 0) {
            logger.error("汇付支付订单号:{}, 退款金额:{}(元)，大于付款金额{}(元), 抛出异常", dto.getOrderCode(), refundAmount, payAmt);
            throw new BusinessException(ReturnCodeEnum.CODE_REFUND_ORDER_AMOUNT_ERROR);
        }

        // BigDecimal refundAmt = null; // 交易退款金额
        // 延迟分账未确认调撤销调撤销接口退款
        // PaymentReverseOperation operation = new PaymentReverseOperation();
        // operation.setPaymentId(paymentId);
        // operation.setReverseAmt(refundAmount);
        // operation.setMerchantKey(dto.getWechatAppId());
        // operation.setMemberId(dto.getMemberId());
        // operation.setScenarioType(ScenarioEnum.ORDER.getValue());
        // operation.setOrderCode(dto.getOrderCode());
        // PaymentReverseResponse response = adapayService.createPaymentReverseRequest(operation);
        PaymentReverseResponse response = adapayService.createPaymentReverseRequest(paymentId, refundAmount,
                dto.getWechatAppId(), dto.getMemberId(), ScenarioEnum.ORDER.getValue(), dto.getOrderCode());
        if (response != null && response.isNotFailed()) {
            // 交易退款金额
            BigDecimal refundAmt = new BigDecimal(response.getReverse_amt());
            memberAdapayRecordService.updateRefundAmountFromFreezeAmount(paymentId, refundAmt);
        }
    }
}
