package com.clx.performance.component;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.clx.order.feign.OrderFeign;
import com.clx.order.vo.feign.FeignOrderInfoVO;
import com.clx.order.vo.pc.owner.OwnerQuotationDetailVO;
import com.clx.performance.constant.RedissonConstants;
import com.clx.performance.dao.OrderChildDao;
import com.clx.performance.dao.OwnerRunningWaterRecordDao;
import com.clx.performance.dao.loan.OwnerLoanAccountDao;
import com.clx.performance.dao.loan.OwnerLoanAccountRunningWaterRecordDao;
import com.clx.performance.dao.loan.OwnerRepaymentDao;
import com.clx.performance.dto.LoanBalanceDTO;
import com.clx.performance.enums.OrderGoodsOverWeightEnum;
import com.clx.performance.enums.OwnerAccountEnum;
import com.clx.performance.enums.PerformanceResultEnum;
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.settle.SettlementOwnerEnum;
import com.clx.performance.model.OrderChild;
import com.clx.performance.model.OrderGoods;
import com.clx.performance.model.OwnerRunningWaterRecord;
import com.clx.performance.model.loan.OwnerLoanAccount;
import com.clx.performance.model.loan.OwnerLoanAccountRunningWaterRecord;
import com.clx.performance.model.loan.OwnerRepayment;
import com.clx.performance.model.settle.SettlementDriverDetail;
import com.clx.performance.model.settle.SettlementOwnerDetail;
import com.clx.performance.service.loan.OwnerLoanRecordService;
import com.clx.user.vo.feign.OwnerInfoFeignVO;
import com.msl.common.base.Optional;
import com.msl.common.exception.ServiceSystemException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
@AllArgsConstructor
@Slf4j
public class OrderChildLoanComponent {

    private final OrderFeign orderFeign;

    private final OwnerRunningWaterRecordDao ownerRunningWaterRecordDao;

    private final OrderChildDao orderChildDao;

    private final OwnerLoanAccountDao ownerLoanAccountDao;

    private final OwnerRepaymentDao ownerRepaymentDao;

    private final IdGenerateSnowFlake idGenerateSnowFlake;

    private final OwnerLoanAccountRunningWaterRecordDao ownerLoanAccountRunningWaterRecordDao;

    private final OwnerLoanRecordService ownerLoanRecordService;

    private final RedissonClient redissonClient;

    /**
     * 接单校验判断
     *
     * @param orderInfoVO
     * @param ownerInfoFeignVO
     * @param orderGoods
     * @param childNo
     */
    public void getChildDetermine(FeignOrderInfoVO orderInfoVO, OwnerInfoFeignVO ownerInfoFeignVO, OrderGoods orderGoods,
                                  String childNo) {
        log.info("1.接单校验判断");
        OwnerQuotationDetailVO quotationDetailVO = orderFeign.getQuotationByOrderNo(orderInfoVO.getOrderNo()).getData();
        BigDecimal freightFreezeRate = quotationDetailVO.getFreightFreezeRate();

        if (freightFreezeRate.compareTo(BigDecimal.ONE) == 0) {
            //百分百预付不需要考虑借款账户
            log.info("2.百分百预付不需要考虑借款账户");
            return;
        }
        //发货-是否可超标准  0 否  1 是
        Integer overWeight = orderInfoVO.getOverWeight();

        if (OrderGoodsOverWeightEnum.NO.getCode().equals(overWeight)) {
            log.info("3.进行标吨判断");
            determine(quotationDetailVO.getPlatformFreightQuotation().multiply(new BigDecimal(35)), ownerInfoFeignVO, orderGoods, childNo, overWeight);
        } else {
            log.info("3.进行超吨判断");
            determine(quotationDetailVO.getPlatformFreightQuotation().multiply(new BigDecimal(50)), ownerInfoFeignVO, orderGoods, childNo, overWeight);
        }
    }

