package com.clx.performance.service.impl.loan;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.clx.message.feign.ClxMessageOpenapiFeign;
import com.clx.message.req.message.AliSmsMessageReq;
import com.clx.order.enums.DeleteStatusEnum;
import com.clx.performance.component.IdGenerateSnowFlake;
import com.clx.performance.config.ClxMessageConfig;
import com.clx.performance.config.loan.ClxPayeeConfig;
import com.clx.performance.config.loan.PaymentFromConfig;
import com.clx.performance.config.nbbank.NbBankConfig;
import com.clx.performance.constant.RabbitKeyConstants;
import com.clx.performance.dao.OrderChildDao;
import com.clx.performance.dao.OwnerBindCardRecordDao;
import com.clx.performance.dao.loan.BorrowerDao;
import com.clx.performance.dao.loan.OwnerLoanAccountDao;
import com.clx.performance.dao.loan.OwnerLoanAccountRunningWaterRecordDao;
import com.clx.performance.dao.loan.OwnerLoanRecordDao;
import com.clx.performance.dao.loan.OwnerRepaymentDao;
import com.clx.performance.dao.settle.SettlementDriverDetailDao;
import com.clx.performance.dao.settle.SettlementOwnerDetailDao;
import com.clx.performance.dto.LoanBalanceDTO;
import com.clx.performance.dao.loan.*;
import com.clx.performance.dto.OwnerLoanMqDTO;
import com.clx.performance.enums.PerformanceResultEnum;
import com.clx.performance.enums.RunningWaterTypeEnum;
import com.clx.performance.enums.loan.BankTradeEnum;
import com.clx.performance.enums.loan.OwnerLoanAccountRunningWaterRecordEnum;
import com.clx.performance.enums.loan.OwnerLoanRecordEnum;
import com.clx.performance.enums.loan.OwnerRePaymentEnum;
import com.clx.performance.enums.nbbank.NbBankStatusEnum;
import com.clx.performance.extranal.user.impl.OwnerInfoServiceImpl;
import com.clx.performance.model.OrderChild;
import com.clx.performance.model.OwnerBindCardRecord;
import com.clx.performance.model.loan.*;
import com.clx.performance.model.settle.SettlementDriverDetail;
import com.clx.performance.model.settle.SettlementOwnerDetail;
import com.clx.performance.param.pc.loan.carrier.*;
import com.clx.performance.param.pc.loan.owner.ExportPaymentApplicationFormParam;
import com.clx.performance.param.pc.loan.owner.OwnerLoanRecordParam;
import com.clx.performance.service.loan.OwnerLoanRecordService;
import com.clx.performance.service.thirdparty.nbbank.NbBankService;
import com.clx.performance.struct.loan.OwnerLoanRecordStruct;
import com.clx.performance.vo.pc.loan.carrier.*;
import com.clx.performance.vo.pc.loan.owner.BorrowerSelectVO;
import com.clx.performance.vo.pc.loan.owner.OwnerLoanRecordDetail;
import com.clx.performance.vo.pc.loan.carrier.CarrierTransferPaymentDetailVO;
import com.clx.performance.vo.pc.nbbank.NbBankOrderPayResultVO;
import com.clx.performance.vo.pc.nbbank.NbBankOrderResultVO;
import com.clx.user.feign.OwnerFeign;
import com.clx.user.vo.feign.OwnerInfoFeignVO;
import com.msl.common.base.Optional;
import com.msl.common.enums.ResultCodeEnum;
import com.msl.common.exception.ServiceSystemException;
import com.msl.common.result.Result;
import com.msl.common.utils.DateUtils;
import com.msl.document.api.feign.ContractEvidenceFeign;
import com.msl.document.api.feign.ContractTemplateFeign;
import com.msl.document.api.param.GenerateContractParam;
import com.msl.document.api.vo.ContractEvidenceRecordVo;
import com.msl.user.data.UserSessionData;
import com.msl.user.utils.TokenUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author kavin
 * Date 2024-01-20
 * Time 17:05
 */
@Service
@Slf4j
@AllArgsConstructor
public class OwnerLoanRecordServiceImpl implements OwnerLoanRecordService {

    private final BorrowerConfigDao borrowerConfigDao;
    private final OwnerLoanRecordDao ownerLoanRecordDao;

    private final OwnerLoanRecordStruct ownerLoanRecordStruct;

    private final IdGenerateSnowFlake idGenerateSnowFlake;

    private final OwnerLoanAccountDao ownerLoanAccountDao;

    private final OwnerRepaymentDao ownerRepaymentDao;

    private final OwnerBindCardRecordDao ownerBindCardRecordDao;

    private final NbBankService bankService;

    private final OwnerLoanAccountRunningWaterRecordDao ownerLoanAccountRunningWaterRecordDao;

    private final BorrowerDao borrowerDao;

    private final BankTradeDao bankTradeDao;

    private final OwnerFeign ownerFeign;

    private final ContractEvidenceFeign contractEvidenceFeign;
    private final ContractTemplateFeign contractTemplateFeign;
    private final PaymentFromConfig paymentFromConfig;
    private final NbBankConfig nbBankConfig;
    private final OwnerInfoServiceImpl ownerInfoService;
    private final ClxMessageOpenapiFeign clxMessageOpenapiFeign;
    private final ClxMessageConfig messageConfig;

    private final OrderChildLoanRetryRecordDao orderChildLoanRetryRecordDao;

