package cn.iocoder.yudao.module.pay.service.wxprofitsharing;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideBackUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideRespDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.divide.PayDivideUnifiedDto;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.enums.divide.PayDivideRefundStatusRespEnum;
import cn.iocoder.yudao.framework.pay.core.enums.divide.PayDivideStatusRespEnum;
import cn.iocoder.yudao.module.pay.controller.app.divide.vo.DrawMoneyVO;
import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.divide.DivideDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.dividecompany.DivideCompanyDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.divideinfo.DivideInfoDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.dividerecord.DivideRecordDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.refundlog.PayRefundLogDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.refundrecord.RefundRecordDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wxorder.WxOrderDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wxprofitsharinginfo.WxProfitsharingInfoDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wxprofitsharingreturn.WxProfitsharingReturnDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.wxrefund.WxRefundDO;
import cn.iocoder.yudao.module.pay.dal.mysql.refundlog.PayRefundLogMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.wxorder.WxOrderMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.wxprofitsharinginfo.WxProfitsharingInfoMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.wxprofitsharingreturn.WxProfitsharingReturnMapper;
import cn.iocoder.yudao.module.pay.dal.mysql.wxrefund.WxRefundMapper;
import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
import cn.iocoder.yudao.module.pay.service.dividecompany.DivideCompanyService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.binarywang.wxpay.bean.profitsharing.request.ProfitSharingV3Request;
import com.github.binarywang.wxpay.bean.profitsharing.result.ProfitSharingV3Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import org.springframework.validation.annotation.Validated;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import cn.iocoder.yudao.module.pay.controller.admin.wxprofitsharing.vo.*;
import cn.iocoder.yudao.module.pay.dal.dataobject.wxprofitsharing.WxProfitsharingDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;

import cn.iocoder.yudao.module.pay.dal.mysql.wxprofitsharing.WxProfitsharingMapper;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;

/**
 * 微信分账记录 Service 实现类
 *
 * @author 管理员
 */
@Service
@Validated
@Slf4j
public class WxProfitsharingServiceImpl implements WxProfitsharingService {

    @Resource
    private WxProfitsharingMapper wxProfitsharingMapper;
    @Resource
    private WxOrderMapper wxOrderMapper;
    @Resource
    private PayNoRedisDAO noRedisDAO;
    @Resource
    private PayProperties payProperties;
    @Resource
    private PayChannelService channelService;
    @Resource
    private DivideCompanyService divideCompanyService;
    @Resource
    private WxProfitsharingInfoMapper infoMapper;
    @Resource
    private WxProfitsharingReturnMapper returnMapper;
    @Resource
    private StringRedisTemplate divideRedisTemplate;
    @Resource
    private WxRefundMapper wxRefundMapper;
    @Resource
    private PayRefundLogMapper payRefundLogMapper;

    @Override
    public Long createWxProfitsharing(WxProfitsharingSaveReqVO createReqVO) {
        // 插入
        WxProfitsharingDO wxProfitsharing = BeanUtils.toBean(createReqVO, WxProfitsharingDO.class);
        wxProfitsharingMapper.insert(wxProfitsharing);
        // 返回
        return wxProfitsharing.getId();
    }

    @Override
    public void updateWxProfitsharing(WxProfitsharingSaveReqVO updateReqVO) {
        // 校验存在
        validateWxProfitsharingExists(updateReqVO.getId());
        // 更新
        WxProfitsharingDO updateObj = BeanUtils.toBean(updateReqVO, WxProfitsharingDO.class);
        wxProfitsharingMapper.updateById(updateObj);
    }

    @Override
    public void deleteWxProfitsharing(Long id) {
        // 校验存在
        validateWxProfitsharingExists(id);
        // 删除
        wxProfitsharingMapper.deleteById(id);
    }

    private void validateWxProfitsharingExists(Long id) {
        if (wxProfitsharingMapper.selectById(id) == null) {
            throw exception(WX_PROFITSHARING_NOT_EXISTS);
        }
    }

    @Override
    public WxProfitsharingDO getWxProfitsharing(Long id) {
        return wxProfitsharingMapper.selectById(id);
    }

    @Override
    public PageResult<WxProfitsharingDO> getWxProfitsharingPage(WxProfitsharingPageReqVO pageReqVO) {
        return wxProfitsharingMapper.selectPage(pageReqVO);
    }

