package com.ruoyi.dk.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.component.json.utils.JsonUtils;
import com.ruoyi.component.mybatis.core.page.PageQuery;
import com.ruoyi.dk.domain.*;
import com.ruoyi.dk.dto.admin.req.BorrowUpdateStatusReq;
import com.ruoyi.dk.dto.admin.resp.BorrowResp;
import com.ruoyi.dk.dto.app.req.BorrowStartReq;
import com.ruoyi.dk.dto.app.req.CalLoanReq;
import com.ruoyi.dk.dto.app.resp.BorrowStepResp;
import com.ruoyi.dk.dto.app.resp.LoanProcessResp;
import com.ruoyi.dk.kit.CalLoanManager;
import com.ruoyi.dk.mapper.BorrowMapper;
import com.ruoyi.dk.service.*;
import com.ruoyi.dk.util.ContentReplaceUtil;
import com.ruoyi.dk.util.Loan;
import com.ruoyi.dk.util.MoneyUtil;
import com.ruoyi.dk.util.OrderTradeNoUtil;
import com.ruoyi.component.core.exception.CustomException;
import com.ruoyi.component.core.util.BeanConvertUtil;
import com.ruoyi.component.core.util.MessageUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class BorrowServiceImpl extends ServiceImpl<BorrowMapper, Borrow> implements BorrowService {

    @Autowired
    private CustomerService customerService;
    @Autowired
    private CustomerInfoService customerInfoService;
    @Autowired
    private CalLoanManager calLoanManager;
    @Autowired
    private LoansSettingService loansSettingService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private AgreementSettingService agreementSettingService;
    @Autowired
    private BorrowStatusService borrowStatusService;

    @Override
    public Borrow borrow(BorrowStartReq req) {
        CustomerInfo customerInfo = customerInfoService.getByCustomerId(req.getCustomerId());
        if(customerInfo == null){
            throw new CustomException(MessageUtils.message("dk.user.no"));
        }
        RLock lock = redissonClient.getLock("borrow:lock:" + req.getCustomerId());
        if(lock.isLocked()){
            throw new CustomException(MessageUtils.message("dk.wait"));
        }
        lock.lock(10, TimeUnit.SECONDS);
        Borrow borrow;
        try {
            Customer customer = customerService.getById(req.getCustomerId());
            if(customer.getLoansFlag() == 1){
                throw new CustomException(MessageUtils.message("dk.wait.last.loan"));
            }
            if(customer.getRealNameAuth() != 1){
                throw new CustomException(MessageUtils.message("dk.loan.userData.missing"));
            }
            if(req.getTotalLoanMoney() == null){
                throw new CustomException(MessageUtils.message("dk.loanAccount.empty"));
            }
            LoansSetting loansSetting = loansSettingService.getLoansSetting();
            if(req.getTotalLoanMoney().compareTo(loansSetting.getLoansMinAccount()) < 0){
                throw new CustomException(MessageUtils.message("dk.loan.amount.notLe")+loansSetting.getLoansMinAccount());
            }
            if(req.getTotalLoanMoney().compareTo(loansSetting.getLoansMaxAccount()) > 0){
                throw new CustomException(MessageUtils.message("dk.loan.amount.notGe")+loansSetting.getLoansMaxAccount());
            }
            CalLoanReq calLoanReq = new CalLoanReq();
            calLoanReq.setTotalLoanMoney(req.getTotalLoanMoney());
            calLoanReq.setTotalMonth(req.getTotalMonth());
            Loan loan = calLoanManager.calLoan(calLoanReq);
            borrow = BeanConvertUtil.convertTo(customerInfo, Borrow::new);
            borrow.setId(null);
            borrow.setTradeNo(OrderTradeNoUtil.createOrder(OrderTradeNoUtil.BORROW));
            borrow.setTotalLoanMoney(loan.getTotalLoanMoney());
            borrow.setTotalMonth(loan.getTotalMonth());
            borrow.setLoanMonthRate(loan.getLoanRateMonth());
            borrow.setLoanYearRate(loan.getLoanRateYear());
            borrow.setTotalInterest(loan.getTotalInterest());
            borrow.setTotalRepayment(loan.getTotalRepayment());
            borrow.setFirstRepayment(loan.getFirstRepayment());
            borrow.setAvgRepayment(loan.getAvgRepayment());
            borrow.setDueDate(loansSetting.getDueDate());
            borrow.setNoteRemark(req.getNoteRemark());
            borrow.setBorrowName(MessageUtils.message("dk.loan.auditing"));
            borrow.setBorrowNameStyle("black");
            borrow.setBorrowRemark(MessageUtils.message("dk.loan.auditing.remark"));
            borrow.setInfoJson(JsonUtils.toJsonString(loan));
            borrow.setFirstBankType(borrow.getBankType());
            borrow.setFirstBackCardNum(borrow.getBackCardNum());
            this.save(borrow);
            customerService.dk(customer.getId());
        }finally {
            lock.unlock();
        }
        return borrow;
    }

    @Override
    public Borrow getByTradeNo(String tradeNo) {
        return this.getOne(Wrappers.lambdaQuery(Borrow.class)
                .eq(Borrow::getTradeNo,tradeNo).last("limit 1"));
    }

    @Override
    public boolean updateLoan(Borrow bo) {
        Loan loan = calLoanManager.calLoan(bo.getTotalLoanMoney(),bo.getTotalMonth(),bo.getLoanMonthRate());
        Borrow update = new Borrow();
        update.setId(bo.getId());
        update.setTotalLoanMoney(loan.getTotalLoanMoney());
        update.setTotalMonth(loan.getTotalMonth());
        update.setLoanMonthRate(loan.getLoanRateMonth());
        update.setLoanYearRate(loan.getLoanRateYear());
        update.setTotalInterest(loan.getTotalInterest());
        update.setTotalRepayment(loan.getTotalRepayment());
        update.setFirstRepayment(loan.getFirstRepayment());
        update.setAvgRepayment(loan.getAvgRepayment());
        update.setInfoJson(JsonUtils.toJsonString(loan));
        return this.updateById(update);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateStatus(BorrowUpdateStatusReq bo) {
        Borrow borrow = this.getById(bo.getId());
        boolean remit = false;
        if(borrow.getRemitFlag() == 0 && bo.getUsedRemit() != null && bo.getUsedRemit() == 1){
            remit = true;
        }
        Borrow update = new Borrow();
        update.setId(bo.getId());
        update.setBorrowName(bo.getBorrowName());
        update.setBorrowRemark(bo.getBorrowRemark());
        update.setBorrowNameStyle(bo.getBorrowNameStyle());
        update.setAuditFlag(true);
        if(remit){
            update.setRemitFlag(1);
        }
        update.setBorrowStatusId(bo.getBorrowStatusId());
        boolean bool = this.updateById(update);
        if(bool && remit){
            customerService.borrowAmount(borrow.getCustomerId(), borrow.getTotalLoanMoney(),borrow.getTotalRepayment());
            return true;
        }
        return bool;
    }

    @Override
    public boolean updateBank(Borrow bo) {
        this.updateById(bo);
        Borrow borrow = this.getById(bo.getId());
        LambdaUpdateWrapper<CustomerInfo> update = Wrappers.lambdaUpdate(CustomerInfo.class).eq(CustomerInfo::getCustomerId, borrow.getCustomerId());
        boolean updateFlag = false;
        if(StringUtils.isNotEmpty(bo.getBankType())){
            update.set(CustomerInfo::getBankType,bo.getBankType());
            updateFlag = true;
        }
        if(StringUtils.isNotEmpty(bo.getBackCardNum())){
            update.set(CustomerInfo::getBackCardNum,bo.getBackCardNum());
            updateFlag = true;
        }
        if(updateFlag){
            customerInfoService.update(update);
        }
        return true;
    }

    @Override
    public void withdraw(Double withdrawAmount, Long customerId) {
        Borrow borrow = this.getByCustomerId(customerId);
        if(borrow == null){
            throw new CustomException(MessageUtils.message("dk.withdraw.account.error"));
        }
        if(borrow.getBorrowStatusId() != null){
            BorrowStatus borrowStatus = borrowStatusService.getById(borrow.getBorrowStatusId());
            if(borrowStatus != null){
                if(BooleanUtils.isFalse(borrowStatus.getWithdrawFlag())){
                    throw new CustomException(MessageUtils.message("dk.withdraw.account.error"));
                }
            }
        }
        Customer customer = customerService.getById(customerId);
        if(BooleanUtils.isNotTrue(customer.getAllowWithdrawFlag())){
            throw new CustomException(MessageUtils.message("dk.withdraw.account.error"));
        }
        if(customer.getAccount().doubleValue() < withdrawAmount){
            throw new CustomException(MessageUtils.message("dk.withdraw.balance.not.enough.error"));
        }
        Borrow one = this.getOne(Wrappers.lambdaQuery(Borrow.class).eq(Borrow::getCustomerId,customerId));
        if(one == null){
            throw new CustomException(MessageUtils.message("dk.withdraw.fail"));
        }
        if(!MessageUtils.message("dk.audit.success").equals(one.getBorrowName())){
            throw new CustomException(one.getBorrowName());
        }
        boolean result = customerService.withdraw(customerId,withdrawAmount);
        Borrow update = new Borrow();
        update.setId(one.getId());
        update.setBorrowName(MessageUtils.message("dk.withdraw.ing"));
        update.setBorrowRemark(MessageUtils.message("dk.withdraw.remark"));
        update.setBorrowNameStyle("red");
        this.updateById(update);
    }

    @Override
    public Borrow getByCustomerId(Long customerId) {
        return this.getOne(Wrappers.lambdaQuery(Borrow.class).eq(Borrow::getCustomerId, customerId)
                .last("limit 1"));
    }

    @Override
    public LoanProcessResp  getStepBorrow(Long customerId){
        Borrow one = this.getOne(Wrappers.lambdaQuery(Borrow.class).eq(Borrow::getCustomerId,customerId));
        return parseStepBorrow(one);
    }

    @Override
    public LoanProcessResp parseStepBorrow(Borrow one){
        LoanProcessResp resp = new LoanProcessResp();
        List<BorrowStepResp> borrowStep = new ArrayList<>();
        if(one == null){
            borrowStep.add(new BorrowStepResp(MessageUtils.message("dk.borrow.push.success"),false));
            borrowStep.add(new BorrowStepResp(MessageUtils.message("dk.borrow.auditing"),false));
            borrowStep.add(new BorrowStepResp(MessageUtils.message("dk.borrow.account.success"),false));
            resp.setBorrowStep(borrowStep);
            resp.setBorrowNameStyle("red");
            resp.setBorrowRemark(MessageUtils.message("dk.borrow.accounting.remark"));
            return resp;
        }
        borrowStep.add(new BorrowStepResp(MessageUtils.message("dk.borrow.push.success"),true));
        if("审核通过".equals(one.getBorrowName())){
            borrowStep.add(new BorrowStepResp(one.getBorrowName(),true));
            borrowStep.add(new BorrowStepResp(MessageUtils.message("dk.borrow.account.success"),true));
        }else{
            borrowStep.add(new BorrowStepResp(one.getBorrowName(),true));
            borrowStep.add(new BorrowStepResp(MessageUtils.message("dk.borrow.account.success"),false));
        }
        resp.setBorrowStep(borrowStep);
        resp.setBorrowNameStyle(one.getBorrowNameStyle());
        resp.setBorrowRemark(one.getBorrowRemark());
        return resp;
    }

    @Override
    public IPage<BorrowResp> pageAdmin(PageQuery pageQuery, Borrow bo) {
        return baseMapper.pageAdmin(pageQuery.build(),bo);
    }

    @Override
    public String getContract(String tradeNo) {
        Borrow borrow = this.getByTradeNo(tradeNo);
        if(borrow == null){
            throw new CustomException(MessageUtils.message("dk.borrow.contract.error"));
        }
        CustomerInfo customerInfo = customerInfoService.getByCustomerId(borrow.getCustomerId());
        Customer customer = customerService.getById(borrow.getCustomerId());
        LocalDate startDate = borrow.getCreateTime().toLocalDate();
        Map<String,Object> map = new HashMap<>();
        map.put("合同编号",borrow.getTradeNo());
        map.put("签订日期",startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        map.put("借款人用户名",StringUtils.isEmpty(customer.getPhoneNumber())?"":customer.getPhoneNumber());
        map.put("借款人身份证号",StringUtils.isEmpty(borrow.getCardNum())?"":borrow.getCardNum());
        map.put("借款人手机号",StringUtils.isEmpty(customer.getPhoneNumber())?"":customer.getPhoneNumber());
        String signatureImage = "<div>签名:</div>";
        if(StringUtils.isNotEmpty(customerInfo.getSignature())){
//            signatureImage = "<img src=\"data:image/png;base64,"+customerInfo.getSignature()+"\" alt=\"无效\" />";
            signatureImage = "<div style='display: flex;align-items: center;'>签名:<img style='width:100px;height:50px' src=\""+customerInfo.getSignature()+"\" alt=\"无效\" /></div>";
        }
        map.put("借款人签名",signatureImage);
        map.put("借款金额大写", MoneyUtil.toChinese(borrow.getTotalLoanMoney().toString()));
        map.put("借款金额小写",borrow.getTotalLoanMoney().toString());
        map.put("借款期限",borrow.getTotalMonth());
        map.put("借款开始日",startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        map.put("借款结束日",startDate.plusMonths(borrow.getTotalMonth()).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        map.put("借款人名称",StringUtils.isEmpty(borrow.getRealName())?"":borrow.getRealName());
        AgreementSetting agreementSetting = agreementSettingService.getAgreementSetting();
        String contractTemplate = agreementSetting.getContractTemplate();
        return ContentReplaceUtil.replaceWord(contractTemplate, map);
    }


}
