package com.sunxung.factoring.service.financing.contract.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sunxung.factoring.component.enums.FinancingCurrentStageEnum;
import com.sunxung.factoring.component.exception.BusinessException;
import com.sunxung.factoring.component.exception.ValidatorException;
import com.sunxung.factoring.component.util.*;
import com.sunxung.factoring.dict.impl.FileModuleDict;
import com.sunxung.factoring.entity.financing.apply.CFinancingBasicInfo;
import com.sunxung.factoring.entity.financing.contract.ContractElectronicInfo;
import com.sunxung.factoring.entity.financing.contract.ContractInfo;
import com.sunxung.factoring.entity.financing.contract.ContractLegalFinalApproval;
import com.sunxung.factoring.entity.financing.payment.PaymentInfo;
import com.sunxung.factoring.entity.financing.review.ReviewFinancingInfo;
import com.sunxung.factoring.entity.financing.review.ReviewQuotaDisburse;
import com.sunxung.factoring.entity.sys.FileStorageDO;
import com.sunxung.factoring.mapper.financing.contract.ContractLegalFinalApprovalMapper;
import com.sunxung.factoring.service.financing.apply.ICFinancingBasicInfoService;
import com.sunxung.factoring.service.financing.contract.IContractElectronicInfoService;
import com.sunxung.factoring.service.financing.contract.IContractInfoService;
import com.sunxung.factoring.service.financing.contract.IContractLegalFinalApprovalService;
import com.sunxung.factoring.service.financing.payment.IPaymentInfoService;
import com.sunxung.factoring.service.financing.review.ReviewFinancingInfoService;
import com.sunxung.factoring.service.financing.review.ReviewQuotaDisburseService;
import com.sunxung.factoring.service.project.BusinessAssigneeService;
import com.sunxung.factoring.service.sys.FileService;
import com.sunxung.factoring.service.sys.flowable.FlowableService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * @Description :
 * @Author : L.Jia
 * @Date: 2024-02-27 15:42
 */
