package com.jsowell.pile.service.programlogic;

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.common.RefundInfo;
import com.jsowell.adapay.config.AbstractAdapayConfig;
import com.jsowell.adapay.factory.AdapayConfigFactory;
import com.jsowell.adapay.response.RefundResponse;
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.SnowflakeIdWorker;
import com.jsowell.pile.domain.*;
import com.jsowell.pile.dto.*;
import com.jsowell.pile.transaction.dto.OrderTransactionDTO;
import com.jsowell.pile.vo.uniapp.customer.MemberVO;
import com.jsowell.pile.vo.web.BalanceDeductionAmountVO;
import com.jsowell.pile.vo.web.OrderDetailInfoVO;
import com.jsowell.pile.vo.web.UpdateMemberBalanceDTO;
import com.jsowell.wxpay.dto.WechatSendMsgDTO;
import org.apache.commons.collections4.CollectionUtils;
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.function.Function;
import java.util.stream.Collectors;

/**
 * 没有设置延时分账的运营商订单逻辑
 */
@Service
public class NotDelayMerchantProgramLogic extends AbstractProgramLogic {

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

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

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

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

    @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.paid.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())) {
            // 余额支付
            balancePayOrderV2(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(Constants.WHITELIST_DEFAULT_AMOUNT);
            whitelistPaymentOrder(dto);
        }
        return resultMap;
    }

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


    /**
     * 余额支付订单
     *
     * @param dto
     */
    @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);
        // 记录支订单付流水
        List<PaymentInfo> paymentInfos = Lists.newArrayList();
        BigDecimal deductionAmount = BigDecimal.ZERO;
        for (BalanceDeductionAmountVO balanceDeductionAmountVO : list) {
            String paymentId = balanceDeductionAmountVO.getPaymentId();
            deductionAmount = deductionAmount.add(balanceDeductionAmountVO.getDeductionAmount());

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

        // 订单支付流水入库
        List<OrderPayRecord> payRecordList = Lists.newArrayList(orderPayRecord);
        orderPayRecordService.batchInsert(payRecordList);

        // 把消费金额冻结
        for (OrderPayRecord record : payRecordList) {
            List<PaymentInfo> paymentInfoList = orderPayRecordService.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);
    }

    /**
     * 余额支付订单逻辑
     * 使用的是通过后管充值的余额
     * 包括赠送金
     */
    @Transactional(rollbackFor = Exception.class)
    public void balancePayOrderV2(PayOrderDTO dto) {
        logger.info("【{}】-余额支付订单V2start, param:{}", this.getClass().getSimpleName(), JSON.toJSONString(dto));
        // 订单编号
        String orderCode = dto.getOrderCode();
        // 查订单详情
        OrderBasicInfo orderBasicInfo = orderBasicInfoService.getOrderInfoByOrderCode(orderCode);
        // 订单所属运营商id
        String merchantId = orderBasicInfo.getMerchantId();
        // 支付金额
        BigDecimal chargeAmount = dto.getPayAmount();
        // 会员id
        String memberId = dto.getMemberId();
        // 查询该会员的余额 在所属运营商的余额
        MemberVO memberVO = memberBasicInfoService.queryMemberInfoByMemberId(memberId, merchantId);
        // 总余额
        BigDecimal totalAccountAmount = memberVO.getTotalBalance();
        if (totalAccountAmount.compareTo(chargeAmount) < 0) {
            // 总余额小于充电金额
            throw new BusinessException(ReturnCodeEnum.CODE_BALANCE_IS_INSUFFICIENT);
        }

        // 本金支付金额
        BigDecimal principalBalancePay = BigDecimal.ZERO;
        // 赠送金支付金额
        BigDecimal giftBalancePay = BigDecimal.ZERO;
        // 计算下发金额
        Map<String, BigDecimal> stringBigDecimalMap = calculateTheAmount(memberVO);
        if (stringBigDecimalMap != null) {
            principalBalancePay = stringBigDecimalMap.get("principalBalancePay");
            giftBalancePay = stringBigDecimalMap.get("giftBalancePay");
        }

        // 更新会员钱包 全部金额都用于支付订单
        UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
                .memberId(memberId)
                .targetMerchantId(merchantId)
                .type(MemberWalletEnum.TYPE_OUT.getValue())
                .subType(MemberWalletEnum.SUBTYPE_PAYMENT_FOR_ORDER.getValue())
                .updatePrincipalBalance(principalBalancePay) // 使用本金支付的金额
                .updateGiftBalance(giftBalancePay) // 赠送金额支付
                .relatedOrderCode(orderCode)
                .build();
        memberBasicInfoService.updateMemberBalance(updateMemberBalanceDTO);

        // 本金支付
        OrderPayRecord principalPayRecord = null;
        if (principalBalancePay.compareTo(BigDecimal.ZERO) > 0) {
            principalPayRecord = OrderPayRecord.builder()
                    .orderCode(orderCode)
                    .payMode(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue())
                    .payAmount(principalBalancePay)
                    .acquirer(AcquirerEnum.LOCAL.getValue())
                    .createBy(memberId)
                    .createTime(DateUtils.getNowDate())
                    .delFlag(DelFlagEnum.NORMAL.getValue())
                    .build();
        }

        // 赠送金支付
        OrderPayRecord giftPayRecord = null;
        if (giftBalancePay.compareTo(BigDecimal.ZERO) > 0) {
            giftPayRecord = OrderPayRecord.builder()
                    .orderCode(orderCode)
                    .payMode(OrderPayRecordEnum.GIFT_BALANCE_PAYMENT.getValue())
                    .payAmount(giftBalancePay)
                    .acquirer(AcquirerEnum.LOCAL.getValue())
                    .createBy(memberId)
                    .createTime(DateUtils.getNowDate())
                    .delFlag(DelFlagEnum.NORMAL.getValue())
                    .build();
        }

        // 记录支订单付流水 记录两条支付
        List<OrderPayRecord> payRecordList = Lists.newArrayList(principalPayRecord, giftPayRecord);
        orderPayRecordService.batchInsert(payRecordList);

        // 余额支付可以直接调支付回调方法
        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(memberId)
                .actionType(ActionTypeEnum.FORWARD.getValue())
                .payMode(PayModeEnum.PAYMENT_OF_BALANCE.getValue())
                .paymentInstitutions(PaymentInstitutionsEnum.LOCAL_ACCOUNTS.getValue())
                .amount(dto.getPayAmount()) // 单位元
                .build();
        memberTransactionRecordService.insertSelective(record);
    }

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

    /**
     * 白名单支付订单
     *
     * @param dto
     */
    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();

        // 白名单支付支付
        OrderPayRecord principalPayRecord = OrderPayRecord.builder()
                .orderCode(orderCode)
                .payMode(OrderPayRecordEnum.WHITELIST_PAYMENT.getValue())
                .payAmount(payAmount)
                .acquirer(AcquirerEnum.LOCAL.getValue())
                .createBy(dto.getMemberId())
                .createTime(DateUtils.getNowDate())
                .delFlag(DelFlagEnum.NORMAL.getValue())
                .build();
        orderPayRecordService.batchInsert(Lists.newArrayList(principalPayRecord));

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

    /**
     * 订单结算/结算订单逻辑/订单结算逻辑
     * @param data
     * @param orderBasicInfo
     */
    @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());

        // TODO 如果该站点的停车场优惠券信息配置不为空，则需绑定一张优惠券

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

    /**
     * 第三方平台结算订单（目前给华为用）
     * @param data
     * @param orderBasicInfo
     */
    @Override
    public void settleOrderForThirdParty(TransactionRecordsData data, OrderBasicInfo orderBasicInfo) {
        logger.info("第三方平台结算订单start data:{}, orderBasicInfo:{}", 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 = returnUpdateOrderDetailForThirdParty(orderBasicInfo, data);

        // 计算订单折扣
        // calculateOrderDiscountsV2(orderBasicInfo, orderDetail);

        // 更新数据库
        OrderTransactionDTO dto = new OrderTransactionDTO();
        dto.setOrderBasicInfo(orderBasicInfo);
        dto.setOrderDetail(orderDetail);
        transactionService.doUpdateOrder(dto);

        // 订单退款
        refundOrder(orderBasicInfo);

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

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

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

    @Override
    public void refundOrder(OrderBasicInfo orderBasicInfo) {
        BigDecimal refundAmount = orderBasicInfo.getRefundAmount();
        // 查询该笔订单的退款记录
        List<OrderDetailInfoVO.OrderRefundInfo> orderRefundInfoList = orderBasicInfoService.getOrderRefundInfoList(orderBasicInfo);
        if (CollectionUtils.isNotEmpty(orderRefundInfoList)) {
            // 已退款金额
            BigDecimal refunded = orderRefundInfoList.stream()
                    .map(x -> Objects.nonNull(x.getReverseAmt()) ? new BigDecimal(x.getReverseAmt()) : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO,BigDecimal::add);
            if (refunded.compareTo(refundAmount) >= 0) {
                logger.info("执行订单退款-校验已退款金额，orderCode:{}, 应退款金额:{}, 已经退款金额:{}, 不再执行退款", orderBasicInfo.getOrderCode(), refundAmount, refunded);
                return;
            }
        }

        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 {
                // 白名单支付或者ETC支付
                logger.info("订单:{}使用:{}，不进行退款处理", orderBasicInfo.getOrderCode(), OrderPayModeEnum.getPayModeDescription(payMode));
            }
        } 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(JSON.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));
        try {
            logger.info("创建汇付支付参数:{}", JSON.toJSONString(createAdaPaymentParam));
            Map<String, Object> response = Payment.create(BeanMap.create(createAdaPaymentParam), config.getWechatAppId());
            logger.info("创建汇付支付参数反参:{}", JSON.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();
        RefundResponse 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);
        // }

        MemberWalletInfo memberWalletInfo = memberWalletInfoService.selectByWalletCode(dto.getWalletCode());
        if (memberWalletInfo == null) {
            throw new BusinessException(ReturnCodeEnum.CODE_WALLET_NOT_FOUND_ERROR);
        }

        // 校验退款金额
        BigDecimal principalBalance = memberWalletInfo.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);
        logger.info("【{}】-余额退款, 需要退款list:{}", this.getClass().getSimpleName(), JSON.toJSONString(list));
        for (BalanceDeductionAmountVO vo : list) {
            String paymentId = vo.getPaymentId();
            BigDecimal deductionAmount = vo.getDeductionAmount();

            // 调汇付的交易退款接口
            RefundResponse response = adapayService.createRefundRequest(paymentId, deductionAmount,
                    dto.getWechatAppId(), dto.getMemberId(), ScenarioEnum.BALANCE.getValue(), dto.getOrderCode());

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

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

    @Override
    public List<OrderDetailInfoVO.OrderRefundInfo> getOrderRefundInfoList(OrderBasicInfo orderBasicInfo) {
        List<OrderDetailInfoVO.OrderRefundInfo> resultList = Lists.newArrayList();

        // 查到原汇付支付id
        // AdapayCallbackRecord adapayCallbackRecord = adapayCallbackRecordService.selectByOrderCode(orderBasicInfo.getOrderCode());
        // if (adapayCallbackRecord == null) {
        //     return resultList;
        // }

        List<OrderPayRecord> orderPayRecordList = orderPayRecordService.getOrderPayRecordList(orderBasicInfo.getOrderCode());
        if (CollectionUtils.isEmpty(orderPayRecordList)) {
            return resultList;
        }
        List<String> paymentIdList = Lists.newArrayList();
        for (OrderPayRecord orderPayRecord : orderPayRecordList) {
            String payMode = orderPayRecord.getPayMode();
            if (StringUtils.equals(payMode, OrderPayRecordEnum.WECHATPAY_PAYMENT.getValue())
                    || StringUtils.equals(payMode, OrderPayRecordEnum.ALIPAY_PAYMENT.getValue())) {
                List<String> idList = orderPayRecordService.parsePaymentIdByDeductionRecord(orderPayRecord.getDeductionRecord());
                paymentIdList.addAll(idList);
            }
        }

        // 获取appId
        String wechatAppId = pileMerchantInfoService.queryAppIdByMerchantId(orderBasicInfo.getMerchantId());

        // 通过支付id查询退款记录
        List<RefundInfo> refundInfoList = Lists.newArrayList();
        for (String paymentId : paymentIdList) {
            try {
                List<RefundInfo> refundInfos = adapayService.queryPaymentRefund(paymentId, wechatAppId);
                if (CollectionUtils.isNotEmpty(refundInfos)) {
                    refundInfoList.addAll(refundInfos);
                }
            } catch (BaseAdaPayException e) {
                logger.error("查询支付退款对象发生异常", e);
            }
        }
        // String paymentId = adapayCallbackRecord.getPaymentId();
        // try {
        //     refundInfoList = adapayService.queryPaymentRefund(paymentId, wechatAppId);
        // } catch (BaseAdaPayException e) {
        //     logger.error("查询支付退款对象发生异常", e);
        // }

        OrderDetailInfoVO.OrderRefundInfo refundInfo;
        if (CollectionUtils.isNotEmpty(refundInfoList)) {
            for (RefundInfo refund : refundInfoList) {
                refundInfo = new OrderDetailInfoVO.OrderRefundInfo();
                refundInfo.setReverseId(refund.getRefund_id());
                refundInfo.setPaymentId(refund.getPayment_id());
                refundInfo.setReverseAmt(refund.getRefund_amt());
                // LocalDateTime createdTime = DateUtils.timestampToDatetime(Long.parseLong(refund.getCreated_time()));
                // refundInfo.setCreatedTime(DateUtils.formatDateTime(createdTime));
                // LocalDateTime succeedTime = DateUtils.timestampToDatetime(Long.parseLong(refund.getSucceed_time()));
                // refundInfo.setSucceedTime(DateUtils.formatDateTime(succeedTime));
                resultList.add(refundInfo);
            }
        }
        return resultList;
    }

    // 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:{}", this.getClass().getSimpleName(), JSON.toJSONString(resultMap));
        } catch (Exception e) {
            logger.error("【{}】-小程序发送充电停止推送消息 error", this.getClass().getSimpleName(), e);
        }
    }

    /**
     * 余额支付订单退款
     *
     * @param orderBasicInfo
     */
    @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> principalPayRecordList = payRecordMap.get(OrderPayRecordEnum.PRINCIPAL_BALANCE_PAYMENT.getValue());
        BigDecimal principalPay = null;
        if (CollectionUtils.isNotEmpty(principalPayRecordList)) {
            principalPay = principalPayRecordList.stream().map(OrderPayRecord::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        // 获取赠送金支付的记录
        List<OrderPayRecord> giftPayRecordList = payRecordMap.get(OrderPayRecordEnum.GIFT_BALANCE_PAYMENT.getValue());
        BigDecimal giftPay = null;
        if (CollectionUtils.isNotEmpty(giftPayRecordList)) {
            giftPay = giftPayRecordList.stream().map(OrderPayRecord::getPayAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        }

        BigDecimal discountAmount = orderBasicInfo.getDiscountAmount() == null ? BigDecimal.ZERO : orderBasicInfo.getDiscountAmount();

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

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

        // 更新会员钱包/余额退回到钱包
        UpdateMemberBalanceDTO updateMemberBalanceDTO = UpdateMemberBalanceDTO.builder()
                .memberId(orderBasicInfo.getMemberId())
                .type(MemberWalletEnum.TYPE_IN.getValue()) // 进账
                .subType(MemberWalletEnum.SUBTYPE_ORDER_SETTLEMENT_REFUND.getValue()) // 订单结算退款
                .updatePrincipalBalance(returnPrincipal)
                .updateGiftBalance(returnGift)
                .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);
        }
    }

    /**
     * 在线支付订单退款
     *
     * @param orderBasicInfo
     */
    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);
        }
        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);
        }

        // 实时分账的调退款接口
        RefundResponse refundRequest = adapayService.createRefundRequest(paymentId, refundAmount,
                dto.getWechatAppId(), dto.getMemberId(), ScenarioEnum.ORDER.getValue(), dto.getOrderCode());
        if (refundRequest != null && refundRequest.isNotFailed()) {
            // 交易退款金额
            BigDecimal refundAmt = new BigDecimal(refundRequest.getRefund_amt());
            // 解冻金额并更新消费金额
            memberAdapayRecordService.unfreezeAmountAndUpdateSpendAmount(paymentId, payAmt, refundAmt);
        }
        logger.info("【{}】-汇付支付订单:{}, 执行退款逻辑 param:{}, result:{}", this.getClass().getSimpleName(), dto.getOrderCode(), JSON.toJSONString(dto), JSON.toJSONString(refundRequest));
    }
}