    /**
     * 分账
     */
    @Override
    public int startProfitsharing() {
        //获取昨天营业额中的微信充值金额
        BigDecimal today = wxProfitsharingMapper.getToday();
        if (ObjectUtil.isEmpty(today)) {
            today = BigDecimal.ZERO;
        }
        //转换单位  元-->分
        int money = today.multiply(new BigDecimal("100")).intValue();

        //加上昨天补充金额
        BigDecimal rechargeLog = wxProfitsharingMapper.getRechargeLog();
        if(ObjectUtil.isEmpty(rechargeLog)){
            rechargeLog = BigDecimal.ZERO;
        }
        money = money+rechargeLog.multiply(new BigDecimal("100")).intValue();


        //退款处理
        money = handleRefund(new BigDecimal(money)).multiply(new BigDecimal("100")).intValue();

        if(money <= 0){
            return 0;
        }

        //查询29天内的充值订单，原因：微信30天自动解冻，订单将不能分账
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime thirtyDaysAgo = now.minusDays(29);
        List<WxOrderDO> wxOrderDOList = wxOrderMapper.selectList(Wrappers.<WxOrderDO>lambdaQuery()
                .gt(WxOrderDO::getProfitsharingPrice, 0)
                .ge(WxOrderDO::getCreateTime, thirtyDaysAgo)
                .orderByAsc(WxOrderDO::getCreateTime));
        if (CollectionUtil.isEmpty(wxOrderDOList)) {
            return 0;
        }
        Map<Long, WxOrderDO> orderMap = wxOrderDOList.stream().collect(Collectors.toMap(WxOrderDO::getId, vo -> vo));

        //计算分账用的订单数据
        List<WxProfitsharingDO> wxProfitsharingDOS = computeProfitsharing(wxOrderDOList, money);

        int counter = 0;
        //调用微信分账
        for (WxProfitsharingDO domain : wxProfitsharingDOS) {

            WxOrderDO wxOrderDO = orderMap.get(domain.getPayWxOrderId());

            PayClient client = channelService.getPayClient(wxOrderDO.getChannelId());
            PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();

            payDivideUnifiedDto.setTransactionId(wxOrderDO.getTransactionId())
                    .setOutOrderNo(domain.getOutOrderNo())
                    .setUnfreezeUnsplit(wxOrderDO.getProfitsharingPrice() == 0)
                    .setReceivers(createReceiver(domain));

            PayDivideRespDto payDivideRespDto = client.unifiedDivide(payDivideUnifiedDto);
            domain.setState("PROCESSING");
            domain.setOrderId(payDivideRespDto.getOrderId());

            insertDB(payDivideRespDto, domain);

            // 每执行250次，休息一分钟
            if (++counter % 250 == 0) {
                try {
                    TimeUnit.MINUTES.sleep(1);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    e.printStackTrace();
                }
            }
        }

        return wxProfitsharingDOS.size();
    }

    /**
     * 退款处理
     */
    public BigDecimal handleRefund(BigDecimal money){
        money = money.divide(new BigDecimal("100"),2, RoundingMode.HALF_UP);
        //去除退款中的微信充值金额
        List<PayRefundLogDO> refundLogDOList = payRefundLogMapper.selectList(Wrappers.<PayRefundLogDO>lambdaQuery()
                .gt(PayRefundLogDO::getLeftMoney,0));
        if(CollectionUtil.isEmpty(refundLogDOList)){
            return money;
        }

        BigDecimal refundToday = refundLogDOList.stream().map(PayRefundLogDO::getLeftMoney).reduce(BigDecimal.ZERO, BigDecimal::add);

        if(money.compareTo(refundToday)>=0){
            money = money.subtract(refundToday);
            List<Long> ids = refundLogDOList.stream().map(PayRefundLogDO::getId).collect(Collectors.toList());
            payRefundLogMapper.update(Wrappers.<PayRefundLogDO>lambdaUpdate().in(PayRefundLogDO::getId,ids)
                    .set(PayRefundLogDO::getLeftMoney,0));
        }else {
            ArrayList<PayRefundLogDO> updateList = new ArrayList<>();
            for (PayRefundLogDO refundLogDO:refundLogDOList){
                BigDecimal leftMoney = refundLogDO.getLeftMoney();
                if(money.compareTo(leftMoney)<=0){
                    refundLogDO.setLeftMoney(leftMoney.subtract(money));
                    updateList.add(refundLogDO);
                    money = BigDecimal.ZERO;
                    break;
                }else {
                    money = money.subtract(leftMoney);
                    refundLogDO.setLeftMoney(BigDecimal.ZERO);
                    updateList.add(refundLogDO);
                }
            }
            if(CollectionUtil.isNotEmpty(updateList)){
                payRefundLogMapper.updateBatch(updateList);
            }
        }
        return money;
    }