    private final SettlementDriverDetailDao settlementDriverDetailDao;

    private final SettlementOwnerDetailDao settlementOwnerDetailDao;

    private final OrderChildDao orderChildDao;

    private final RabbitTemplate rabbitTemplate;


    @Override
    public IPage<OwnerLoanRecordVO> pageOwnerLoanRecord(PageCarrierOwnerLoanRecordParam param) {
        IPage<OwnerLoanRecord> page = ownerLoanRecordDao.pageOwnerLoanRecord(param);
        List<OwnerLoanRecordVO> records = ownerLoanRecordStruct.convertList(page.getRecords());
        IPage<OwnerLoanRecordVO> returnPage = new Page<>();
        returnPage.setPages(page.getPages());
        returnPage.setTotal(page.getTotal());
        returnPage.setRecords(records);
        return returnPage;
    }

    @Override
    public void ownerLoanRecordSubmit(OwnerLoanRecordParam param) {

        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();
        OwnerBindCardRecord ownerBindCardRecord = ownerBindCardRecordDao.getOwnerBindCardByUserNo(loginUserInfo.getUserNo());

        OwnerLoanRecord entity = new OwnerLoanRecord();
        LocalDateTime createTime = LocalDateTime.now();
        LocalDateTime loanRepaymentTime = createTime.plusDays(param.getExpireDay());
        entity.setLoanRepaymentTime(loanRepaymentTime);
        entity.setLoanNo(idGenerateSnowFlake.nextId(1L));
        entity.setCreateTime(createTime);
        entity.setOwnerUserNo(loginUserInfo.getUserNo());
        entity.setOwnerUserName(loginUserInfo.getUserName());
        entity.setMobile(loginUserInfo.getUserMobile());
        entity.setLoanBalance(param.getLoanBalance());

        entity.setBorrowerId(param.getBorrowerId());
        entity.setBorrower(param.getBorrower());

        entity.setLendingParty(param.getLendingParty());
        entity.setLendingPartyAccount(ownerBindCardRecord.getBankCardNumber());

        entity.setPayee(param.getPayee());
        entity.setPayeeAccount(param.getPayeeAccount());
        entity.setCreateBy(loginUserInfo.getUserName());
        entity.setStatus(OwnerLoanRecordEnum.Status.APPROVE_WAIT.getCode());
        ownerLoanRecordDao.saveEntity(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void ownerLoanRecordApprove(CarrierOwnerLoanRecordApproveParam param) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getOneByField(OwnerLoanRecord::getLoanNo, param.getLoanNo()).get();
        if (param.getStatus()) {
            borrowerConfigDao.selectByBorrowerIdAndType(ownerLoanRecord.getBorrowerId(), param.getLoanType())
                    .orElseThrow(PerformanceResultEnum.BORROWER_CONFIG_TYPE_NOT_SUPPORT_ERROR);
            Borrower borrower = borrowerDao.getEntityByKey(ownerLoanRecord.getBorrowerId())
                    .orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);

            //同意
            ownerLoanRecord.setLoanType(param.getLoanType());

            //设置借款记录中借款方和借出方的银行账户信息
            OwnerInfoFeignVO ownerInfoFeignVO = ownerInfoService.getOwnerInfo(ownerLoanRecord.getOwnerUserNo());
            String ownerAccountBank = ownerInfoFeignVO.getOwnerAccountBank();
            ownerLoanRecord.setLendingParty(ownerInfoFeignVO.getName());
            ownerLoanRecord.setLendingPartyBank(ownerAccountBank);
            ownerLoanRecord.setLendingPartyAccount(ownerInfoFeignVO.getOwnerBankAccount());
            ownerLoanRecord.setLendingPartyOpenBank(ownerInfoFeignVO.getOwnerOpenBank());
            ownerLoanRecord.setLendingPartyBankCode(ownerInfoFeignVO.getOwnerOpenBankNumber());
            //设置借款记录中借出方的银行账户信息
            ownerLoanRecord.setBorrower(borrower.getName());
            ownerLoanRecord.setBorrowerAccount(borrower.getBankCardNo());
            ownerLoanRecord.setBorrowerBank(borrower.getBankName());
            ownerLoanRecord.setBorrowerOpenBank(borrower.getOpenBankName());
            ownerLoanRecord.setBorrowerBankCode(borrower.getOpenBankId());

            if (OwnerLoanRecordEnum.LoanType.FUND.getCode().equals(param.getLoanType())) {
                ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.PAY_WAIT.getCode());
                //fundPayProcess(param, ownerLoanRecord, borrower);
            } else {
                // 虚拟币 直接增加 更改状态审批通过
                ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.APPROVE_PASS.getCode());
                while (true) {
                    OwnerLoanAccount ownerLoanAccount = ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo,
                            ownerLoanRecord.getOwnerUserNo()).get();
                    OwnerLoanAccount entity = new OwnerLoanAccount();
                    entity.setId(ownerLoanAccount.getId());
                    entity.setFundingAccountBalance(BigDecimal.ZERO);
                    entity.setFundingFrozenBalance(BigDecimal.ZERO);
                    entity.setFundingUsableBalance(BigDecimal.ZERO);
                    entity.setVirtuallyAccountBalance(ownerLoanRecord.getLoanBalance());
                    entity.setVirtuallyFrozenBalance(BigDecimal.ZERO);
                    entity.setVirtuallyUsableBalance(ownerLoanRecord.getLoanBalance());
                    entity.setModifiedTime(ownerLoanAccount.getModifiedTime());
                    Integer flag = ownerLoanAccountDao.updateAccountCAS(entity, LocalDateTime.now(), true);
                    if (flag == 1) {
                        //生成借款流水
                        initOwnerLoanRunningWaterRecord(ownerLoanRecord);
                        //生成还款记录
                        initOwnerRepayment(ownerLoanRecord);
                        break;
                    }
                }
            }

        } else {
            //拒绝
            ownerLoanRecord.setLoanType(param.getLoanType());
            ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.APPROVE_REFUSE.getCode());
        }
        //更新借款记录
        ownerLoanRecordDao.updateEntityByKey(ownerLoanRecord);
    }

    private void fundPayProcess(CarrierOwnerLoanRecordApproveParam param, OwnerLoanRecord ownerLoanRecord, Borrower borrower) {
        //资金
        BankTrade bankTrade = new BankTrade();
        bankTrade.setRemark("借款单");
        if (OwnerLoanRecordEnum.PayChannel.ORDER_DIRECT_PAY.getCode().equals(param.getPayChannel())) {
            // 订单支付
            NbBankOrderPayResultVO orderPayResultVO = bankService.orderDirectPay(param.getLoanNo(), "借款单",
                    ownerLoanRecord.getLoanBalance().intValue(), borrower.getOpenBankId(), borrower.getBankCardNo(), borrower.getName());
            ownerLoanRecord.setRunningWaterOpenNo(orderPayResultVO.getTransSeqNo());
            ownerLoanRecord.setMerchantRunningWaterNo(orderPayResultVO.getMerSeqNo());
            ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.PAYING.getCode());
            ownerLoanRecord.setLoanResidueBalance(ownerLoanRecord.getLoanBalance());

            ownerLoanRecord.setLendingParty(borrower.getName());
            ownerLoanRecord.setLendingPartyAccount(borrower.getBankCardNo());

            bankTrade.setTradeType(BankTradeEnum.TradeType.ORDER_DIRECT_PAY.getCode());
        } else {
            // 转账支付
            NbBankOrderPayResultVO orderPayResultVO = bankService.orderTransferPay(ownerLoanRecord.getLoanBalance().intValue());
            ownerLoanRecord.setRunningWaterOpenNo(orderPayResultVO.getTransSeqNo());
            ownerLoanRecord.setMerchantRunningWaterNo(orderPayResultVO.getMerSeqNo());
            ownerLoanRecord.setRemittanceIdentificationCode(orderPayResultVO.getSignNo());
            ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.PAYING.getCode());
            ownerLoanRecord.setLoanResidueBalance(ownerLoanRecord.getLoanBalance());
            ownerLoanRecord.setRemittanceIdentificationCode(orderPayResultVO.getSignNo());
            bankTrade.setTradeType(BankTradeEnum.TradeType.ORDER_TRANSFER_PAY.getCode());
        }
        // 更新借款支付信息
        ownerLoanRecordDao.updatePay(ownerLoanRecord);

        bankTrade.setRelationNo(ownerLoanRecord.getLoanNo().toString());
        bankTrade.setOrderType(BankTradeEnum.OrderType.OWNER_LOAN_RECORD.getCode());
        bankTrade.setAmount(ownerLoanRecord.getLoanBalance());
        bankTrade.setMerchantRunningWaterNo(ownerLoanRecord.getMerchantRunningWaterNo());
        bankTrade.setRunningWaterOpenNo(ownerLoanRecord.getRunningWaterOpenNo());

        // 保存银行交易记录
        bankTradeDao.saveEntity(bankTrade);
    }


    @Override
    public IPage<OwnerLoanRecordVO> pageOwnerLoanRecordOfOwner(PageOwnerLoanRecordOfOwner param) {
        return ownerLoanRecordDao.pageByParam(param);

    }

    /**
     * 生成借款流水
     *
     * @param ownerLoanRecord
     */
    private void initOwnerLoanRunningWaterRecord(OwnerLoanRecord ownerLoanRecord) {
        OwnerLoanAccount ownerLoanAccount= ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo, ownerLoanRecord.getOwnerUserNo()).get();
        ownerLoanRecordDao.updateEntityByKey(ownerLoanRecord);
        OwnerLoanAccountRunningWaterRecord record = new OwnerLoanAccountRunningWaterRecord()
                .setOwnerUserNo(ownerLoanRecord.getOwnerUserNo())
                .setOwnerUserName(ownerLoanRecord.getOwnerUserName())
                .setMobile(ownerLoanRecord.getMobile())
                .setRunningWaterNo(idGenerateSnowFlake.nextId(2L))
                .setLoanNo(ownerLoanRecord.getLoanNo())
                .setRunningWaterType(RunningWaterTypeEnum.Status.LOAN.getCode())
                .setAlterationBalance(ownerLoanRecord.getLoanBalance())
                .setUseAbleBalance(ownerLoanAccount.getFundingUsableBalance().add(ownerLoanAccount.getVirtuallyUsableBalance()))
                .setFrozenBalance(ownerLoanAccount.getFundingFrozenBalance().add(ownerLoanAccount.getVirtuallyFrozenBalance()))
                .setAccountBalance(ownerLoanAccount.getFundingAccountBalance().add(ownerLoanAccount.getVirtuallyAccountBalance()))
                .setCreateBy("系统");
        ownerLoanAccountRunningWaterRecordDao.saveEntity(record);
    }

    /**
     * 生成还款记录
     *
     * @param ownerLoanRecord
     */
    private void initOwnerRepayment(OwnerLoanRecord ownerLoanRecord) {
        OwnerRepayment ownerRepayment = new OwnerRepayment();
        ownerRepayment.setLoanRepaymentTime(ownerLoanRecord.getLoanRepaymentTime());
        ownerRepayment.setMobile(ownerLoanRecord.getMobile());
        ownerRepayment.setOwnerUserName(ownerLoanRecord.getOwnerUserName());
        ownerRepayment.setOwnerUserNo(ownerLoanRecord.getOwnerUserNo());
        ownerRepayment.setLoanNo(ownerLoanRecord.getLoanNo());
        ownerRepayment.setCreateBy(ownerLoanRecord.getOwnerUserName());
        ownerRepayment.setRepaymentBalance(ownerLoanRecord.getLoanBalance());
        ownerRepayment.setRepaymentNo(idGenerateSnowFlake.nextId(2L));


        ownerRepayment.setPayee(ownerLoanRecord.getPayee());
        ownerRepayment.setPayeeAccount(ownerLoanRecord.getPayeeAccount());
        ownerRepayment.setPayChannel(ownerLoanRecord.getPayChannel());
        ownerRepayment.setPayeeBank(ownerLoanRecord.getPayeeBank());
        ownerRepayment.setPayeeBankCode(ownerLoanRecord.getPayeeBankCode());
        ownerRepayment.setBeOverdue(OwnerRePaymentEnum.BeOverdue.NO.getCode());
        ownerRepayment.setCreateBy("系统");
        ownerRepayment.setStatus(OwnerRePaymentEnum.Status.PAY_WAIT.getCode());
        ownerRepaymentDao.saveEntity(ownerRepayment);
    }

    @Override
    public OwnerLoanRecordDetail getOwnerLoanRecordDetail() {
        List<BorrowerSelectVO> borrowerList = borrowerDao.selectAllBorrowConfig();
        Map<String, BorrowerSelectVO> map = new TreeMap<>();
        for (BorrowerSelectVO item : borrowerList) {
            if (map.containsKey(item.getName())) {
                //选择虚拟货币的借出方，虚拟货币的借出方时效较短，优先级高。
                if (Objects.equals(item.getType(), OwnerLoanRecordEnum.LoanType.VIRTUAL_CURRENCY.getCode())) {
                    map.put(item.getName(), item);
                }
            } else {
                map.put(item.getName(), item);
            }
        }
        OwnerLoanRecordDetail detail = new OwnerLoanRecordDetail();
        //借出方
        detail.setBorrowerList(new ArrayList<>(map.values()));
        UserSessionData loginUserInfo = TokenUtil.getLoginUserInfo();

        //企业信息
        OwnerInfoFeignVO ownerInfoFeignVO = Optional.ofNullable(ownerFeign.getUserCompany(loginUserInfo.getUserNo())).filter(
                Result::succeed).map(Result::getData).orElseThrow(ResultCodeEnum.FAIL);

        detail.setCompanyName(ownerInfoFeignVO.getCompanyName());
        //收款方
        List<PayeeVO> payeeList = ClxPayeeConfig.payee.stream().map(
                item -> PayeeVO.builder().name(item.getName()).account(item.getAccount()).id(item.getId()).build()
        ).collect(Collectors.toList());
        detail.setPayeeList(payeeList);

        return detail;
    }


    @Override
    public CarrierCashierInfoVO getCashierInfo(Integer id) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getEntityByKey(id).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        CarrierCashierInfoVO vo = ownerLoanRecordStruct.convertCashierInfo(ownerLoanRecord);
        Borrower borrower = borrowerDao.getEntityByKey(ownerLoanRecord.getBorrowerId()).orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        if (Objects.equals(borrower.getDeleteStatus(), DeleteStatusEnum.NO.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.DATA_NOT_FIND);
        }
        vo.setOrderPayWay(false);
        String bankName = borrower.getBankName();
        if (nbBankConfig.getOrderSupportBank().contains(bankName)) {
            vo.setOrderPayWay(true);
        }
        vo.setBankName(bankName);
        vo.setPayAccount(borrower.getBankCardNo());
        return vo;
    }


    @Override
    public CarrierOwnerOrderPaymentDetailVO getOrderPaymentDetail(Integer id) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getEntityByKey(id).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        CarrierOwnerOrderPaymentDetailVO vo = ownerLoanRecordStruct.convertOrderPaymentDetail(ownerLoanRecord);
        vo.setPayer(ownerLoanRecord.getBorrower());
        vo.setPayerAccount(ownerLoanRecord.getPayeeAccount());
        ClxPayeeConfig.PayeeConfig payeeMap = ClxPayeeConfig.getPayee(ClxPayeeConfig.XXH_ID);
        vo.setPayee(payeeMap.getName());
        vo.setPayeeAccount(payeeMap.getAccount());
        //计算倒计时时间，下单时间 + 30天 为截止时间
        LocalDateTime endTime = ownerLoanRecord.getCreateTime().plusDays(30);
        vo.setFinalPaymentTime(DateUtils.formatDateTime(endTime).get());
        vo.setLoanNo(ownerLoanRecord.getLoanNo());
        return vo;
    }

    @Override
    public CarrierTransferPaymentDetailVO getTransferPaymentDetail(Integer id) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getEntityByKey(id).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        ClxPayeeConfig.PayeeConfig payeeMap = ClxPayeeConfig.getPayee(ClxPayeeConfig.XXH_ID);
        return CarrierTransferPaymentDetailVO.builder().payee(payeeMap.getName())
                .payeeAccount(payeeMap.getAccount()).payeeBank(payeeMap.getBank()).payeeBankNo(payeeMap.getBankNo())
                .loanBalance(ownerLoanRecord.getLoanBalance())
                .remittanceIdentificationCode(ownerLoanRecord.getRemittanceIdentificationCode())
                .status(ownerLoanRecord.getStatus()).finalPaymentTime(ownerLoanRecord.getCloseOrderTime())
                .loanNo(ownerLoanRecord.getLoanNo()).build();
    }

    @Override
    public String savePaymentApplicationForm(ExportPaymentApplicationFormParam param) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getEntityByKey(param.getId()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        ExportOwnerLoanRecordVO exportOwnerLoanRecordVO = ownerLoanRecordStruct.convert(ownerLoanRecord);
        //货主借款：  收款方鑫祥和  付款方为诚联信    借款方为货主
        Map<String, String> map = JSON.parseObject(JSON.toJSONString(exportOwnerLoanRecordVO), new TypeReference<Map<String, String>>() {
        });
        // 创建合同
        GenerateContractParam feignParam = new GenerateContractParam();
        if (Objects.equals(OwnerRePaymentEnum.Channel.ORDER.getCode(), param.getPayWay())) {
            feignParam.setTemplateNo(Long.valueOf(paymentFromConfig.getLoanOrderTemplateNo()));
        } else {
            feignParam.setTemplateNo(Long.valueOf(paymentFromConfig.getLoanTransferTemplateNo()));
        }

        feignParam.setParametersValueMap(map);
        Result<Long> longResult = contractTemplateFeign.generateContract(feignParam);
        Result<ContractEvidenceRecordVo> contractEvidenceDetail = contractEvidenceFeign.getContractEvidenceDetail(longResult.getData());
        return contractEvidenceDetail.getData().getFileUrl();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void getLoanBalance(LoanBalanceDTO param) {
        BigDecimal orderChildPrice = param.getOrderChildPrice();
        Long userNo = param.getOwnerUserNo();
        String childNo = param.getChildNo();
        log.info("执行借款,借款金额{}, 货主编号：{}, 运单号：{}", orderChildPrice, userNo, childNo);

        List<OwnerLoanRecord> updateList = new LinkedList<>();
        List<OwnerLoanRecord> ownerLoanRecords = ownerLoanRecordDao.selectLoanBalance(userNo);
        BigDecimal orderChildPriceTemp = orderChildPrice;
        log.info("查询ownerLoanRecords:{}", JSONUtil.parse(ownerLoanRecords));
        for (OwnerLoanRecord ownerLoanRecord : ownerLoanRecords) {
            BigDecimal loanResidueBalance = ownerLoanRecord.getLoanResidueBalance();
            log.info("当前借款金额：{},借款单号{},借款剩余金额{}", orderChildPriceTemp, ownerLoanRecord.getLoanNo(), loanResidueBalance);
            if (loanResidueBalance.compareTo(orderChildPriceTemp) >= 0) {
                log.info("当前借款单的剩余金额足够借款金额");
                generateFrozenOwnerLoanRunningWater(ownerLoanRecord, childNo, orderChildPriceTemp);
                updateList.add(ownerLoanRecord.setLoanResidueBalance(loanResidueBalance.subtract(orderChildPriceTemp)));
                orderChildPriceTemp = BigDecimal.ZERO;
                break;
            } else {
                log.info("当前借款单的剩余金额不够抵扣借款金额,先进行扣除当前借款所有剩余");
                generateFrozenOwnerLoanRunningWater(ownerLoanRecord, childNo, loanResidueBalance);
                updateList.add(ownerLoanRecord.setLoanResidueBalance(BigDecimal.ZERO));
                orderChildPriceTemp = orderChildPriceTemp.subtract(loanResidueBalance);
            }
            log.info("当前orderChildPriceTemp：{}", orderChildPriceTemp);
        }

        if (orderChildPriceTemp.compareTo(BigDecimal.ZERO) != 0) {
            //当前钱不够,需要记录,流程卡死,不往下走

            throw new ServiceSystemException(PerformanceResultEnum.HTTP_ERROR, "执行借款发现：货主货款不足");
        }

        for (OwnerLoanRecord update : updateList) {
            ownerLoanRecordDao.updateEntityByKey(update);
        }
        log.info("执行借款成功,借款金额{}, 货主编号：{}, 运单号：{}", orderChildPrice, userNo, childNo);

    }


    @Override
    public void updateOwnerLoanRecordPay(CarrierOwnerLoanRecordPayParam param) {

    }

    @Override
    public void updateOwnerLoanRecordRepay(CarrierOwnerLoanRecordRepayParam param) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getOneByField(OwnerLoanRecord::getLoanNo, param.getLoanNo())
                .orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);

        if (!Objects.equals(ownerLoanRecord.getStatus(), OwnerLoanRecordEnum.Status.PAY_FAIL.getCode())) {
            throw new ServiceSystemException(PerformanceResultEnum.OWNER_LOAN_RECORD_PAY_STATUS_ERROR);
        }

        borrowerConfigDao.selectByBorrowerIdAndType(ownerLoanRecord.getBorrowerId(), OwnerLoanRecordEnum.LoanType.FUND.getCode())
                .orElseThrow(PerformanceResultEnum.BORROWER_CONFIG_TYPE_NOT_SUPPORT_ERROR);
        Borrower borrower = borrowerDao.getEntityByKey(ownerLoanRecord.getBorrowerId())
                .orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);

        BankTrade bankTrade = new BankTrade();
        if (OwnerLoanRecordEnum.PayChannel.ORDER_DIRECT_PAY.getCode().equals(param.getPayChannel())) {
            //资金 TODO 调宁波银企直连的产品，从诚联信账户中给鑫祥和执行转账
            // 订单支付
            NbBankOrderPayResultVO orderPayResultVO = bankService.orderDirectPay(param.getLoanNo(), "借款单",
                    ownerLoanRecord.getLoanBalance().intValue(), borrower.getOpenBankId(), borrower.getBankCardNo(), borrower.getName());
            ownerLoanRecord.setRunningWaterOpenNo(orderPayResultVO.getTransSeqNo());
            ownerLoanRecord.setMerchantRunningWaterNo(orderPayResultVO.getMerSeqNo());
            ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.PAYING.getCode());
            ownerLoanRecord.setLoanResidueBalance(ownerLoanRecord.getLoanBalance());

            ownerLoanRecord.setLendingParty(borrower.getName());
            ownerLoanRecord.setLendingPartyAccount(borrower.getBankCardNo());

            bankTrade.setTradeType(BankTradeEnum.TradeType.ORDER_DIRECT_PAY.getCode());
        } else {
            //资金 TODO 调宁波银企直连的产品，从诚联信账户中给鑫祥和执行转账
            // 转账支付
            NbBankOrderPayResultVO orderPayResultVO = bankService.orderTransferPay(ownerLoanRecord.getLoanBalance().intValue());
            ownerLoanRecord.setRunningWaterOpenNo(orderPayResultVO.getTransSeqNo());
            ownerLoanRecord.setMerchantRunningWaterNo(orderPayResultVO.getMerSeqNo());
            ownerLoanRecord.setRemittanceIdentificationCode(orderPayResultVO.getSignNo());
            ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.PAYING.getCode());
            ownerLoanRecord.setLoanResidueBalance(ownerLoanRecord.getLoanBalance());

            ownerLoanRecord.setLendingParty(null);
            ownerLoanRecord.setLendingPartyAccount(null);

            bankTrade.setTradeType(BankTradeEnum.TradeType.ORDER_TRANSFER_PAY.getCode());

        }
        // 更新借款支付信息
        ownerLoanRecordDao.updatePay(ownerLoanRecord);

        bankTrade.setOrderType(BankTradeEnum.OrderType.OWNER_LOAN_RECORD.getCode());
        bankTrade.setAmount(ownerLoanRecord.getLoanBalance());
        bankTrade.setMerchantRunningWaterNo(ownerLoanRecord.getMerchantRunningWaterNo());
        bankTrade.setRunningWaterOpenNo(ownerLoanRecord.getRunningWaterOpenNo());

        // 保存银行交易记录
        bankTradeDao.saveEntity(bankTrade);

    }


    @Override
    public void updateOwnerLoanRecordPayCancel(CarrierOwnerLoanRecordPayCancelParam param) {

    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void payFail(String merSeqNo) {
        BankTrade bankTrade = bankTradeDao.selectByMerchantRunningWaterNo(merSeqNo).get();
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getOneByField(OwnerLoanRecord::getLoanNo, bankTrade.getRelationNo())
                .orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        if (Objects.equals(ownerLoanRecord.getStatus(), OwnerLoanRecordEnum.Status.PAYING.getCode())) {
            log.info("借款单非支付中状态, loanNo:{}", bankTrade.getRelationNo());
            return;
        }

        ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.PAY_FAIL.getCode());

        ownerLoanRecordDao.updateStatusById(ownerLoanRecord);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(String merSeqNo) {
        BankTrade bankTrade = bankTradeDao.selectByMerchantRunningWaterNo(merSeqNo).get();

        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getOneByField(OwnerLoanRecord::getLoanNo, bankTrade.getRelationNo())
                .orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);

        if (Objects.equals(ownerLoanRecord.getStatus(), OwnerLoanRecordEnum.Status.PAYING.getCode())) {
            log.info("借款单非支付中状态, loanNo:{}", ownerLoanRecord.getLoanNo());
            return;
        }

        ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.PAY_SUCCESS.getCode());
        while (true) {
            OwnerLoanAccount ownerLoanAccount = ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo,
                    ownerLoanRecord.getOwnerUserNo()).get();
            OwnerLoanAccount entity = new OwnerLoanAccount();
            entity.setId(ownerLoanAccount.getId());
            entity.setFundingAccountBalance(BigDecimal.ZERO);
            entity.setFundingFrozenBalance(BigDecimal.ZERO);
            entity.setFundingUsableBalance(BigDecimal.ZERO);
            entity.setVirtuallyAccountBalance(ownerLoanRecord.getLoanBalance());
            entity.setVirtuallyFrozenBalance(BigDecimal.ZERO);
            entity.setVirtuallyUsableBalance(ownerLoanRecord.getLoanBalance());
            entity.setModifiedTime(ownerLoanAccount.getModifiedTime());
            Integer flag = ownerLoanAccountDao.updateAccountCAS(entity, LocalDateTime.now(), true);
            if (flag == 1) {
                //生成借款流水
                initOwnerLoanRunningWaterRecord(ownerLoanRecord);
                //生成还款记录
                initOwnerRepayment(ownerLoanRecord);
                ownerLoanRecordDao.updateStatusById(ownerLoanRecord);
                //执行货主未处理借款标志的计费(运单确认生成后因为借款不够阻塞流程的相关计费进行处理)
                orderChildLoanProcess(ownerLoanRecord.getOwnerUserNo());
                break;
            }
        }


    }

    /**
     * 执行货主未处理借款标志的计费
     *
     * @param ownerUserNo
     */
    @Override
    public void orderChildLoanProcess(Long ownerUserNo) {
        List<OrderChildLoanRetryRecord> orderChildLoanRetryRecordList = orderChildLoanRetryRecordDao.selectListByIdAsc();
        for (OrderChildLoanRetryRecord retryRecord : orderChildLoanRetryRecordList) {
            //生成借款标识
            SettlementDriverDetail settlementDriverDetail = settlementDriverDetailDao.getEntityByKey(retryRecord.getSettlementDriverId()).get();
            SettlementOwnerDetail settlementOwnerDetail = settlementOwnerDetailDao.getEntityByKey(retryRecord.getSettlementOwnerId()).get();
            OrderChild orderChild = orderChildDao.getByChildNo(settlementDriverDetail.getChildNo()).get();
            OwnerLoanMqDTO dto = new OwnerLoanMqDTO();
            dto.setSettlementOwnerDetail(settlementOwnerDetail);
            dto.setSettlementDriverDetail(settlementDriverDetail);
            dto.setOrderChild(orderChild);
            //货主借款监听器处理
            Message message = MessageBuilder.withBody(JSONUtil.parse(dto).toString().getBytes()).build();
            rabbitTemplate.send(
                    RabbitKeyConstants.OWNER_LOAN_EXCHANGE, RabbitKeyConstants.OWNER_LOAN_ROUTE_KEY, message
            );
            orderChildLoanRetryRecordDao.deleteByKey(retryRecord.getId());
        }
    }

    @Override
    public void ownerLoanRecordRetryPay(String loanNo) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getOneByField(OwnerLoanRecord::getLoanNo, loanNo)
                .orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.APPROVE_WAIT.getCode());
        // 更新借款信息
        ownerLoanRecordDao.updateStatusById(ownerLoanRecord);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void ownerLoanRecordCancelPay(String loanNo) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getOneByField(OwnerLoanRecord::getLoanNo, loanNo)
                .orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        if (!ownerLoanRecord.getStatus().equals(OwnerLoanRecordEnum.Status.APPROVE_WAIT.getCode())
                && !ownerLoanRecord.getStatus().equals(OwnerLoanRecordEnum.Status.PAYING.getCode())
        ) {
            log.info("借款记录状态异常，不能取消");
            throw new ServiceSystemException(PerformanceResultEnum.OWNER_LOAN_RECORD_CANCEL_STATUS_ERROR);
        }

        NbBankOrderResultVO result = bankService.getResult(loanNo);
        Integer status = result.getStatus();

        if (!NbBankStatusEnum.Status.INIT.getCode().equals(status)
                && !NbBankStatusEnum.Status.FAIL.getCode().equals(status)
                && !NbBankStatusEnum.Status.NOT_FOUND.getCode().equals(status)
        ) {
            log.info("宁波银行响应当前业务，不能取消{}", JSONUtil.parse(result));
            throw new ServiceSystemException(PerformanceResultEnum.OWNER_LOAN_RECORD_CANCEL_STATUS_ERROR);
        }
        log.info("借款记录取消，更新状态为待审批");
        ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.APPROVE_WAIT.getCode());
        ownerLoanRecordDao.updateStatusById(ownerLoanRecord);

        BankTrade bankTrade = bankTradeDao.selectByRelationNo(loanNo).get();
        Integer tradeType = bankTrade.getTradeType();
        // 调用宁波退款
        bankService.refund(tradeType, bankTrade.getMerchantRunningWaterNo(), Integer.valueOf(ownerLoanRecord.getLoanBalance().movePointRight(2).toString()), bankTrade.getRunningWaterOpenNo());

    }


    public void generateFrozenOwnerLoanRunningWater(OwnerLoanRecord ownerLoanRecord, String childNo, BigDecimal orderChildPrice) {
        log.info("13.生成借款冻结流水");
        OwnerLoanAccount update = new OwnerLoanAccount();
        while (true) {
            ownerLoanAccountUpdate(ownerLoanRecord, orderChildPrice, update);
            Integer i = ownerLoanAccountDao.updateAccountCAS(update, LocalDateTime.now(), false);
            log.info("CAS更新结果:{}", i);
            if (i == 1) {
                OwnerLoanAccountRunningWaterRecord waterRecord = new OwnerLoanAccountRunningWaterRecord();
                waterRecord.setLoanNo(ownerLoanRecord.getLoanNo());
                waterRecord.setOwnerUserName(ownerLoanRecord.getOwnerUserName());
                waterRecord.setOwnerUserNo(ownerLoanRecord.getOwnerUserNo());
                waterRecord.setMobile(ownerLoanRecord.getMobile());
                waterRecord.setLoanType(ownerLoanRecord.getLoanType());
                waterRecord.setRunningWaterNo(idGenerateSnowFlake.nextId(1L));
                waterRecord.setChildNo(childNo);
                waterRecord.setRunningWaterType(OwnerLoanAccountRunningWaterRecordEnum.RunWaterType.APPROVE_FROZEN.getCode());
                waterRecord.setAlterationBalance(orderChildPrice);
                OwnerLoanAccount account = ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo,
                        ownerLoanRecord.getOwnerUserNo()).get();
                waterRecord.setAccountBalance(account.getVirtuallyAccountBalance().add(account.getFundingAccountBalance()));
                waterRecord.setUseAbleBalance(account.getVirtuallyUsableBalance().add(account.getFundingUsableBalance()));
                waterRecord.setFrozenBalance(account.getVirtuallyFrozenBalance().add(account.getFundingFrozenBalance()));
                waterRecord.setCreateBy("系统");
                log.info("开始生成流水");
                ownerLoanAccountRunningWaterRecordDao.saveEntity(waterRecord);
                log.info("生成流水成功");
                break;
            }
        }

    }

    public void ownerLoanAccountUpdate(OwnerLoanRecord ownerLoanRecord, BigDecimal orderChildPrice, OwnerLoanAccount update) {
        log.info("执行CAS前置借款账户信息赋值");
        Integer loanType = ownerLoanRecord.getLoanType();
        OwnerLoanAccount account = ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo,
                ownerLoanRecord.getOwnerUserNo()).get();
        if (OwnerLoanRecordEnum.LoanType.VIRTUAL_CURRENCY.getCode().equals(loanType)) {
            log.info("进行虚拟货币信息赋值");
            update.setId(account.getId());
            update.setModifiedTime(account.getModifiedTime());
            update.setVirtuallyFrozenBalance(orderChildPrice);
            update.setVirtuallyUsableBalance(orderChildPrice);
            update.setVirtuallyAccountBalance(BigDecimal.ZERO);

            update.setFundingAccountBalance(BigDecimal.ZERO);
            update.setFundingFrozenBalance(BigDecimal.ZERO);
            update.setFundingUsableBalance(BigDecimal.ZERO);
        } else {
            log.info("进行资金信息赋值");
            update.setId(account.getId());
            update.setModifiedTime(account.getModifiedTime());
            update.setFundingFrozenBalance(orderChildPrice);
            update.setFundingUsableBalance(orderChildPrice);
            update.setFundingAccountBalance(BigDecimal.ZERO);

            update.setVirtuallyFrozenBalance(BigDecimal.ZERO);
            update.setVirtuallyUsableBalance(BigDecimal.ZERO);
            update.setVirtuallyAccountBalance(BigDecimal.ZERO);
        }
    }

    @Override
    public String sendLoanSms(String mobile, Long loanNo) {
        AliSmsMessageReq req = new AliSmsMessageReq();
        req.setTemplateCode(messageConfig.getRepaymentTemplateCode());
        JSONObject jsonObject = new JSONObject();
        //随机生成4位数字
        jsonObject.set("borrowNo", loanNo);
        req.setChannelId(messageConfig.getChannelId());
        req.setAppId(messageConfig.getAppId().toString());
        req.setMobile(mobile);
        req.setContent(jsonObject.toString());
        req.setExpire(300L);
        clxMessageOpenapiFeign.sendAliSms(req);

        return UUID.randomUUID().toString();
    }

    @Override
    public String sendOrderPaySms(String mobile, String payee, String payeeAccount) {
        AliSmsMessageReq req = new AliSmsMessageReq();
        req.setTemplateCode(messageConfig.getOrderPayTemplateCode());
        JSONObject jsonObject = new JSONObject();
        jsonObject.set("companyName", payee);
        jsonObject.set("bankAccount", payeeAccount);
        req.setChannelId(messageConfig.getChannelId());
        req.setAppId(messageConfig.getAppId().toString());
        req.setMobile(mobile);
        req.setContent(jsonObject.toString());
        req.setExpire(300L);
        clxMessageOpenapiFeign.sendAliSms(req);

        return UUID.randomUUID().toString();
    }

    @Override
    public Integer ownerLoanRecordSubmitNbBank(CarrierOwnerLoanRecordApproveParam param) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getOneByField(OwnerLoanRecord::getLoanNo, param.getLoanNo()).get();
        if (!OwnerLoanRecordEnum.Status.PAY_WAIT.getCode().equals(ownerLoanRecord.getStatus())) {
            throw new ServiceSystemException(PerformanceResultEnum.OWNER_LOAN_RECORD_PAY_WAIT_ERROR);
        }
        borrowerConfigDao.selectByBorrowerIdAndType(ownerLoanRecord.getBorrowerId(), param.getLoanType())
                .orElseThrow(PerformanceResultEnum.BORROWER_CONFIG_TYPE_NOT_SUPPORT_ERROR);
        Borrower borrower = borrowerDao.getEntityByKey(ownerLoanRecord.getBorrowerId())
                .orElseThrow(PerformanceResultEnum.DATA_NOT_FIND);
        fundPayProcess(param, ownerLoanRecord, borrower);
        return ownerLoanRecord.getId();
    }

}
