package com.casic.missiles.service.listeners.register;

import cn.hutool.core.lang.Assert;
import com.casic.missiles.dto.business.file.FilePrintRegister;
import com.casic.missiles.enums.notifyevent.AwareFilePrintTypeEnum;
import com.casic.missiles.service.listeners.register.data.PrintFileDataGeneric;
import com.casic.missiles.utils.MinioUtil;
import com.casic.missiles.utils.SpringContextUtil;
import io.minio.GetObjectArgs;
import io.minio.MinioClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.List;
import java.util.Objects;

/**
 * @author cz
 * @date 2023-03-06
 */
@Component
@Slf4j
public class PrintFileRegister {

    @Autowired
    private MinioClient minioClient;
    @Autowired
    private MinioUtil minioUtil;
    @Value("${minio.bucketName}")
    private String bucketName;
    @Value("${casic.file.dir}")
    private String localFileDir;

    /**
     * 打印文件注册到服务器上有以下几个步骤
     * 1、根据打印证书的类型，获取自定义的注册打印文件对象
     * 2、进行远程文件的下载工作，支持文件的读写权限操作
     * 3、进行文件的填充合并等操作，即通过模板进行打印证书的信息注册
     * 4、将注册产生的打印证书信息上传到服务器
     * 5、递归删除因为打印产生的文件信息
     * 6、返还打印产生的证书文件
     */
    public String registerPrintFile(Object printFileRegister, String prefixType) {
        String tempLocalFileDir = localFileDir;
        Assert.isFalse(Objects.isNull(printFileRegister), () -> {
            throw new RuntimeException("the certificateReport is null,please check your params...");
        });
        //获取对应证书的beanName
        String fileDataRegisterBeanName = AwareFilePrintTypeEnum.CERTIFICATE_TYPE_BEAN_MAP
                .getCertificateTypeBeanMap().get(prefixType);
        Assert.isFalse(StringUtils.isEmpty(fileDataRegisterBeanName), () -> {
            throw new RuntimeException("the bean mapped by this certificate type has no mapping relationship...");
        });
        PrintFileDataGeneric fileDataGeneric = SpringContextUtil.getBean(fileDataRegisterBeanName);
        if (fileDataGeneric != null) {
            FilePrintRegister metaData = fileDataGeneric.CustomizedPrintFileRegister(printFileRegister, tempLocalFileDir);
            //获取文件夹目录，没有则创建
            tempLocalFileDir = tempLocalFileDir + metaData.getFileNameId() + File.separator;
            getLocalFileDir(tempLocalFileDir);
            //证书产生准备工作，将相应的文件下载到本地的工作空间，获取相应的模板的读取权限
            for (String fileName : metaData.getBeDownloadedFileNames()) {
                downTemplateFile(fileName, tempLocalFileDir);
            }
            //合并注册
            String certificatePdfUrl = FilePrintRegisterUtils.filePrintGenerate(metaData);
            //上传到服务器
            String certificatePdfName=uploadCertificate(certificatePdfUrl);
            //删除无效的文件
            File invalidFile = new File(tempLocalFileDir);
            log.debug("invalid print file start delete, the file directory  is {}", tempLocalFileDir);
            //删除运行空间下的文件
            deleteFile(invalidFile);
            return certificatePdfName;
        }
        throw new RuntimeException("the bean mapped to this certificate type is empty...");
    }

    /**
     * 创建文件目录,为mini下载文件准备目录
     */
    private void getLocalFileDir(String tempLocalFileDir) {
        File file = new File(tempLocalFileDir); //以.
        if (file.exists()) {
            deleteFile(file);
        }
        boolean dr = file.mkdirs(); //创建目录
    }

    /**
     * 从mini文件服务器下载文件,获取文件的读写权限，用户证书报告的生成
     */
    private void downTemplateFile(String fileName, String tempLocalFileDir) {
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            FilePrintRegisterUtils.byteToFile(bytes, tempLocalFileDir, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 通过生成的证书报告，将证书报告上传到远程mini的文件服务器
     */
    public String uploadCertificate(String certificatePrintUrl) {
        //上传证书
        File file = new File(certificatePrintUrl);
        //获取file对象的文件输入流
        FileInputStream input = null;
        List<String> nameList = null;
        try {
            input = new FileInputStream(file);
            MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), MediaType.MULTIPART_FORM_DATA_VALUE, IOUtils.toByteArray(input));
            MultipartFile[] multipartFiles = new MultipartFile[1];
            multipartFiles[0] = multipartFile;
            nameList = minioUtil.upload(multipartFiles);
        } catch (IOException ie) {
            log.error("Description Failed to upload the certificate to the server，the failed reason is {}...", ie);
        } finally {
            if (input != null) {
                try {
                    input.close();
                } catch (IOException e) {
                    log.error("the upload Certificate input stream release is  anomaly");
                }
            }
            return nameList.get(0);
        }
    }

    /**
     * 对打印报告产生的空间进行清除，还原空间
     */
    private void deleteFile(File invalidFile) {
        //返回一个抽象路径名数组，这些路径名表示此抽象路径名表示的目录中的文件。
        File[] files = invalidFile.listFiles();
        if (files != null) {//如果包含文件进行删除操作
            for (int i = 0; i < files.length; i++) {
                if (files[i].isFile()) {
                    //删除子文件
                    files[i].delete();
                } else if (files[i].isDirectory()) {
                    //通过递归的方法找到子目录的文件
                    deleteFile(files[i]);
                }
            }
        }
        if (invalidFile.list().length == 0) {
            log.debug("invalid print file has already delete success!");
        }
        //删除子目录
        if (invalidFile != null && invalidFile.isDirectory()) {
            invalidFile.delete();
        }
    }
}