    public void determine(BigDecimal orderChildPrice, OwnerInfoFeignVO ownerInfoFeignVO, OrderGoods orderGoods, String childNo, Integer overWeight) {
        log.info("4.预估运费{},货主{},订单号{},运单号{}", orderChildPrice, ownerInfoFeignVO.getUserNo(), orderGoods.getOrderNo(), childNo);
        List<OwnerRunningWaterRecord> runningWaterRecordList = ownerRunningWaterRecordDao.getOwnerRunningWaterRecord(orderGoods.getOrderNo());
        BigDecimal frozen = runningWaterRecordList.stream().filter(item -> {
            return item.getRunningWaterType().equals(OwnerAccountEnum.RunningWaterStatus.FROZEN.getCode())
                    && item.getAccountType().equals(OwnerAccountEnum.AccountTypeStatus.PREPAID_FREIGHT_ACCOUNT.getCode())
                    ;
        }).map(OwnerRunningWaterRecord::getAlterationBalance).reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal takeOut = runningWaterRecordList.stream().filter(item -> {
            return item.getRunningWaterType().equals(OwnerAccountEnum.RunningWaterStatus.TAKE_OUT.getCode())
                    && item.getAccountType().equals(OwnerAccountEnum.AccountTypeStatus.PREPAID_FREIGHT_ACCOUNT.getCode())
                    ;
        }).map(OwnerRunningWaterRecord::getAlterationBalance).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal subtract = frozen.subtract(takeOut);
        log.info("5.查询预付运费相关流水：订单冻结预付运费{}, 扣除流水{}", frozen, takeOut);
        if (subtract.compareTo(BigDecimal.ZERO) > 0) {
            //查询未结算的运单（没有产生扣除流水的运单）
            List<OrderChild> orderChildList = orderChildDao.selectInTransitOrderChildLtUnsettle(orderGoods.getOrderNo());
            BigDecimal orderChildSum = null;
            int size = 1;
            if (CollectionUtil.isNotEmpty(orderChildList)) {
                size = orderChildList.size();
            }
            if (OrderGoodsOverWeightEnum.NO.getCode().equals(overWeight)) {
                orderChildSum = orderGoods.getPendingOrderFreight().multiply(new BigDecimal(35)).multiply(new BigDecimal(size));
            } else {
                orderChildSum = orderGoods.getPendingOrderFreight().multiply(new BigDecimal(50)).multiply(new BigDecimal(size));
            }
            BigDecimal ans = subtract.subtract(orderChildSum);
            log.info("6.当前订单冻结的预付运费还有剩余, 查询查询未结算的运单（没有产生扣除流水的运单）总计{},订单冻结预付运费剩余{}", orderChildSum, subtract);

            if (ans.compareTo(BigDecimal.ZERO) >= 0) {
                //预付运费够
                log.info("7.预付运费足够,不限制");
                return;
            }
        }
        log.info("8.预付运费不够,开始进行借款判断");
        // 进行借款判断
        OwnerLoanAccount ownerLoanAccount = ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo, ownerInfoFeignVO.getUserNo()).get();
        BigDecimal ownerLoanAccountSum = ownerLoanAccount.getVirtuallyUsableBalance().add(ownerLoanAccount.getFundingUsableBalance());