@Service("contract_approvalService")
public class ContractLegalFinalApprovalServiceImpl extends ServiceImpl<ContractLegalFinalApprovalMapper, ContractLegalFinalApproval>
        implements IContractLegalFinalApprovalService {

    @Autowired
    private ICFinancingBasicInfoService financingBasicInfoService;
    @Autowired
    private IContractInfoService contractInfoService;
    @Autowired
    private ReviewQuotaDisburseService reviewQuotaDisburseService;
    @Autowired
    private ReviewFinancingInfoService reviewFinancingInfoService;
    @Autowired
    private IContractElectronicInfoService contractElectronicInfoService;
    @Autowired
    private FlowableService flowableService;
    @Autowired
    private BusinessAssigneeService assigneeService;
    @Autowired
    private IPaymentInfoService paymentInfoService;
    @Autowired
    private FileService fileService;

    @Override
    public ContractLegalFinalApproval getContractLegalFinalApprovalEditData(Long id) {

        ContractInfo contractInfo = getContractInfoByFinancingBasicInfoId(id);
        ContractLegalFinalApproval contractLegalFinalApproval = baseMapper.selectOne(new QueryWrapper<ContractLegalFinalApproval>()
                .eq("contract_info_id", contractInfo.getId())
                .eq("visible", false));
        if (null == contractLegalFinalApproval) {
            contractLegalFinalApproval = new ContractLegalFinalApproval();
            contractLegalFinalApproval.setContractInfoId(contractInfo.getId());
        }
        List<ContractElectronicInfo> electronicInfoList = contractElectronicInfoService.list(new QueryWrapper<ContractElectronicInfo>()
                .eq("contract_info_id", contractInfo.getId()));
        List<FileStorageDO> fileStorages = new ArrayList<>();
        if (electronicInfoList != null && electronicInfoList.size() > 0) {
            for (ContractElectronicInfo contractElectronicInfo : electronicInfoList) {
                FileStorageDO fileStorageDO = fileService.getById(contractElectronicInfo.getFileId());
                fileStorages.add(fileStorageDO);
            }
        }
        contractLegalFinalApproval.setFileStorageList(fileStorages);
        return contractLegalFinalApproval;
    }

    /**
     * 根据融资信息id获取合同信息
     *
     * @param id
     * @return
     */
    private ContractInfo getContractInfoByFinancingBasicInfoId(Long id) {
        CFinancingBasicInfo financingBasicInfo = financingBasicInfoService.getById(id);
        ReviewQuotaDisburse quotaDisburse = reviewQuotaDisburseService.getOne(new QueryWrapper<ReviewQuotaDisburse>()
                .eq("c_financing_basic_info_id", financingBasicInfo.getId()));
        if (quotaDisburse == null) {
            throw new BusinessException(CodeUtil.FAIL, "获取不到对应额度支用信息");
        }
        ContractInfo contractInfo = contractInfoService.getOne(new QueryWrapper<ContractInfo>()
                .eq("review_quota_disburse_id", quotaDisburse.getId())
                .orderByDesc("id").last("LIMIT 1"));
        if (null == contractInfo) {
            throw new BusinessException(CodeUtil.FAIL, "通过业务编号获取不到对应的合同信息");
        }
        return contractInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ContractLegalFinalApproval editContractLegalFinalApproval(ContractLegalFinalApproval contractLegalFinalApproval) {
        if (null == contractLegalFinalApproval) {
            throw new ValidatorException(CodeUtil.FAIL, "请至少填写一条法务终审信息");
        }
        contractLegalFinalApproval.setAssigneeId(ThreadLocalUtil.getUserId());
        saveOrUpdate(contractLegalFinalApproval);
        if (contractLegalFinalApproval.getVisible()) {
            ContractInfo contractInfo = contractInfoService.getById(contractLegalFinalApproval.getContractInfoId());
            if (contractInfo == null) {
                throw new BusinessException(CodeUtil.FAIL, "获取不到对应合同信息");
            }
            ReviewQuotaDisburse quotaDisburse = reviewQuotaDisburseService.getById(contractInfo.getReviewQuotaDisburseId());
            if (quotaDisburse == null) {
                throw new BusinessException(CodeUtil.FAIL, "获取不到对应额度支用信息");
            }
            CFinancingBasicInfo financingBasicInfo = financingBasicInfoService.getById(quotaDisburse.getFinancingBasicInfoId());
            String key = ConstantUtil.FINANCING_APPLY;

            Map<String, Object> msgVariables = new HashMap<>(2);
            msgVariables.put(ConstantUtil.FLOWABLE_MSG_TITLE, financingBasicInfo.getBusinessNumber());
            msgVariables.put(ConstantUtil.FLOWABLE_MSG_CONTENT, "业务编号为“" + financingBasicInfo.getBusinessNumber() + "”的项目已inOut“"
                    + ConstantConversionUtil.flowBusinessKeyNames.get(key) + "—formBusiness”阶段，");

            Map<String, Object> procVariables = new HashMap<>(1);
            Long assigneeId = assigneeService.getIdByBusiness(financingBasicInfo.getId(), key, ConstantUtil.ASSIGNEE_PROJECT_MANAGER);
            procVariables.put(ConstantUtil.PAYMENT_APPLY_ASSIGNEE, assigneeId);

            Boolean result = flowableService.completeMyTask(contractLegalFinalApproval.getTaskId(), financingBasicInfo, procVariables,
                    ConstantConversionUtil.processApprovalResult.get(contractLegalFinalApproval.getConclusion()),
                    contractLegalFinalApproval.getId(), msgVariables, key);
            if (result == null || !result) {
                throw new ValidatorException(CodeUtil.FAIL, "法务终审提交时出错啦！");
            }
            if (contractLegalFinalApproval.getConclusion().equals(ConstantConversionUtil.APPROVAL_RESULT_PASS)) {
                //修改融资申请当前阶段
                financingBasicInfo.setCurrentStage(FinancingCurrentStageEnum.PAYMENT_APPLY.getCode());
                financingBasicInfoService.updateById(financingBasicInfo);
                //生成付款信息
                addPaymentInfo(financingBasicInfo, contractInfo, quotaDisburse);
            } else if (contractLegalFinalApproval.getConclusion().equals(ConstantConversionUtil.APPROVAL_RESULT_BACK)) {
                //修改融资申请当前阶段
                financingBasicInfo.setCurrentStage(FinancingCurrentStageEnum.REVIEW_QUOTA_DISBURSE.getCode());
                financingBasicInfoService.updateById(financingBasicInfo);
            }
        }
        return contractLegalFinalApproval;
    }

    private void addPaymentInfo(CFinancingBasicInfo financingBasicInfo, ContractInfo contractInfo, ReviewQuotaDisburse quotaDisburse) {
        PaymentInfo paymentInfo = new PaymentInfo();
        paymentInfo.setBusinessNo(contractInfo.getBusinessNo());
        paymentInfo.setFinancingBasicInfoId(financingBasicInfo.getId());
        paymentInfo.setContractInfoId(contractInfo.getId());
        paymentInfo.setQuotaDisburseId(quotaDisburse.getId());
        paymentInfo.setQuotaDisburseBatch(quotaDisburse.getQuotaDisburseBatch());
        paymentInfo.setContractNo(contractInfo.getContractNo());

        ReviewFinancingInfo reviewFinancingInfo = reviewFinancingInfoService.getOne(new QueryWrapper<ReviewFinancingInfo>()
                .eq("review_quota_disburse_id", quotaDisburse.getId()));
        if (reviewFinancingInfo == null) {
            throw new ValidatorException(CodeUtil.FAIL, "获取不到对应融资信息！");
        }
        paymentInfo.setTotalLoanAmount(reviewFinancingInfo.getFinancingAmount());

        paymentInfoService.save(paymentInfo);
    }

    @Override
    public void downloadAll(Long contractInfoId, HttpServletResponse res) {
        List<ContractElectronicInfo> electronicInfoList = contractElectronicInfoService.list(new QueryWrapper<ContractElectronicInfo>()
                .eq("contract_info_id", contractInfoId));
        ArrayList<File> files = new ArrayList<>();
        if (electronicInfoList != null && electronicInfoList.size() > 0) {
            for (ContractElectronicInfo contractElectronicInfo : electronicInfoList) {
                FileStorageDO fileStorageDO = fileService.getById(contractElectronicInfo.getFileId());
                files.add(new File(fileStorageDO.getAbsolutePath()));
            }
        }
        if (files.size() > 0) {
            // 打包成zip文件
            File zipFile = packagingZip(files, "合同文件");
            // 下载zip附件
            downloadFile(res, zipFile);
        }
    }

    @Override
    public ContractLegalFinalApproval getView(Long id) {
        ContractLegalFinalApproval contractLegalFinalApproval = getById(id);

        List<ContractElectronicInfo> electronicInfoList = contractElectronicInfoService.list(new QueryWrapper<ContractElectronicInfo>()
                .eq("contract_info_id", contractLegalFinalApproval.getContractInfoId()));
        List<FileStorageDO> fileStorages = new ArrayList<>();
        if (electronicInfoList != null && electronicInfoList.size() > 0) {
            for (ContractElectronicInfo contractElectronicInfo : electronicInfoList) {
                FileStorageDO fileStorageDO = fileService.getById(contractElectronicInfo.getFileId());
                fileStorages.add(fileStorageDO);
            }
        }
        contractLegalFinalApproval.setFileStorageList(fileStorages);
        return contractLegalFinalApproval;
    }

    /**
     * 下载zip文件
     */
    private File packagingZip(List<File> fileList, String contractName) {
        // 要生成的压缩文件地址和文件名称
        try {
            contractName = new String(contractName.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e1) {
            throw new BusinessException(CodeUtil.FAIL, "下载合同附件转码失败：" + e1.getMessage());
        }
        String desPath = FileUtil.getAbsolutePath(contractName, ".zip", FileModuleDict.ChildEnum.CONTRACT_FILE_PREVIEW);
        File zipFile = new File(desPath);
        if (!zipFile.getParentFile().exists()) {
            zipFile.getParentFile().mkdirs();
        }
        ZipOutputStream zipStream = null;
        FileInputStream zipSource = null;
        BufferedInputStream bufferStream = null;
        try {
            // 构造最终压缩包的输出流
            zipStream = new ZipOutputStream(new FileOutputStream(zipFile));
            for (File file : fileList) {
                // 将需要压缩的文件格式化为输入流
                zipSource = new FileInputStream(file);
                // 压缩条目不是具体独立的文件，而是压缩包文件列表中的列表项，称为条目，就像索引一样
                ZipEntry zipEntry = new ZipEntry(file.getName());
                // 定位该压缩条目位置，开始写入文件到压缩包中
                zipStream.putNextEntry(zipEntry);
                // 输入缓冲流
                bufferStream = new BufferedInputStream(zipSource, 1024 * 10);
                int read = 0;
                // 创建读写缓冲区
                byte[] buf = new byte[1024 * 10];
                while ((read = bufferStream.read(buf, 0, 1024 * 10)) != -1) {
                    zipStream.write(buf, 0, read);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            try {
                if (null != bufferStream) {
                    bufferStream.close();
                }
                if (null != zipStream) {
                    zipStream.close();
                }
                if (null != zipSource) {
                    zipSource.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return zipFile;
    }

    private void downloadFile(HttpServletResponse res, File file) {
        try {
            if (file.exists()) {
                String fileName = file.getName();
                fileName = new String(fileName.getBytes("UTF-8"));
                String basePath = file.getAbsolutePath();
                InputStream in = new FileInputStream(new File(basePath));
                fileName = URLEncoder.encode(fileName, "UTF-8");
                res.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
                res.setHeader("content-disposition", "attachment;filename=" + fileName);
                OutputStream out = res.getOutputStream();
                byte[] b = new byte[1024];
                int len = -1;
                while ((len = in.read(b)) != -1) {
                    out.write(b, 0, len);
                }
                // 关闭
                out.close();
                in.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
