
package com.tmerclub.cloud.payment.feign;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.tmerclub.cloud.api.auth.constant.SysTypeEnum;
import com.tmerclub.cloud.api.order.vo.ShopAmountVO;
import com.tmerclub.cloud.api.payment.constant.PaySysType;
import com.tmerclub.cloud.api.payment.dto.AllinpaySettlementApiDto;
import com.tmerclub.cloud.api.payment.dto.OrderPayInfoDTO;
import com.tmerclub.cloud.api.payment.dto.PayInfoApiDto;
import com.tmerclub.cloud.api.payment.feign.PaymentFeignClient;
import com.tmerclub.cloud.api.payment.vo.SplitRuleVO;
import com.tmerclub.cloud.api.platform.feign.ConfigFeignClient;
import com.tmerclub.cloud.common.bean.Allinpay;
import com.tmerclub.cloud.common.constant.AllinpayConstant;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.constant.PayType;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.rocketmq.config.RocketMqConstant;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.Json;
import com.tmerclub.cloud.payment.allinpay.order.req.AgentPay;
import com.tmerclub.cloud.payment.allinpay.order.req.BatchAgentPay;
import com.tmerclub.cloud.payment.allinpay.order.req.SplitRule;
import com.tmerclub.cloud.payment.bo.PayInfoBO;
import com.tmerclub.cloud.payment.bo.PayInfoResultBO;
import com.tmerclub.cloud.payment.bo.RefundInfoResultBO;
import com.tmerclub.cloud.payment.constant.DistributedIdKey;
import com.tmerclub.cloud.payment.constant.PayEntry;
import com.tmerclub.cloud.payment.constant.PayStatus;
import com.tmerclub.cloud.payment.manager.AllinpayManager;
import com.tmerclub.cloud.payment.manager.PayManager;
import com.tmerclub.cloud.payment.manager.PayNoticeManager;
import com.tmerclub.cloud.payment.mapper.PayInfoMapper;
import com.tmerclub.cloud.payment.mapper.RefundInfoMapper;
import com.tmerclub.cloud.payment.model.PayInfo;
import com.tmerclub.cloud.payment.model.RefundInfo;
import com.tmerclub.cloud.payment.service.RefundInfoService;
import com.tmerclub.cloud.payment.vo.RefundInfoVO;
import jakarta.validation.constraints.NotNull;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author YXF
 * @Date 2022/08/10 9:21
 */
@DubboService
public class PaymentFeignController implements PaymentFeignClient {

    private static final Logger log = LoggerFactory.getLogger(PaymentFeignController.class);

    @Autowired
    private PayManager payManager;
    @Autowired
    private PayNoticeManager payNoticeManager;
    @Autowired
    private PayInfoMapper payInfoMapper;
    @Autowired
    private RefundInfoMapper refundInfoMapper;
    @Autowired
    private AllinpayManager allinpayManager;
    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private AllinpayManager allinpayPayManager;
    @DubboReference
    private ConfigFeignClient configFeignClient;
    @Autowired
    private RefundInfoService refundInfoService;

    @Value("${application.domainUrl}")
    private String domainUrl;

    @Autowired
    private RocketMQTemplate platformTransferTemplate;