    /**
     * 生成分账订单
     */
    public List<WxProfitsharingDO> computeProfitsharing(List<WxOrderDO> wxOrderDOList, int money) {

        List<WxProfitsharingDO> returnList = new ArrayList<>();
        List<WxOrderDO> updateList = new ArrayList<>();

        int sum = 0;
        for (WxOrderDO wxOrderDO : wxOrderDOList) {
            WxProfitsharingDO wxProfitsharingDO = new WxProfitsharingDO();
            wxProfitsharingDO.setPayWxOrderId(wxOrderDO.getId());
            wxProfitsharingDO.setOutOrderNo(noRedisDAO.generate(payProperties.getProfitsharingNoPrefix()));
            sum = sum + wxOrderDO.getProfitsharingPrice();
            if (sum < money) {
                wxProfitsharingDO.setTotalPrice(wxOrderDO.getProfitsharingPrice());
                wxProfitsharingDO.setReturnPrice(wxOrderDO.getProfitsharingPrice());
                wxOrderDO.setProfitsharingPrice(0);

                returnList.add(wxProfitsharingDO);
                updateList.add(wxOrderDO);
            } else if (sum == money) {
                wxProfitsharingDO.setTotalPrice(wxOrderDO.getProfitsharingPrice());
                wxProfitsharingDO.setReturnPrice(wxOrderDO.getProfitsharingPrice());
                wxOrderDO.setProfitsharingPrice(0);

                returnList.add(wxProfitsharingDO);
                updateList.add(wxOrderDO);
                break;
            } else {
                int leftMoney = money + wxOrderDO.getProfitsharingPrice() - sum;
                wxProfitsharingDO.setTotalPrice(leftMoney);
                wxProfitsharingDO.setReturnPrice(leftMoney);
                wxOrderDO.setProfitsharingPrice(wxOrderDO.getProfitsharingPrice() - leftMoney);

                returnList.add(wxProfitsharingDO);
                updateList.add(wxOrderDO);
                break;
            }

        }
        if(CollectionUtil.isNotEmpty(updateList)){
            wxOrderMapper.updateBatch(updateList);
        }
        return returnList;
    }

    /**
     * 生成分账接收方数据
     */
    public List<ProfitSharingV3Request.Receiver> createReceiver(WxProfitsharingDO domain) {

        List<ProfitSharingV3Request.Receiver> receivers = new ArrayList<>();
        List<DivideCompanyDO> companyList = divideCompanyService.getList();

        for (DivideCompanyDO divideCompanyDO : companyList) {
            int money = (int) Math.ceil(domain.getTotalPrice() * Double.parseDouble(divideCompanyDO.getProportion()));
            ProfitSharingV3Request.Receiver receiver = new ProfitSharingV3Request.Receiver();
            receiver.setName(divideCompanyDO.getName());
            receiver.setType(divideCompanyDO.getType());
            receiver.setAmount(money);
            receiver.setAccount(divideCompanyDO.getAccount());
            receiver.setDescription("给" + divideCompanyDO.getName() + "分账");
            receivers.add(receiver);
        }
        return receivers;
    }

    /**
     * 分账和分账明细添加数据库
     */
    public void insertDB(PayDivideRespDto payDivideRespDto, WxProfitsharingDO domain) {
        domain.setOrderId(payDivideRespDto.getOrderId());
        domain.setState("PROCESSING");
        wxProfitsharingMapper.insert(domain);
        List<DivideCompanyDO> companyList = divideCompanyService.getList();
        Map<String, DivideCompanyDO> companyMap = companyList.stream().collect(Collectors.toMap(DivideCompanyDO::getAccount, vo -> vo));

        List<WxProfitsharingInfoDO> insertList = new ArrayList<>();
        for (ProfitSharingV3Result.Receiver receiver : payDivideRespDto.getReceivers()) {
            DivideCompanyDO divideCompanyDO = companyMap.get(receiver.getAccount());
            if(ObjectUtil.isEmpty(divideCompanyDO)){
                continue;
            }
            WxProfitsharingInfoDO infoDO = new WxProfitsharingInfoDO();
            infoDO.setPayWxProfitsharingId(domain.getId());
            infoDO.setName(divideCompanyDO.getName());
            infoDO.setType(receiver.getType());
            infoDO.setAccount(receiver.getAccount());
            infoDO.setAmount(receiver.getAmount());
            infoDO.setDescription(receiver.getDescription());
            infoDO.setResult(receiver.getResult());
            infoDO.setFailReason(receiver.getFailReason());
            infoDO.setDetailId(receiver.getDetailId());
            infoDO.setDCreateTime(receiver.getCreateTime());
            infoDO.setDFinishTime(receiver.getFinishTime());
            insertList.add(infoDO);
        }
        infoMapper.insertBatch(insertList);
    }