        if (ownerLoanAccountSum.compareTo(orderChildPrice) < 0) {
            log.info("9.当前货主借款账户总计{},小于预估运费{}", ownerLoanAccountSum, orderChildPrice);
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_SAVE_FAIL, "货主已欠款");
        }
        //借款账户钱够，判断是否逾期
        log.info("10.借款账户钱够，判断是否逾期");
        Optional<OwnerRepayment> optional = ownerRepaymentDao.getLimitOneByField(OwnerRepayment::getBeOverdue, OwnerRePaymentEnum.BeOverdue.YES.getCode());
        if (optional.isPresent()) {
            //逾期：不允许
            log.info("11.当前货主存在逾期借款");
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_SAVE_FAIL, "货主已欠款");
        }

        //查询审批通过并且未用完的借款
        LoanBalanceDTO param = new LoanBalanceDTO();
        param.setOrderChildPrice(orderChildPrice);
        param.setOwnerUserNo(ownerLoanAccount.getOwnerUserNo());
        param.setChildNo(childNo);
        getLoanBalanceLock(param);
    }

    /**
     * 运单确认借款相关处理执行
     *
     * @param settlementDriverDetail
     * @param settlementOwnerDetail
     */
    @Transactional(rollbackFor = Exception.class)
    public void childLoanConfirmAfterProcess(SettlementDriverDetail settlementDriverDetail, SettlementOwnerDetail settlementOwnerDetail) {
        log.info("1.运单确认收货后借款相关处理执行");
        if (SettlementOwnerEnum.InvoiceType.ORDINARY.getCode().equals(settlementDriverDetail.getInvoiceType())) {
            log.info("2.当前不是网运单,不需要处理借款标识");
            return;
        }

        if (settlementOwnerDetail.getPrepayFreight().compareTo(BigDecimal.ZERO) != 0) {
            log.info("3.当前是网运单,但预付运费不为0,说明已经处理");
            return;
        }

        String childNo = settlementDriverDetail.getChildNo();
        List<OwnerLoanAccountRunningWaterRecord> runningWaterRecordList = ownerLoanAccountRunningWaterRecordDao.getListByOrderNoAndRunningWaterType
                (childNo, OwnerLoanAccountRunningWaterRecordEnum.RunWaterType.APPROVE_FROZEN.getCode());
        settlementDriverDetail.setLoanFlag(1);
        settlementOwnerDetail.setLoanFreight(settlementOwnerDetail.getSettlementFreight());

        log.info("4.查询当前运单号：{}, 当前货主,{}, 此运单借款冻结记录：{}", childNo, settlementOwnerDetail.getOwnerUserNo(), JSONUtil.parse(runningWaterRecordList));
        if (CollectionUtil.isEmpty(runningWaterRecordList)) {
            //说明当初没借款，预付运费够，但是真实结算后，预付运费不够，走此逻辑
            log.info("4.1 说明当初没借款，预付运费够，但是真实结算后，预付运费不够，走此逻辑");
            LoanBalanceDTO param = new LoanBalanceDTO();
            param.setOrderChildPrice(settlementOwnerDetail.getSettlementFreight());
            param.setOwnerUserNo(settlementOwnerDetail.getOwnerUserNo());
            param.setChildNo(childNo);
            param.setSettlementDriverDetail(settlementDriverDetail);
            param.setSettlementOwnerDetail(settlementOwnerDetail);
            //扣钱,获取锁,此处有可能钱不够,抛异常,走网络货运重试
            getLoanBalanceLock(param);

            runningWaterRecordList = ownerLoanAccountRunningWaterRecordDao.getListByOrderNoAndRunningWaterType
                    (childNo, OwnerLoanAccountRunningWaterRecordEnum.RunWaterType.APPROVE_FROZEN.getCode());
            childWriteOffOwnerLoanAccount(runningWaterRecordList);
        } else {
            BigDecimal alterationBalance = runningWaterRecordList.stream().map(OwnerLoanAccountRunningWaterRecord::getAlterationBalance).reduce(BigDecimal.ZERO, BigDecimal::add);

            if (alterationBalance.compareTo(settlementOwnerDetail.getSettlementFreight()) == 0) {
                log.info("4.2 相等直接生成运单核销");

                //相等直接生成运单核销
                childWriteOffOwnerLoanAccount(runningWaterRecordList);
            } else if (alterationBalance.compareTo(settlementOwnerDetail.getSettlementFreight()) > 0) {
                //大于,则生成核销后，释放冻结的借款
                log.info("4.3 大于,则生成核销后，释放冻结的借款");

                BigDecimal thawBigdecimal = settlementOwnerDetail.getSettlementFreight().subtract(alterationBalance);
                OwnerLoanAccountRunningWaterRecord entity = runningWaterRecordList.get(runningWaterRecordList.size() - 1);
                Long loanNo = entity.getLoanNo();
                Integer loanType = entity.getLoanType();
                entity.setAlterationBalance(entity.getAlterationBalance().subtract(thawBigdecimal.negate()));
                //核销扣除
                childWriteOffOwnerLoanAccount(runningWaterRecordList);
                //多余的金额需要生成解冻流水
                thawOwnerLoanAccount(loanNo, loanType, entity.getOwnerUserNo(), entity.getOwnerUserName(), entity.getMobile(), entity.getChildNo(), thawBigdecimal.negate());
            } else {
                log.info("4.4 冻结的借款不够了");

                BigDecimal subtract = alterationBalance.subtract(settlementOwnerDetail.getSettlementFreight());
                // 冻结的借款不够了

                LoanBalanceDTO param = new LoanBalanceDTO();
                param.setOrderChildPrice(subtract.negate());
                param.setOwnerUserNo(settlementOwnerDetail.getOwnerUserNo());
                param.setChildNo(childNo);
                param.setSettlementDriverDetail(settlementDriverDetail);
                param.setSettlementOwnerDetail(settlementOwnerDetail);
                //扣钱,获取锁,此处有可能钱不够,抛异常,走网络货运重试
                getLoanBalanceLock(param);

                runningWaterRecordList = ownerLoanAccountRunningWaterRecordDao.getListByOrderNoAndRunningWaterType
                        (childNo, OwnerLoanAccountRunningWaterRecordEnum.RunWaterType.APPROVE_FROZEN.getCode());
                log.info("4.5新查询出冻结借款记录：{}", JSONUtil.parse(runningWaterRecordList));
                childWriteOffOwnerLoanAccount(runningWaterRecordList);

            }
        }


    }

    //归还借款单的剩余金额+解冻流水
    public void thawOwnerLoanAccount(Long loanNo, Integer loanType, Long userNo, String userName, String mobile, String childNo, BigDecimal balance) {
        while (true) {
            OwnerLoanAccount update = new OwnerLoanAccount();
            OwnerLoanAccount account = ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo,
                    userNo).get();
            if (OwnerLoanRecordEnum.LoanType.VIRTUAL_CURRENCY.getCode().equals(loanType)) {
                update.setId(account.getId());
                update.setModifiedTime(account.getModifiedTime());
                update.setVirtuallyAccountBalance(balance);
                update.setVirtuallyFrozenBalance(balance);
                update.setVirtuallyUsableBalance(balance);
                update.setFundingUsableBalance(BigDecimal.ZERO);
                update.setFundingAccountBalance(BigDecimal.ZERO);
                update.setFundingFrozenBalance(BigDecimal.ZERO);
            } else {
                update.setId(account.getId());
                update.setModifiedTime(account.getModifiedTime());
                update.setFundingAccountBalance(balance);
                update.setFundingFrozenBalance(balance);
                update.setFundingUsableBalance(balance);
                update.setVirtuallyAccountBalance(BigDecimal.ZERO);
                update.setVirtuallyFrozenBalance(BigDecimal.ZERO);
                update.setVirtuallyUsableBalance(BigDecimal.ZERO);
            }

            Integer i = ownerLoanAccountDao.thawOwnerLoanAccountCAS(update, LocalDateTime.now());
            if (i > 0) {
                OwnerLoanAccountRunningWaterRecord waterRecord = new OwnerLoanAccountRunningWaterRecord();
                waterRecord.setLoanNo(loanNo);
                waterRecord.setOwnerUserName(userName);
                waterRecord.setOwnerUserNo(userNo);
                waterRecord.setMobile(mobile);
                waterRecord.setLoanType(loanType);
                waterRecord.setRunningWaterNo(idGenerateSnowFlake.nextId(1L));
                waterRecord.setChildNo(childNo);
                waterRecord.setRunningWaterType(OwnerLoanAccountRunningWaterRecordEnum.RunWaterType.APPROVE_THAW.getCode());
                waterRecord.setAlterationBalance(balance);

                account = ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo,
                        userNo).get();
                waterRecord.setAccountBalance(account.getVirtuallyAccountBalance().add(account.getFundingAccountBalance()));
                waterRecord.setUseAbleBalance(account.getVirtuallyUsableBalance().add(account.getFundingUsableBalance()));
                waterRecord.setFrozenBalance(account.getFundingFrozenBalance().add(account.getVirtuallyFrozenBalance()));
                waterRecord.setCreateBy("系统");
                ownerLoanAccountRunningWaterRecordDao.saveEntity(waterRecord);
                break;
            }
        }

    }

    public void childWriteOffOwnerLoanAccount(List<OwnerLoanAccountRunningWaterRecord> frozenList) {
        log.info("5. 执行核销扣除,相关流水{}", JSONUtil.parse(frozenList));
        while (true) {
            int flag = 0;
            List<OwnerLoanAccountRunningWaterRecord> saveList = new LinkedList<>();
            for (OwnerLoanAccountRunningWaterRecord record : frozenList) {

                OwnerLoanAccount update = new OwnerLoanAccount();
                OwnerLoanAccount account = ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo,
                        record.getOwnerUserNo()).get();
                if (OwnerLoanRecordEnum.LoanType.VIRTUAL_CURRENCY.getCode().equals(record.getLoanType())) {
                    update.setId(account.getId());
                    update.setModifiedTime(account.getModifiedTime());
                    update.setVirtuallyAccountBalance(record.getAlterationBalance());
                    update.setVirtuallyFrozenBalance(record.getAlterationBalance());
                    update.setVirtuallyUsableBalance(BigDecimal.ZERO);
                    update.setFundingUsableBalance(BigDecimal.ZERO);
                    update.setFundingAccountBalance(BigDecimal.ZERO);
                    update.setFundingFrozenBalance(BigDecimal.ZERO);
                } else {
                    update.setId(account.getId());
                    update.setModifiedTime(account.getModifiedTime());
                    update.setFundingAccountBalance(record.getAlterationBalance());
                    update.setFundingFrozenBalance(record.getAlterationBalance());
                    update.setFundingUsableBalance(BigDecimal.ZERO);
                    update.setVirtuallyAccountBalance(BigDecimal.ZERO);
                    update.setVirtuallyFrozenBalance(BigDecimal.ZERO);
                    update.setVirtuallyUsableBalance(BigDecimal.ZERO);
                }
                //核销抵扣
                Integer i = ownerLoanAccountDao.childWriteOffOwnerLoanAccountCAS(update, LocalDateTime.now());

                if (i <= 0) {
                    break;
                }
                flag += i;
                OwnerLoanAccountRunningWaterRecord waterRecord = new OwnerLoanAccountRunningWaterRecord();
                waterRecord.setLoanNo(record.getLoanNo());
                waterRecord.setOwnerUserName(record.getOwnerUserName());
                waterRecord.setOwnerUserNo(record.getOwnerUserNo());
                waterRecord.setMobile(record.getMobile());
                waterRecord.setLoanType(record.getLoanType());
                waterRecord.setRunningWaterNo(idGenerateSnowFlake.nextId(1L));
                waterRecord.setChildNo(record.getChildNo());
                waterRecord.setRunningWaterType(OwnerLoanAccountRunningWaterRecordEnum.RunWaterType.APPROVE_CHILD_WRITE_OFF.getCode());
                waterRecord.setAlterationBalance(record.getAlterationBalance());

                account = ownerLoanAccountDao.getOneByField(OwnerLoanAccount::getOwnerUserNo,
                        record.getOwnerUserNo()).get();
                waterRecord.setAccountBalance(account.getVirtuallyAccountBalance().add(account.getFundingAccountBalance()));
                waterRecord.setUseAbleBalance(account.getVirtuallyUsableBalance().add(account.getFundingUsableBalance()));
                waterRecord.setFrozenBalance(account.getFundingFrozenBalance().add(account.getVirtuallyFrozenBalance()));
                waterRecord.setCreateBy("系统");
                saveList.add(waterRecord);
            }

            if (flag == frozenList.size()) {
                ownerLoanAccountRunningWaterRecordDao.batchSaveEntity(saveList);
                break;
            }
        }

    }

    public void getLoanBalanceLock(LoanBalanceDTO dto) {
        RLock lock = null;
        try {
            lock = redissonClient.getLock(RedissonConstants.ORDER_CHILD_LOAN_OWNER_USERID_LOCK + dto.getOwnerUserNo());

            boolean flag = lock.tryLock(15, 30, TimeUnit.SECONDS);
            if (!flag) {
                throw new ServiceSystemException(PerformanceResultEnum.GET_LOAN_RECORD);
            }

            ownerLoanRecordService.getLoanBalance(dto);
        } catch (Exception e) {
            throw new ServiceSystemException(PerformanceResultEnum.GET_LOAN_RECORD, e.getMessage());
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}