    /**
     * 情况一、 只支付了一次，且已支付： 调用订单支付成功处理，调用失败就打印日志，等待下一次调用
     * 情况二、 两条支付记录，一条已支付，一条未支付： 无论执行顺序怎样，未支付的记录不会处理，已支付的进行订单支付成功处理
     * 情况三、 三条支付记录，两条已支付，一次未支付： 未支付的记录不会处理，已支付的记录按创建时间进行订单支付成功处理，存在以下几种情况：
     * 1.第一条已支付记录处理后，订单状态变更为待发货，第二条已支付记录就会进行退款
     * 2.第一条已支付记录处理异常（服务出问题了，应该等待服务恢复后再处理），打印错误日志不再进行后续处理
     *
     * @param orderPayInfoDTO 订单id列表
     * @return
     */
    @Override
    public List<Long> queryAndHandleOrderPaySuccess(OrderPayInfoDTO orderPayInfoDTO) {
        List<Long> orderIdList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (CollUtil.isEmpty(orderPayInfoDTO.getOrderIds())) {
            return orderIdList;
        }
        // 查询订单已经支付记录
        List<PayInfo> payInfoList = payInfoMapper.listPayedInfoByOrderIds(orderPayInfoDTO.getOrderIds(), orderPayInfoDTO.getUserId(), PayStatus.PAYED.value());

        if (CollUtil.isEmpty(payInfoList)) {
            return orderIdList;
        }

        for (PayInfo payInfo : payInfoList) {
            // 第三方平台查询已支付成功的订单
            PayInfoResultBO payInfoResultBO = payManager.checkPaySuccess(payInfo);

            // 没有支付
            if (Objects.equals(payInfoResultBO.getPaySuccess(), Boolean.FALSE)) {
                continue;
            }
            // 已支付成功或查询支付接口异常，订单id先添加到支付成功的id列表中，优先保证订单不会被取消
            String[] orderIdArray = payInfo.getOrderIds().split(Constant.COMMA);
            for (String s : orderIdArray) {
                orderIdList.add(Long.valueOf(s));
            }
            //  查询支付接口异常就不能进行后续处理了
            if (Objects.isNull(payInfoResultBO.getPaySuccess())) {
                continue;
            }
            // 处理已支付成功，但订单状态还是待支付的订单
            payInfoResultBO.setCallbackContent("取消订单时-支付成功");
            // 服务出问题了等待服务恢复后再处理，先打印错误日志不再进行后续处理，等待下一场处理
            ResponseEntity<String> payNoticeResponseEntity = payNoticeManager.noticeOrder(payInfoResultBO, payInfo, payInfo.getPaySysType());
            if (Objects.equals(payNoticeResponseEntity.getBody(), ResponseEnum.EXCEPTION.value())) {
                log.error("取消订单的订单成功回调处理失败，{}", payInfoResultBO);
                break;
            }
        }
        return orderIdList;
    }



    @Override
    public ServerResponseEntity<?> doAllinPayShopBalance(PayInfoApiDto payInfoApiDto) {
        long userId = Long.parseLong(payInfoApiDto.getUserId());
        // 支付单号
        Long payId = segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_PAY, userId);
        // 保存预支付信息
        PayInfo payInfo = new PayInfo();
        payInfo.setPayId(payId);
        payInfo.setUserId(userId);
        payInfo.setPayAmount(payInfoApiDto.getPayAmount());
        payInfo.setPayScore(0L);
        payInfo.setPayStatus(PayStatus.UNPAY.value());
        payInfo.setSysType(SysTypeEnum.MULTISHOP.value());
        payInfo.setPayType(payInfoApiDto.getPayType());
        payInfo.setVersion(0);
        payInfo.setOrderIds(payInfoApiDto.getOrderIds());
        payInfo.setRefundId(Objects.isNull(payInfoApiDto.getRefundId()) ? 0L : payInfoApiDto.getRefundId());
        payInfo.setPayEntry(payInfoApiDto.getPayEntry());
        payInfoMapper.save(payInfo);