    @Override
    public int getDivideResult() {
        //获取待查询结果的分账订单
        LambdaQueryWrapper<WxProfitsharingDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(WxProfitsharingDO::getState, "PROCESSING");

        List<WxProfitsharingDO> divideRecordDOList = wxProfitsharingMapper.selectList(wrapper);

        if (CollectionUtil.isEmpty(divideRecordDOList)) {
            return 0;
        }
        //调用微信接口
        for (WxProfitsharingDO domain : divideRecordDOList) {
            WxOrderDO wxOrderDO = wxOrderMapper.selectById(domain.getPayWxOrderId());

            PayClient client = channelService.getPayClient(wxOrderDO.getChannelId());
            PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();

            payDivideUnifiedDto.setTransactionId(wxOrderDO.getTransactionId())
                    .setOutOrderNo(domain.getOutOrderNo());
            PayDivideRespDto payDivideRespDto = client.unifiedDivideResult(payDivideUnifiedDto);

            //同步数据库结果
            domain.setState(payDivideRespDto.getState());
            wxProfitsharingMapper.updateById(domain);
            //同步详情结果
            for (ProfitSharingV3Result.Receiver receiver : payDivideRespDto.getReceivers()) {
                LambdaQueryWrapper<WxProfitsharingInfoDO> infoWrapper = new LambdaQueryWrapper<>();
                infoWrapper.eq(WxProfitsharingInfoDO::getPayWxProfitsharingId, domain.getId())
                        .eq(WxProfitsharingInfoDO::getAmount, receiver.getAmount());
                List<WxProfitsharingInfoDO> infoDOList = infoMapper.selectList(infoWrapper);
                if (CollectionUtil.isNotEmpty(infoDOList)) {
                    WxProfitsharingInfoDO infoDO = infoDOList.get(0);
                    infoDO.setResult(receiver.getResult());
                    infoDO.setFailReason(receiver.getFailReason());
                    infoMapper.updateById(infoDO);
                }
            }
        }
        return divideRecordDOList.size();
    }

    @Override
    public Boolean drawMoney(DrawMoneyVO drawMoneyVO) {
        //判断订单是否完成
        int i = wxProfitsharingMapper.orderCount(drawMoneyVO.getUserId());
        if (i > 0) {
            throw new ServiceException(ORDER_NOT_COMPLETE);
        }

        //判断是否有金额可退款
        BigDecimal wxAmount = wxProfitsharingMapper.getWxAmount(drawMoneyVO.getUserId());
        if (wxAmount == null) {
            throw new ServiceException(WX_ACCOUNT_NO);
        }

        //判断是否有金额可退款
        int money = wxAmount.multiply(new BigDecimal("100")).intValue();
        if (money <= 0) {
            throw new ServiceException(WX_ACCOUNT_NO);
        }
        //指定金额
        if (drawMoneyVO.getTotalPrice() != null) {
            if (money < drawMoneyVO.getTotalPrice()) {
                throw new ServiceException(WX_ACCOUNT_NO);
            }
            money = drawMoneyVO.getTotalPrice();
        }
        //交易时间超过一年的订单无法提交退款（按支付成功时间+365天计算）
        LocalDateTime now = LocalDateTime.now(); // 获取当前时间
        LocalDateTime threeYearsAgo = now.minusDays(364);

        //查询最近的充值订单，时间倒序退款
        List<WxOrderDO> wxOrderDOList = wxOrderMapper.selectList(Wrappers.<WxOrderDO>lambdaQuery()
                .eq(WxOrderDO::getOpenId, drawMoneyVO.getChannelUserId())
                .gt(WxOrderDO::getRefundPrice, 0)
                .ge(WxOrderDO::getCreateTime, threeYearsAgo)
                .orderByDesc(WxOrderDO::getCreateTime));

        Map<Long, WxOrderDO> orderMap = wxOrderDOList.stream().collect(Collectors.toMap(WxOrderDO::getId, vo -> vo));

        //筛选出未分账的订单
        List<WxOrderDO> unDivideList = wxOrderDOList.stream().filter(vo -> vo.getRefundPrice().equals(vo.getProfitsharingPrice())).collect(Collectors.toList());

        //筛选出已分账的订单
        List<WxOrderDO> divideList = wxOrderDOList.stream().filter(vo -> !vo.getRefundPrice().equals(vo.getProfitsharingPrice())).collect(Collectors.toList());

        //筛选出分账完的订单
        List<WxOrderDO> divideCpList = divideList.stream().filter(vo -> vo.getProfitsharingPrice() == 0).collect(Collectors.toList());

        //剩下的是未分账完的订单
        List<WxOrderDO> divideUnCpList = divideList.stream().filter(vo -> vo.getProfitsharingPrice() != 0).collect(Collectors.toList());

        //生成退款订单
        List<WxRefundDO> refundDOList = createRefund(unDivideList, divideCpList, divideUnCpList, money, drawMoneyVO.getUserId());


        int failNum = 0;
        //调用微信退款接口
        if (CollectionUtil.isNotEmpty(refundDOList)) {
            for (WxRefundDO wxRefundDO : refundDOList) {
                WxOrderDO wxOrderDO = orderMap.get(wxRefundDO.getPayWxOrderId());

                PayChannelDO channel = channelService.validPayChannel(wxOrderDO.getChannelId());

                PayClient client = channelService.getPayClient(wxOrderDO.getChannelId());
                PayRefundUnifiedReqDTO payRefundUnifiedReqDTO = new PayRefundUnifiedReqDTO();
                payRefundUnifiedReqDTO.setTransactionId(wxOrderDO.getTransactionId())
                        .setReason("提现")
                        .setRefundPrice(wxRefundDO.getTotalPrice())
                        .setOutRefundNo(wxRefundDO.getOutRefundNo())
                        .setPayPrice(wxOrderDO.getTotalPrice())
                        .setNotifyUrl(genChannelRefundNotifyUrl(channel));
                PayRefundRespDTO payRefundRespDTO = client.unifiedRefund(payRefundUnifiedReqDTO);

                String channelRefundNo = payRefundRespDTO.getChannelRefundNo();
                wxRefundDO.setRefundId(channelRefundNo);
                if (cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.WAITING.getStatus().equals(payRefundRespDTO.getStatus())) {
                    wxRefundDO.setStatus(PayDivideRefundStatusRespEnum.PROCESSING.getStatus());
                } else if (cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum.SUCCESS.getStatus().equals(payRefundRespDTO.getStatus())) {
                    wxRefundDO.setStatus(PayDivideRefundStatusRespEnum.SUCCESS.getStatus());
                } else {
                    failNum++;
                    wxRefundDO.setStatus(PayDivideRefundStatusRespEnum.ABNORMAL.getStatus());
                }

                wxRefundMapper.insert(wxRefundDO);
            }
        }

        return failNum <= 0;
    }

