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

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.performance.component.IdGenerateSnowFlake;
import com.clx.performance.config.loan.ClxPayeeConfig;
import com.clx.performance.config.loan.PaymentFromConfig;
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.enums.PerformanceResultEnum;
import com.clx.performance.enums.RunningWaterTypeEnum;
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.model.OwnerBindCardRecord;
import com.clx.performance.model.loan.OwnerLoanAccount;
import com.clx.performance.model.loan.OwnerLoanAccountRunningWaterRecord;
import com.clx.performance.model.loan.OwnerLoanRecord;
import com.clx.performance.model.loan.OwnerRepayment;
import com.clx.performance.param.pc.loan.carrier.CarrierOwnerLoanRecordApproveParam;
import com.clx.performance.param.pc.loan.carrier.PageCarrierOwnerLoanRecordParam;
import com.clx.performance.param.pc.loan.carrier.PageOwnerLoanRecordOfOwner;
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.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.apache.poi.ss.formula.functions.T;
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 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 OwnerFeign ownerFeign;

    private final ContractEvidenceFeign contractEvidenceFeign;
    private final ContractTemplateFeign contractTemplateFeign;
    private final PaymentFromConfig paymentFromConfig;

    @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.setBorrower(param.getBorrower());
        entity.setBorrowerAccount(ownerBindCardRecord.getBankCardNumber());
        entity.setLendingParty(param.getLendingParty());
        entity.setPayee("收款方写死");
        entity.setPayeeAccount("收款方账户写死");
        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()) {
            //同意
            ownerLoanRecord.setLoanType(param.getLoanType());

            if (OwnerLoanRecordEnum.LoanType.FUND.getCode().equals(param.getLoanType())) {
                //资金 TODO 调宁波银企直连的产品，从诚联信账户中给鑫祥和执行转账
                NbBankOrderPayResultVO orderPayResultVO = bankService.orderDirectPay(param.getLoanNo() ,
                        ownerLoanRecord.getLoanBalance().intValue(), "", "", "");
                ownerLoanRecord.setRunningWaterOpenNo(orderPayResultVO.getTransSeqNo());
                ownerLoanRecord.setMerchantRunningWaterNo(orderPayResultVO.getMerSeqNo());
                ownerLoanRecord.setStatus(OwnerLoanRecordEnum.Status.PAYING.getCode());
                ownerLoanRecord.setLoanResidueBalance(ownerLoanRecord.getLoanBalance());
            } 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.setVirtuallyAmount(ownerLoanRecord.getLoanBalance());
                entity.setFundingAmount(BigDecimal.ZERO);*/
                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);
    }


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

    }

    /**
     * 生成借款流水
     * @param ownerLoanRecord
     */
    private void initOwnerLoanRunningWaterRecord(OwnerLoanRecord ownerLoanRecord) {
        OwnerLoanAccount 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())
                .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.setPayment(ownerLoanRecord.getBorrower());
        ownerRepayment.setPaymentAccount(ownerLoanRecord.getBorrowerAccount());
        ownerRepayment.setPayee(ownerRepayment.getPayee());
        ownerRepayment.setPayeeAccount(ownerLoanRecord.getPayeeAccount());
        ownerRepayment.setPayChannel(OwnerRePaymentEnum.Channel.ORDER.getCode());
        ownerRepayment.setRunningWaterOpenNo(ownerLoanRecord.getRunningWaterOpenNo());
        ownerRepayment.setMerchantRunningWaterNo(ownerLoanRecord.getMerchantRunningWaterNo());
        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.payeeList.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);
        //TODO 设置支付方式和付款账户 ，宇帆提供
        return vo;
    }


    @Override
    public OwnerOrderPaymentDetailVO getOrderPaymentDetail(Integer id) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getEntityByKey(id).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        OwnerOrderPaymentDetailVO vo = ownerLoanRecordStruct.convertOrderPaymentDetail(ownerLoanRecord);
        //计算倒计时时间，下单时间 + 30天 为截止时间
        LocalDateTime endTime = ownerLoanRecord.getCreateTime().plusDays(30);
        vo.setFinalPaymentTime(DateUtils.formatDateTime(endTime).get());
        return vo;
    }

    @Override
    public CarrierTransferPaymentDetailVO getTransferPaymentDetail(Integer id) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getEntityByKey(id).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        ClxPayeeConfig.PayeeConfig payeeMap = ClxPayeeConfig.getPayeeMap(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()).build();
    }

    @Override
    public String savePaymentApplicationForm(ExportPaymentApplicationFormParam param) {
        OwnerLoanRecord ownerLoanRecord = ownerLoanRecordDao.getEntityByKey(param.getId()).orElseThrow(
                PerformanceResultEnum.DATA_NOT_FIND);
        OwnerLoanRecordVO ownerLoanRecordVO = ownerLoanRecordStruct.convert(ownerLoanRecord);
        //货主借款：  收款方鑫祥和  付款方为诚联信    借款方为货主
        Map<String,String> map = JSON.parseObject(JSON.toJSONString(ownerLoanRecordVO), new TypeReference<Map<String,String>>(){});
        // 创建合同
        GenerateContractParam feignParam = new GenerateContractParam();
        if(Objects.equals(OwnerRePaymentEnum.Channel.ORDER.getCode(),param.getPayWay())){
            feignParam.setTemplateNo(paymentFromConfig.getLoanOrderTemplateNo());
        }else{
            feignParam.setTemplateNo(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(BigDecimal orderChildPrice, Long userNo, String childNo) {
        log.info("执行借款,借款金额{}, 货主编号：{}, 运单号：{}", orderChildPrice, userNo, childNo);
        //TODO 需要加锁处理
        List<OwnerLoanRecord> updateList = new LinkedList<>();
        List<OwnerLoanRecord> ownerLoanRecords = ownerLoanRecordDao.selectLoanBalance(userNo);
        BigDecimal orderChildPriceTemp = orderChildPrice;
        for (OwnerLoanRecord ownerLoanRecord : ownerLoanRecords) {
            BigDecimal loanResidueBalance = ownerLoanRecord.getLoanResidueBalance();
            log.info("当前借款金额：{},借款单号{},借款剩余金额{}", orderChildPriceTemp, ownerLoanRecord.getLoanNo(), loanResidueBalance);
            if (loanResidueBalance.compareTo(orderChildPriceTemp) >= 0) {
                log.info("当前借款单的剩余金额足够借款金额");
                generateFrozenOwnerLoanRunningWater(ownerLoanRecord, childNo, orderChildPrice);
                updateList.add(ownerLoanRecord.setLoanResidueBalance(loanResidueBalance.subtract(orderChildPriceTemp)));
                orderChildPriceTemp = BigDecimal.ZERO;
                break;
            } else {
                log.info("当前借款单的剩余金额不够抵扣借款金额,先进行扣除当前借款所有剩余");
                generateFrozenOwnerLoanRunningWater(ownerLoanRecord, childNo, orderChildPrice);
                updateList.add(ownerLoanRecord.setLoanResidueBalance(BigDecimal.ZERO));
                orderChildPriceTemp = orderChildPriceTemp.subtract(loanResidueBalance);
            }
        }

        if (orderChildPriceTemp.compareTo(BigDecimal.ZERO) != 0) {
            throw new ServiceSystemException(PerformanceResultEnum.ORDER_CHILD_SAVE_FAIL, "货主货款不足");
        }

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

    }
    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("系统");
                ownerLoanAccountRunningWaterRecordDao.saveEntity(waterRecord);
                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);
        }
    }

}