        // 进行支付
        PayInfoBO payInfoBO = BeanUtil.map(payInfoApiDto, PayInfoBO.class);
        payInfoBO.setExtendInfo(payInfoApiDto.getExtendInfo());
        payInfoBO.setSystemType(2);
        payInfoBO.setValidateType(1L);
        payInfoBO.setApiNoticeUrl(domainUrl + "/ua/notice/pay/" + 1 + "/" + payInfoApiDto.getPayEntry() + "/" + payInfoApiDto.getPayType());
        // 付款人此时是为商家id
        payInfoBO.setUserId(AllinpayConstant.SHOP + payInfoApiDto.getShopId());
        // 收款人为供应商
        List<ShopAmountVO> shopAmountVOList = new ArrayList<>(16);
        ShopAmountVO shopAmountVO = new ShopAmountVO();
        shopAmountVO.setBizUserId(AllinpayConstant.SUPPLIER + payInfoApiDto.getBizUserId());
        shopAmountVO.setAmount(payInfoBO.getPayAmount());
        shopAmountVOList.add(shopAmountVO);
        payInfoBO.setReceiveList(shopAmountVOList);
        return allinpayPayManager.doAllinPay(payInfoBO);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ServerResponseEntity<?> doAllinPaySettlement(AllinpaySettlementApiDto payInfoApiVO) {
        PayInfo settlementPayInfo = payInfoMapper.getByOrderIdAndEntryAndToUserId(null, Long.valueOf(payInfoApiVO.getOrderId()), PayEntry.ORDER_SETTLEMENT.value());
        if (Objects.nonNull(settlementPayInfo)) {
            log.info("订单已在结算中，orderId={}", payInfoApiVO.getOrderId());
            return ServerResponseEntity.showFailMsg("订单已在结算中，请勿重复提交");
        }
        // 1.用户支付
        List<PayInfo> payInfoDbList = payInfoMapper.listByPayIds(payInfoApiVO.getPayIds());
        if (CollectionUtil.isEmpty(payInfoDbList)) {
            throw new LuckException("支付信息不存在");
        }
        // 1.用户退款
        List<RefundInfoVO> refundInfoDbList = refundInfoMapper.listByPayIds(payInfoApiVO.getPayIds());
        Map<Long, List<RefundInfoVO>> refundInfoMap = refundInfoDbList.stream().collect(Collectors.groupingBy(RefundInfoVO::getPayId));
        Long payAmount = payInfoDbList.stream().mapToLong(PayInfo::getPayAmount).sum();
        List<PayInfo> payInfoList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        SplitRuleVO shopSplitRuleDto = payInfoApiVO.getShopSplitRuleDto();
        // 分销金额
        List<SplitRuleVO> distributionUserSplitRuleList = payInfoApiVO.getDistributionUserSplitRuleList();
        SplitRuleVO supplierSplitRuleDto = payInfoApiVO.getSupplierSplitRuleDto();
        SplitRuleVO platformSplitRuleDto = payInfoApiVO.getPlatformSplitRuleDto();
        setUserIdPrefix(shopSplitRuleDto, distributionUserSplitRuleList, supplierSplitRuleDto);
        long platformAmount = Objects.isNull(platformSplitRuleDto) ? 0L : platformSplitRuleDto.getFee();
        // 如果平台收入金额为负数，则需要转账给商家or分销员，此处考虑到商家还有一笔分账，优先转账给分销
        // 如果比平台需要转账的还大，就多转一份给商家/分销员，此时多转的部分再从商家分账里面扣除手续费给到平台
        // 平台转账
        if (platformAmount < 0L) {
            platformAmount = handlePlatformTransfer(shopSplitRuleDto, distributionUserSplitRuleList, supplierSplitRuleDto, platformAmount);
        }
        BatchAgentPay batchAgentPay = new BatchAgentPay();
        List<AgentPay> agentPayList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        ServerResponseEntity<String> configResp = configFeignClient.getConfig(Constant.ALLINPAY_CONFIG);
        if (!configResp.isSuccess()) {
            log.info("获取通联配置失败，msg={}", configResp.getMsg());
            throw new LuckException(configResp.getMsg());
        }
        Allinpay allinpay = Json.parseObject(configResp.getData(), Allinpay.class);
        // 判断下是否需要处理供应商、平台、分销员分账
        if (Objects.nonNull(supplierSplitRuleDto) && supplierSplitRuleDto.getAmount() > 0L) {
            log.info("供应商分账，orderId={}", payInfoApiVO.getOrderId());
            // 供应商商品不能添加为预售所以只有一个支付信息
            PayInfo payInfoDb = payInfoDbList.get(0);
            // 处理供应商分账
            handleAccountSharing(payInfoApiVO, allinpay, shopSplitRuleDto, supplierSplitRuleDto, platformAmount,
                    payInfoDb, agentPayList, payInfoList, refundInfoMap);
        } else {
            log.info("商家分账，orderId={}", payInfoApiVO.getOrderId());
            // 处理商家分账(平台分账，分销员分账)
            accountSharing(payInfoDbList, shopSplitRuleDto, distributionUserSplitRuleList, platformAmount, agentPayList, allinpay, refundInfoMap);
        }
        for (AgentPay agentPay : agentPayList) {
            setCollectPayList(agentPay,settlementPayInfo.getBizPayNo());
        }
        batchAgentPay.setTradeCode("4001");
        batchAgentPay.setBatchPayList(agentPayList);
        batchAgentPay.setBizBatchNo(payInfoApiVO.getOrderId());

        // 保存预支付信息
        PayInfo payInfo = new PayInfo();
        // 支付单号
        Long payId = segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_PAY, payInfoDbList.get(0).getUserId());
        payInfo.setPayId(payId);
        payInfo.setRefundId(0L);
        payInfo.setUserId(payInfoDbList.get(0).getUserId());
        payInfo.setPayAmount(payAmount);
        payInfo.setPayScore(0L);
        payInfo.setPayStatus(PayStatus.UNPAY.value());
        payInfo.setSysType(payInfoDbList.get(0).getSysType());
        payInfo.setPayType(payInfoDbList.get(0).getPayType());
        payInfo.setVersion(0);
        payInfo.setOrderIds(payInfoApiVO.getOrderId());
        payInfo.setPayEntry(PayEntry.ORDER_SETTLEMENT.value());
        // TODO 是否发送个定时任务
        payInfoMapper.save(payInfo);
        allinpayManager.batchShareAccountToShop(batchAgentPay);
        return ServerResponseEntity.success();
    }

    @Override
    public ServerResponseEntity<?> cancelOrderQrCode(OrderPayInfoDTO orderPayInfoDTO) {
        List<PayInfo> payInfoList = payInfoMapper.listPayedInfoByOrderIds(orderPayInfoDTO.getOrderIds(), orderPayInfoDTO.getUserId(), PayStatus.UNPAY.value());
        for (PayInfo payInfo : payInfoList) {
            // 失效未支付订单的二维码
            if (Objects.equals(payInfo.getPayType(), PayType.WECHATPAY_SWEEP_CODE.value()) ||
                    Objects.equals(payInfo.getPayType(), PayType.ALIPAY.value())) {
                // 如果有重复的payNo，不用失效掉二维码。
                try {
                    payManager.cancelOrderQrCode(payInfo.getPayId(), payInfo.getPayType());
                }catch (Exception e) {
                    log.error("失效支付二维码失败");
                }
            }
        }
        return ServerResponseEntity.success();
    }

    @Override
    public ServerResponseEntity<List<PayInfoApiDto>> getPayInfoByUserIdAndOrderId(Long userId, Long orderId) {
        List<PayInfo> payInfoList = payInfoMapper.getPayInfoByUserIdAndOrderId(userId, orderId);
        List<PayInfoApiDto> apiDto = BeanUtil.mapAsList(payInfoList, PayInfoApiDto.class);
        return ServerResponseEntity.success(apiDto);
    }

    @Override
    public ServerResponseEntity<PayInfoApiDto> getPayInfoByPayId(Long payId){
        PayInfo payInfo = payInfoMapper.getPayInfoByPayId(payId);
        if (Objects.isNull(payInfo)) {
            return ServerResponseEntity.success(null);
        }
        PayInfoApiDto apiDto = BeanUtil.map(payInfo, PayInfoApiDto.class);
        return ServerResponseEntity.success(apiDto);
    }

    @Override
    public ServerResponseEntity<Boolean> checkRefundStatus(Long refundId) {
        RefundInfoVO refundInfoVO = refundInfoMapper.getByRefundId(refundId);
        if (Objects.isNull(refundInfoVO)) {
            return ServerResponseEntity.success(Boolean.FALSE);
        }
        PayInfo payInfo = payInfoMapper.getByPayId(refundInfoVO.getPayId());
        RefundInfoResultBO refundInfoResultBO = payManager.getRefundInfo(refundInfoVO.getPayType(), refundInfoVO.getPayId(), payInfo.getPaySysType(), refundInfoVO.getRefundId());
        if (refundInfoResultBO.getRefundSuccess()) {
            log.info("退款订单{}退款成功，进行回调....", refundId);
            // 退款回调
            RefundInfo refundInfo;
            if(Objects.equals(payInfo.getPaySysType(), PaySysType.DEFAULT.value())) {
                // 如果普通支付是预售订单时，此处会有两个,此时会有两种情况
                // 情况1，都是微信支付此时只需要退款成功即可
                // 情况2，其中一个是余额支付，因为这是直接成功没有回调，所以同上
                refundInfo = refundInfoService.getByRefundIdAndWxPay(refundInfoResultBO.getRefundId());
            } else {
                refundInfo = refundInfoService.getByRefundId(refundInfoResultBO.getRefundId());
            }
            refundInfoService.refundSuccess(refundInfo);
            return ServerResponseEntity.success(Boolean.TRUE);
        }
        return ServerResponseEntity.success(Boolean.FALSE);
    }

    @Override
    public ServerResponseEntity<Boolean> updatePayInfoBySplitGroup(String orderIds, Long oldOrderId) {
        List<PayInfo> payInfoList = payInfoMapper.getByOrderIdsAndEntry(oldOrderId.toString(), PayEntry.ORDER.value());
        PayInfo payInfo = new PayInfo();
        // 组合商品只有一条支付信息
        payInfo.setPayId(payInfoList.get(0).getPayId());
        payInfo.setOrderIds(orderIds);
        payInfoMapper.update(payInfo);
        return ServerResponseEntity.success();
    }


    private void setCollectPayList(AgentPay agentPay, String bizPayNo) {
        // 回调地址
        agentPay.setBackUrl(domainUrl + "/ua/notice/pay/" + 1 + "/" + PayEntry.ORDER_SETTLEMENT.value() + "/" + 0);
        AgentPay.CollectPay collectPay = new AgentPay.CollectPay();
        collectPay.setAmount(agentPay.getAmount());
        collectPay.setBizOrderNo(bizPayNo);
        collectPay.setOrderNo(agentPay.getBizOrderNo());
        agentPay.setCollectPayList(Collections.singletonList(collectPay));
    }

    /**
     * 处理商家分账(平台分账，分销员分账)
     *
     * @param payInfoDbList
     * @param shopSplitRuleDto
     * @param distributionUserSplitRuleList
     * @param platformAmount
     * @param agentPayList
     * @param allinpay
     * @param refundInfoMap
     */
    private void accountSharing(List<PayInfo> payInfoDbList, SplitRuleVO shopSplitRuleDto, List<SplitRuleVO> distributionUserSplitRuleList,
                                long platformAmount, List<AgentPay> agentPayList, Allinpay allinpay, Map<Long, List<RefundInfoVO>> refundInfoMap) {
        List<SplitRule> splitRuleList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (PayInfo payInfo : payInfoDbList) {
            AgentPay agentPay = new AgentPay();
            // 计算退款金额总和
            long refundAmount = refundInfoMap.containsKey(payInfo.getPayId()) ?  refundInfoMap.get(payInfo.getPayId()).stream().mapToLong(RefundInfoVO::getRefundAmount).sum() : 0L;
            // 可分账金额
            long payAmount = payInfo.getPayAmount() - refundAmount;
            // 1.先处理平台分账
            if (platformAmount > 0L) {
                // 如果平台分账金额大于当前可分账金额，则平台分账金额为当前可分账金额
                if (platformAmount > payAmount) {
                    agentPay.setFee(payInfo.getPayAmount());
                    payAmount = 0L;
                } else {
                    payAmount = payAmount - platformAmount;
                    agentPay.setFee(platformAmount);
                }
            }

            // 2.进行分销员分账
            if (CollectionUtil.isNotEmpty(distributionUserSplitRuleList) && payAmount > 0L) {
                for (SplitRuleVO pendingSplitRuleVO : distributionUserSplitRuleList) {
                    if (pendingSplitRuleVO.getAmount() == 0L) {
                        continue;
                    }
                    // 如果分销员分账金额大于当前可分账金额，则分销员分账金额为当前可分账金额
                    if (payAmount > pendingSplitRuleVO.getAmount()) {
                        splitRuleList.add(BeanUtil.map(pendingSplitRuleVO, SplitRule.class));
                        payAmount -= pendingSplitRuleVO.getAmount();
                    } else {
                        // 减去支付金额下一轮继续进行分账
                        pendingSplitRuleVO.setAmount(pendingSplitRuleVO.getAmount() - payInfo.getPayAmount());
                        // 如果大于支付金额，则这次分摊给分销员的金额就是支付金额而不是分销员的分账金额
                        SplitRule partSplitRule = BeanUtil.map(pendingSplitRuleVO, SplitRule.class);
                        partSplitRule.setAmount(payAmount);
                        splitRuleList.add(partSplitRule);
                        break;
                    }
                    // 剩余可分账的金额如果为0，直接break
                    if (payAmount <= 0) {
                        break;
                    }
                }
                agentPay.setSplitRuleList(splitRuleList);
            }
            agentPay.setAccountSetNo(allinpay != null ? allinpay.getAccountSetNo() : null);
            agentPay.setBizUserId(shopSplitRuleDto.getBizUserId());
            agentPay.setBizOrderNo(payInfo.getPayId().toString());
            agentPay.setAmount(payAmount);
            agentPay.setFee(0L);
            agentPay.setTradeCode("4001");
            agentPayList.add(agentPay);
        }
    }

    /**
     * 处理供应商分账
     *
     * @param payInfoApiVO
     * @param allinpay
     * @param shopSplitRuleDto
     * @param supplierSplitRuleDto
     * @param platformAmount
     * @param payInfo
     * @param agentPayList
     * @param payInfoList
     * @param refundInfoMap
     */
    private void handleAccountSharing(AllinpaySettlementApiDto payInfoApiVO, Allinpay allinpay, SplitRuleVO shopSplitRuleDto, SplitRuleVO supplierSplitRuleDto, long platformAmount, PayInfo payInfo, List<AgentPay> agentPayList, List<PayInfo> payInfoList, Map<Long, List<RefundInfoVO>> refundInfoMap) {
        // 商家支付采购费用
        List<PayInfo> purchasePayInfoList = payInfoMapper.getByOrderIdsAndEntry(payInfoApiVO.getOrderId(), PayEntry.SHOP_BALANCE_PAY_PURCHASE.value());
        // 商家支付自行处理退款费用
        List<PayInfo> selfHandleRefundPayInfoList = payInfoMapper.getByOrderIdsAndEntry(payInfoApiVO.getOrderId(), PayEntry.SHOP_BALANCE_PAY_REFUND_SPREAD.value());
        // 1.商家采购金额分账，分摊钱给到供应商，其中商家支付的采购费用和自行处理的退款费用优先给到供应商
        if (CollectionUtil.isNotEmpty(purchasePayInfoList)) {
            // 采购只有一笔
            PayInfo purchasePayInfo = purchasePayInfoList.get(0);
            AgentPay agentPay = getAgentPay(allinpay, supplierSplitRuleDto, purchasePayInfo);
            agentPayList.add(agentPay);

            // 设置下供应商还需要结算的钱以及需要支付的手续费
            long settlementAmount = agentPay.getAmount() - agentPay.getFee();
            long settlementFee = agentPay.getFee();
            supplierSplitRuleDto.setAmount(supplierSplitRuleDto.getAmount() - settlementAmount);
            supplierSplitRuleDto.setFee(supplierSplitRuleDto.getFee() - settlementFee);
        }
        // 2.商家自行处理退款的费用分账给到供应商，如果这里有自行处理退款的费用，表示分销员的钱为0，不用考虑
        long settlementAmount = 0L;
        long settlementFee = 0L;
        if (CollectionUtil.isNotEmpty(selfHandleRefundPayInfoList)) {
            for (PayInfo refundPayInfo : selfHandleRefundPayInfoList) {
                AgentPay agentPay = getAgentPay(allinpay, supplierSplitRuleDto, refundPayInfo);
                agentPayList.add(agentPay);
                // 供应商已结算的钱以及手续费
                settlementAmount += agentPay.getAmount() - agentPay.getFee();
                settlementFee += agentPay.getFee();
            }
            supplierSplitRuleDto.setAmount(supplierSplitRuleDto.getAmount() - settlementAmount);
            supplierSplitRuleDto.setFee(supplierSplitRuleDto.getFee() - settlementFee);
        }
        // 3.处理下用户支付的那一笔分账
        // 计算退款金额总和
        long refundAmount = refundInfoMap.get(payInfo.getPayId()).stream().mapToLong(RefundInfoVO::getRefundAmount).sum();
        Long payAmount = payInfo.getPayAmount() - refundAmount;
        List<SplitRule> splitRuleList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (supplierSplitRuleDto.getAmount() > 0L) {
            splitRuleList.add(BeanUtil.map(supplierSplitRuleDto, SplitRule.class));
        }
        AgentPay agentPay = new AgentPay();
        agentPay.setAccountSetNo(allinpay.getAccountSetNo());
        agentPay.setBizUserId(shopSplitRuleDto.getBizUserId());
        agentPay.setBizOrderNo(payInfo.getPayId().toString());
        agentPay.setAmount(payAmount);
        agentPay.setFee(platformAmount);
        agentPay.setSplitRuleList(splitRuleList);
        agentPay.setTradeCode("4001");
        agentPayList.add(agentPay);
    }

    @NotNull
    private static AgentPay getAgentPay(Allinpay allinpay, SplitRuleVO supplierSplitRuleDto, PayInfo purchasePayInfo) {
        AgentPay agentPay = new AgentPay();
        agentPay.setAccountSetNo(allinpay.getAccountSetNo());
        agentPay.setBizUserId(supplierSplitRuleDto.getBizUserId());
        // 因为有用户支付最少一分钱的情况下，就算订单有进行退款，因为采购金额和供货金额都是同比例变小，所以这里支付的采购费用不可能可能大于供应商的收入金额
        agentPay.setBizOrderNo(purchasePayInfo.getPayId().toString());
        agentPay.setAmount(purchasePayInfo.getPayAmount());
        // 如果这笔采购金额大于供应商的平台佣金支出，则在这里全部给到供应商的平台佣金
        if (agentPay.getAmount() > supplierSplitRuleDto.getFee()) {
            agentPay.setFee(supplierSplitRuleDto.getFee());
        } else {
            agentPay.setFee(agentPay.getAmount());
        }
        agentPay.setSplitRuleList(null);
        agentPay.setTradeCode("4001");
        return agentPay;
    }

    /**
     * 处理下平台转账
     *
     * @param shopSplitRuleDto
     * @param distributionUserSplitRuleList
     * @param supplierSplitRuleDto
     * @param platformAmount
     * @return
     */
    private long handlePlatformTransfer(SplitRuleVO shopSplitRuleDto, List<SplitRuleVO> distributionUserSplitRuleList, SplitRuleVO supplierSplitRuleDto, long platformAmount) {

        platformAmount = Math.abs(platformAmount);
        // 平台需要转账的钱
        List<SplitRuleVO> splitRuleList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        // 1.优先转账给分销员
        if (CollectionUtil.isNotEmpty(distributionUserSplitRuleList)) {
            for (SplitRuleVO distributionSplitRuleVO : distributionUserSplitRuleList) {
                if (platformAmount > distributionSplitRuleVO.getAmount()) {
                    platformAmount = platformAmount - distributionSplitRuleVO.getAmount();
                    distributionSplitRuleVO.setAmount(0L);
                    splitRuleList.add(distributionSplitRuleVO);
                } else {
                    // 分销金额大于剩余要分帐的金额时处理下金额
                    SplitRuleVO subSplitRuleVO = BeanUtil.map(distributionSplitRuleVO, SplitRuleVO.class);
                    subSplitRuleVO.setAmount(platformAmount);
                    splitRuleList.add(subSplitRuleVO);
                    // 分销员这一步减少平台分账的金额，并进行下一步分账
                    distributionSplitRuleVO.setAmount(distributionSplitRuleVO.getAmount() - platformAmount);
                    break;
                }
                if (platformAmount <= 0) {
                    break;
                }
            }
        }
        // 2.如果扣去分销员的钱后还有部分要转账的，转账给供应商
        if (platformAmount > 0 && Objects.nonNull(supplierSplitRuleDto)) {
            if (platformAmount > supplierSplitRuleDto.getAmount()) {
                platformAmount = platformAmount - supplierSplitRuleDto.getAmount();
                splitRuleList.add(supplierSplitRuleDto);
                // 这一步减少平台分账的金额，分账的金额已经为0
                supplierSplitRuleDto.setAmount(0L);
            } else {
                // 供应商需要分账的金额大于剩余要分帐的金额时处理下金额
                SplitRuleVO subSplitRuleVO = BeanUtil.map(supplierSplitRuleDto, SplitRuleVO.class);
                subSplitRuleVO.setAmount(platformAmount);
                splitRuleList.add(subSplitRuleVO);
                // 这一步减少平台分账的金额，并进行下一步分账
                supplierSplitRuleDto.setAmount(supplierSplitRuleDto.getAmount() - platformAmount);
            }
        }
        // 如果小于0则表示平台多支出了一部分，因为这部分钱本来是由商家出的，将其设置为商家手续费转账给平台
        if (platformAmount < 0) {
            shopSplitRuleDto.setFee(Math.abs(platformAmount));
        }
        // 如果扣去分销员、供应商的钱后还有部分要转账的继续转账给商家
        if (platformAmount > 0) {
            // 此处商家收入金额 肯定大于 平台转账金额，因为用户支付了一笔，还需要结算的
            SplitRuleVO splitRuleVO = BeanUtil.map(shopSplitRuleDto, SplitRuleVO.class);
            splitRuleVO.setAmount(platformAmount);
            splitRuleList.add(shopSplitRuleDto);

            // 最后要结算的金额减去平台转账的金额
            shopSplitRuleDto.setAmount(shopSplitRuleDto.getAmount() - platformAmount);
        }
        // 此时为了防止支付出错就不执行了，这里使用mq执行多次
        // 发送延迟消息
        // 发送消息执行平台转账
        SendStatus sendStatus = platformTransferTemplate.syncSend(RocketMqConstant.PLATFORM_TRANSFER_TOPIC, new GenericMessage<>(splitRuleList)).getSendStatus();
        if (!Objects.equals(sendStatus, SendStatus.SEND_OK)) {
            // 消息发不出去就抛异常，发的出去无所谓
            throw new LuckException(ResponseEnum.EXCEPTION);
        }
        return platformAmount;
    }

    private void setUserIdPrefix(SplitRuleVO shopSplitRuleDto, List<SplitRuleVO> distributionUserSplitRuleList, SplitRuleVO supplierSplitRuleDto) {
        if (Objects.nonNull(shopSplitRuleDto)) {
            shopSplitRuleDto.setBizUserId(AllinpayConstant.SHOP + shopSplitRuleDto.getBizUserId());
        }
        if (Objects.nonNull(supplierSplitRuleDto)) {
            supplierSplitRuleDto.setBizUserId(AllinpayConstant.SUPPLIER + supplierSplitRuleDto.getBizUserId());
        }
    }
}