    /**
     * 生成退款订单
     */
    public List<WxRefundDO> createRefund(List<WxOrderDO> unDivideList, List<WxOrderDO> divideCpList
            , List<WxOrderDO> divideUnCpList, int money, Long userId) {

        //退款订单
        List<WxRefundDO> backList = new ArrayList<>();


        int sum = 0;

        //先从未分账的订单计算
        for (WxOrderDO wxOrderDO : unDivideList) {
            Integer totalPrice = wxOrderDO.getRefundPrice();
            sum = sum + totalPrice;

            WxRefundDO wxRefundDO = new WxRefundDO();
            wxRefundDO.setPayWxOrderId(wxOrderDO.getId());
            wxRefundDO.setUserId(userId);
            wxRefundDO.setOutRefundNo(noRedisDAO.generate(payProperties.getRefundNoPrefix()));

            if (sum < money) {
                wxRefundDO.setTotalPrice(totalPrice);
                wxOrderDO.setRefundPrice(0);
                wxOrderDO.setProfitsharingPrice(0);

                String jsonString = JsonUtils.toJsonString(wxOrderDO);
                divideRedisTemplate.opsForValue().set("R" + wxOrderDO.getId(), jsonString);
                backList.add(wxRefundDO);
            } else if (sum == money) {
                wxRefundDO.setTotalPrice(totalPrice);
                wxOrderDO.setRefundPrice(0);
                wxOrderDO.setProfitsharingPrice(0);

                String jsonString = JsonUtils.toJsonString(wxOrderDO);
                divideRedisTemplate.opsForValue().set("R" + wxOrderDO.getId(), jsonString);
                backList.add(wxRefundDO);
                break;
            } else {
                int leftMoney = money + totalPrice - sum;
                wxRefundDO.setTotalPrice(leftMoney);
                wxOrderDO.setRefundPrice(totalPrice - leftMoney);
                wxOrderDO.setProfitsharingPrice(totalPrice - leftMoney);

                String jsonString = JsonUtils.toJsonString(wxOrderDO);
                divideRedisTemplate.opsForValue().set("R" + wxOrderDO.getId(), jsonString);
                backList.add(wxRefundDO);
                break;
            }
        }
        //从分账完的订单进行退款
        if (sum < money) {
            for (WxOrderDO wxOrderDO : divideCpList) {
                Integer totalPrice = wxOrderDO.getRefundPrice();
                sum = sum + totalPrice;

                WxRefundDO wxRefundDO = new WxRefundDO();
                wxRefundDO.setPayWxOrderId(wxOrderDO.getId());
                wxRefundDO.setUserId(userId);
                wxRefundDO.setOutRefundNo(noRedisDAO.generate(payProperties.getRefundNoPrefix()));

                if (sum < money) {
                    wxRefundDO.setTotalPrice(totalPrice);
                    wxOrderDO.setRefundPrice(0);

                    String jsonString = JsonUtils.toJsonString(wxOrderDO);
                    divideRedisTemplate.opsForValue().set("R" + wxOrderDO.getId(), jsonString);
                    divideRedisTemplate.opsForValue().set("R" + wxRefundDO.getOutRefundNo(), String.valueOf(totalPrice));
                    backList.add(wxRefundDO);
                } else if (sum == money) {
                    wxRefundDO.setTotalPrice(totalPrice);
                    wxOrderDO.setRefundPrice(0);

                    String jsonString = JsonUtils.toJsonString(wxOrderDO);
                    divideRedisTemplate.opsForValue().set("R" + wxOrderDO.getId(), jsonString);
                    divideRedisTemplate.opsForValue().set("R" + wxRefundDO.getOutRefundNo(), String.valueOf(totalPrice));
                    backList.add(wxRefundDO);
                    break;
                } else {
                    int leftMoney = money + totalPrice - sum;
                    wxRefundDO.setTotalPrice(leftMoney);
                    wxOrderDO.setRefundPrice(totalPrice - leftMoney);

                    String jsonString = JsonUtils.toJsonString(wxOrderDO);
                    divideRedisTemplate.opsForValue().set("R" + wxOrderDO.getId(), jsonString);
                    divideRedisTemplate.opsForValue().set("R" + wxRefundDO.getOutRefundNo(), String.valueOf(totalPrice));
                    backList.add(wxRefundDO);
                    break;
                }
            }
        }

        //从部分分账完的订单进行退款
        if (sum < money) {
            for (WxOrderDO wxOrderDO : divideUnCpList) {
                //还能退款的金额
                Integer totalPrice = wxOrderDO.getRefundPrice();

                //查询分账出去的金额
                int profitsharingPrice = getProfitsharingPrice(wxOrderDO.getId());

                //该订单还冻结的金额
                int freeze = totalPrice - profitsharingPrice;

                //未解冻资金中还未分账的金额
                Integer unps = wxOrderDO.getProfitsharingPrice();

                //拿来分账的金额
                Integer ps = totalPrice - unps;

                sum = sum + totalPrice;

                WxRefundDO wxRefundDO = new WxRefundDO();
                wxRefundDO.setPayWxOrderId(wxOrderDO.getId());
                wxRefundDO.setUserId(userId);
                wxRefundDO.setOutRefundNo(noRedisDAO.generate(payProperties.getRefundNoPrefix()));


                if (sum < money) {
                    wxRefundDO.setTotalPrice(totalPrice);
                    wxOrderDO.setRefundPrice(0);
                    wxOrderDO.setProfitsharingPrice(0);

                    //先解冻剩余资金
                    String unFreeNo = noRedisDAO.generate(payProperties.getOrderNoPrefix());
                    PayClient client = channelService.getPayClient(wxOrderDO.getChannelId());
                    PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();
                    payDivideUnifiedDto.setTransactionId(wxOrderDO.getTransactionId())
                            .setOutOrderNo(unFreeNo);
                    client.unifiedDivideFreeze(payDivideUnifiedDto);
                    String state = "";
                    do {
                        try {
                            //等待解冻成功
                            TimeUnit.SECONDS.sleep(15);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        PayDivideUnifiedDto resultDto = new PayDivideUnifiedDto();
                        resultDto.setTransactionId(wxOrderDO.getTransactionId())
                                .setOutOrderNo(unFreeNo);
                        PayDivideRespDto payDivideRespDto = client.unifiedDivideResult(resultDto);

                        state = payDivideRespDto.getState();
                    } while ("PROCESSING".equals(state));


                    //计算回退资金
                    String jsonString = JsonUtils.toJsonString(wxOrderDO);
                    divideRedisTemplate.opsForValue().set("R" + wxOrderDO.getId(), jsonString);
                    divideRedisTemplate.opsForValue().set("R" + wxRefundDO.getOutRefundNo(), String.valueOf(ps));

                    backList.add(wxRefundDO);
                } else if (sum == money) {
                    wxRefundDO.setTotalPrice(totalPrice);
                    wxOrderDO.setRefundPrice(0);
                    wxOrderDO.setProfitsharingPrice(0);

                    //先解冻剩余资金
                    String unFreeNo = noRedisDAO.generate(payProperties.getOrderNoPrefix());
                    PayClient client = channelService.getPayClient(wxOrderDO.getChannelId());
                    PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();
                    payDivideUnifiedDto.setTransactionId(wxOrderDO.getTransactionId())
                            .setOutOrderNo(unFreeNo);
                    client.unifiedDivideFreeze(payDivideUnifiedDto);
                    String state = "";
                    do {
                        try {
                            //等待解冻成功
                            TimeUnit.SECONDS.sleep(15);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                        PayDivideUnifiedDto resultDto = new PayDivideUnifiedDto();
                        resultDto.setTransactionId(wxOrderDO.getTransactionId())
                                .setOutOrderNo(unFreeNo);
                        PayDivideRespDto payDivideRespDto = client.unifiedDivideResult(resultDto);

                        state = payDivideRespDto.getState();
                    } while ("PROCESSING".equals(state));


                    //计算回退资金
                    String jsonString = JsonUtils.toJsonString(wxOrderDO);
                    divideRedisTemplate.opsForValue().set("R" + wxOrderDO.getId(), jsonString);
                    divideRedisTemplate.opsForValue().set("R" + wxRefundDO.getOutRefundNo(), String.valueOf(ps));
                    backList.add(wxRefundDO);
                    break;
                } else {

                    int leftMoney = money + totalPrice - sum;
                    wxRefundDO.setTotalPrice(leftMoney);
                    wxOrderDO.setRefundPrice(totalPrice - leftMoney);
                    wxOrderDO.setProfitsharingPrice(Math.max(unps - leftMoney, 0));

                    //是否解冻判定
                    if (freeze < leftMoney) {
                        String unFreeNo = noRedisDAO.generate(payProperties.getOrderNoPrefix());
                        PayClient client = channelService.getPayClient(wxOrderDO.getChannelId());
                        PayDivideUnifiedDto payDivideUnifiedDto = new PayDivideUnifiedDto();
                        payDivideUnifiedDto.setTransactionId(wxOrderDO.getTransactionId())
                                .setOutOrderNo(unFreeNo);
                        client.unifiedDivideFreeze(payDivideUnifiedDto);
                        String state = "";
                        do {
                            try {
                                //等待解冻成功
                                TimeUnit.SECONDS.sleep(15);
                            } catch (InterruptedException e) {
                                throw new RuntimeException(e);
                            }
                            PayDivideUnifiedDto resultDto = new PayDivideUnifiedDto();
                            resultDto.setTransactionId(wxOrderDO.getTransactionId())
                                    .setOutOrderNo(unFreeNo);
                            PayDivideRespDto payDivideRespDto = client.unifiedDivideResult(resultDto);

                            state = payDivideRespDto.getState();
                        } while ("PROCESSING".equals(state));
                    }

                    //计算回退资金
                    String jsonString = JsonUtils.toJsonString(wxOrderDO);
                    divideRedisTemplate.opsForValue().set("R" + wxOrderDO.getId(), jsonString);

                    if (unps < leftMoney) {
                        divideRedisTemplate.opsForValue().set("R" + wxRefundDO.getOutRefundNo(), String.valueOf(leftMoney - unps));
                    }
                    backList.add(wxRefundDO);
                    break;
                }

            }

        }
        return backList;
    }

    /**
     * 获取已分账金额
     */
    public int getProfitsharingPrice(Long orderId) {
        List<WxProfitsharingDO> wxProfitsharingDOS = wxProfitsharingMapper.selectList(Wrappers.<WxProfitsharingDO>lambdaQuery().eq(WxProfitsharingDO::getPayWxOrderId, orderId));
        if (CollectionUtil.isNotEmpty(wxProfitsharingDOS)) {
            List<Long> ids = wxProfitsharingDOS.stream().map(WxProfitsharingDO::getId).collect(Collectors.toList());
            List<WxProfitsharingInfoDO> wxProfitsharingInfoDOS = infoMapper.selectList(Wrappers.<WxProfitsharingInfoDO>lambdaQuery().in(WxProfitsharingInfoDO::getPayWxProfitsharingId, ids));
            if (CollectionUtil.isNotEmpty(wxProfitsharingInfoDOS)) {
                int sum = wxProfitsharingInfoDOS.stream().filter(vo -> ObjectUtil.isNotEmpty(vo.getAccount()))
                        .mapToInt(WxProfitsharingInfoDO::getAmount)
                        .sum();
                return sum;
            }
        }
        return 0;
    }

    /**
     * 退款回调地址
     */
    private String genChannelRefundNotifyUrl(PayChannelDO channel) {
        return payProperties.getRefundNotifyUrl() + "/" + channel.getId();
    }

    @Override
    public void returnMoney(Long payWxOrderId, int returnMoney) {
        List<WxProfitsharingDO> wxProfitsharingDOS = wxProfitsharingMapper.selectList(Wrappers.<WxProfitsharingDO>lambdaQuery().eq(WxProfitsharingDO::getPayWxOrderId, payWxOrderId));

        List<WxProfitsharingReturnDO> returnDOList = new ArrayList<>();
        int sum = 0;
        Map<Long,WxProfitsharingDO> map = new HashMap<>();
        for (WxProfitsharingDO domain : wxProfitsharingDOS) {
            int totalPrice = domain.getReturnPrice();
            sum = sum + totalPrice;

            List<WxProfitsharingInfoDO> infoDOList = infoMapper.selectList(Wrappers.<WxProfitsharingInfoDO>lambdaQuery()
                    .eq(WxProfitsharingInfoDO::getPayWxProfitsharingId, domain.getId()));



            if (sum < returnMoney) {
                domain.setReturnPrice(0);
                map.put(domain.getId(),domain);
                for (WxProfitsharingInfoDO infoDO : infoDOList){
                    WxProfitsharingReturnDO returnDO = new WxProfitsharingReturnDO();
                    returnDO.setPayWxProfitsharingId(domain.getId());
                    returnDO.setOutReturnNo(noRedisDAO.generate(payProperties.getReturnNoPrefix()));
                    returnDO.setAmount(infoDO.getAmount());
                    returnDO.setDescription("分账回退");
                    returnDO.setReturnMchid(infoDO.getAccount());
                    returnDO.setTotalPrice(totalPrice);
                    returnDOList.add(returnDO);
                }
            }else if (sum == returnMoney){
                domain.setReturnPrice(0);
                map.put(domain.getId(),domain);
                for (WxProfitsharingInfoDO infoDO : infoDOList){
                    WxProfitsharingReturnDO returnDO = new WxProfitsharingReturnDO();
                    returnDO.setPayWxProfitsharingId(domain.getId());
                    returnDO.setOutReturnNo(noRedisDAO.generate(payProperties.getReturnNoPrefix()));
                    returnDO.setAmount(infoDO.getAmount());
                    returnDO.setDescription("分账回退");
                    returnDO.setReturnMchid(infoDO.getAccount());
                    returnDO.setTotalPrice(totalPrice);
                    returnDOList.add(returnDO);
                }
                break;
            }else {
                int leftMoney = returnMoney - (sum - totalPrice);
                domain.setReturnPrice(totalPrice-leftMoney);
                map.put(domain.getId(),domain);
                for (WxProfitsharingInfoDO infoDO : infoDOList){
                    WxProfitsharingReturnDO returnDO = new WxProfitsharingReturnDO();
                    returnDO.setPayWxProfitsharingId(domain.getId());
                    returnDO.setOutReturnNo(noRedisDAO.generate(payProperties.getReturnNoPrefix()));
                    returnDO.setAmount(divideCompanyService.computeAmount(infoDO.getAccount(),leftMoney));
                    returnDO.setDescription("分账回退");
                    returnDO.setReturnMchid(infoDO.getAccount());
                    returnDO.setTotalPrice(leftMoney);
                    returnDOList.add(returnDO);
                    returnDOList.add(returnDO);
                }
                break;
            }

        }
        PayClient client = channelService.getPayClient(wxOrderMapper.selectById(payWxOrderId).getChannelId());
        //调微信接口
        for(WxProfitsharingReturnDO returnDO:returnDOList){
            WxProfitsharingDO wxProfitsharingDO = map.get(returnDO.getPayWxProfitsharingId());

            PayDivideBackUnifiedDto payDivideBackUnifiedDto = new PayDivideBackUnifiedDto();
            payDivideBackUnifiedDto.setAmount(returnDO.getAmount().longValue());
            payDivideBackUnifiedDto.setDescription(returnDO.getDescription());
            payDivideBackUnifiedDto.setOrderId(wxProfitsharingDO.getOrderId());
            payDivideBackUnifiedDto.setReturnMchid(returnDO.getReturnMchid());
            payDivideBackUnifiedDto.setOutOrderNo(wxProfitsharingDO.getOutOrderNo());
            payDivideBackUnifiedDto.setOutReturnNo(returnDO.getOutReturnNo());
            PayDivideBackRespDto respDto = client.unifiedDivideback(payDivideBackUnifiedDto);
            log.info("订单：" + respDto.getOrderId() + "回退结果：" + respDto.getResult());
            returnDO.setResult(respDto.getResult());
            returnDO.setReturnId(respDto.getReturnId());
            returnDO.setFailReason(respDto.getFailReason());
            returnDO.setDFinishTime(respDto.getFinishTime());
            returnDO.setDCreateTime(respDto.getCreateTime());
            returnMapper.insert(returnDO);
            wxProfitsharingMapper.updateById(wxProfitsharingDO);
        }

    }
}