package com.chinamobile.interfaces.biz.utils;
 
import com.chinamobile.interfaces.biz.config.SignConfig;
import com.chinamobile.interfaces.biz.constant.Constants;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.AesKeyStrength;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
 
/**
 * Zip工具类
 *
 * @author: lp
 */
public class ZipUtil {
 
    private static final Logger log = LoggerFactory.getLogger(ZipUtil.class);
 
    /**
     * 生成zip文件
     * 1.csv文件->压缩成zip
     * 2.产生DataSign.txt文件
     * 3.将1和2两个文件,压缩成zip,密码在yml配置
     *
     * @param list
     * @param fileName
     * @param clazz
     * @param <T>
     */
    public static <T> void generateZip(List<T> list, String fileName, Class<T> clazz) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, SignatureException, InvalidKeyException {
        byte[] bytes = CsvUtil.exportToByteArray(list, clazz);
        byte[] innerZipBytes = ZipUtil.createZipFromByteArray(bytes, fileName + ".csv");
        byte[] signBytes = SignUtil.getEncryptedSignature(bytes);
        List<byte[]> fileDatas = new ArrayList<>();
        List<String> fileNames = new ArrayList<>();
        fileDatas.add(signBytes);
        fileDatas.add(innerZipBytes);
        fileNames.add(Constants.SIGN_TEXT_NAME);
        fileNames.add(fileName + ".zip");
        ZipUtil.createEncryptedZip(fileDatas, fileNames, SignConfig.getZipPath() + fileName + ".zip");
    }
 
    /**
     * 创建ZIP文件
     *
     * @param data      数据
     * @param entryName 条目名称
     * @return
     * @throws IOException
     */
    public static byte[] createZipFromByteArray(byte[] data, String entryName) throws IOException {
        File tempFile = new File(entryName);
        File tempZip = null;
        try (FileOutputStream fileOutputStream = new FileOutputStream(tempFile)) {
            fileOutputStream.write(data);
            tempZip = cn.hutool.core.util.ZipUtil.zip(tempFile);
            return cn.hutool.core.io.FileUtil.readBytes(tempZip);
        } finally {
            boolean fileFlag = tempFile.delete();
            if (!fileFlag) {
                log.error("文件删除异常{}", entryName);
            }
            if (tempZip != null) {
                boolean fileZipFlag=tempZip.delete();
                if (!fileZipFlag) {
                    log.error("压缩文件删除异常{}", entryName);
                }
            }
        }
    }
 
    /**
     * 创建zip文件(设置默认密码)
     *
     * @param filesData  数据集合
     * @param fileNames  文件名集合
     * @param outputPath 输出路径
     * @throws IOException
     */
    public static void createEncryptedZip(List<byte[]> filesData, List<String> fileNames, String outputPath) throws IOException {
        createEncryptedZip(filesData, fileNames, outputPath, SignConfig.getZipPwd());
    }
 
    /**
     * 创建zip文件并设置密码
     *
     * @param filesData  数据集合
     * @param fileNames  文件名集合
     * @param outputPath 输出路径
     * @param password   压缩密码
     * @throws IOException
     */
    public static void createEncryptedZip(List<byte[]> filesData, List<String> fileNames, String outputPath, String password) throws IOException {
        // 创建输出文件
        File outputFile = new File(outputPath);
        // 创建ZipFile对象
        ZipFile zipFile = new ZipFile(outputFile, password.toCharArray());
        // 设置ZIP参数,包括加密
        ZipParameters zipParameters = new ZipParameters();
        zipParameters.setEncryptFiles(true);
        zipParameters.setEncryptionMethod(EncryptionMethod.AES);
        zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256);
        // 添加每个文件到ZIP文件
        for (int i = 0; i < filesData.size(); i++) {
            byte[] fileData = filesData.get(i);
            String fileName = fileNames.get(i);
            // 设置ZIP参数中的文件名
            zipParameters.setFileNameInZip(fileName);
            // 创建一个ByteArrayInputStream来读取字节数组
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(fileData);
            // 添加流到ZIP文件
            zipFile.addStream(byteArrayInputStream, zipParameters);
            // 关闭流
            byteArrayInputStream.close();
        }
    }
 
    /**
     * 解压文件到指定的目录
     *
     * @param zipFilePath 文件所在路径
     * @param outputPath  解压的路径
     * @throws IOException
     */
    public static void extractZipFile(String zipFilePath, String outputPath) throws IOException {
        File zipFile = new File(zipFilePath);
        ZipFile zip = new ZipFile(zipFile);
        zip.extractAll(outputPath);
    }
 
 
    /**
     * 压缩多个文件,指定文件夹为.zip
     *
     * @param sourceDirPath 源文件所在文件夹路径
     * @param zipFilePath   目标压缩文件全路径,包含文件名
     * @throws IOException
     */
    public static void compressFiles(String sourceDirPath, String zipFilePath) throws IOException {
        FileOutputStream fos = new FileOutputStream(zipFilePath);
        ZipOutputStream zos = new ZipOutputStream(fos);
        try {
            File dir = new File(sourceDirPath);
            compress(dir, dir, zos);
        } finally {
            if (zos != null) {
                zos.close();
            }
            if (fos != null) {
                fos.close();
            }
        }
    }
 
 
    /**
     * 压缩单个指定文件为.zip
     *
     * @param sourceFilePath 源文件路径,包含文件名
     * @param sourceDirPath  源文件所在文件夹路径
     * @param zipFilePath    目标压缩文件全路径,包含文件名
     * @throws IOException
     */
    public static void compressFile(String sourceFilePath, String sourceDirPath, String zipFilePath) throws IOException {
        FileOutputStream fos = new FileOutputStream(zipFilePath);
        ZipOutputStream zos = new ZipOutputStream(fos);
        try {
            File file = new File(sourceFilePath);
            File dir = new File(sourceDirPath);
            compress(file, dir, zos);
        } finally {
            if (zos != null) {
                zos.close();
            }
            if (fos != null) {
                fos.close();
            }
        }
    }
 
 
    private static void compress(File sourceFile, File sourceDir, ZipOutputStream zos) throws IOException {
        if (sourceFile.isDirectory()) {
            File[] files = sourceFile.listFiles();
            if (files != null) {
                for (File file : files) {
                    compress(file, sourceDir, zos);
                }
            }
        } else {
            ZipEntry entry = new ZipEntry(sourceFile.getPath().substring(sourceDir.getPath().length() + 1));
            zos.putNextEntry(entry);
            try (FileInputStream fis = new FileInputStream(sourceFile)) {
                byte[] buffer = new byte[1024];
                int len;
                while ((len = fis.read(buffer)) != -1) {
                    zos.write(buffer, 0, len);
                }
            }
            zos.closeEntry();
        }
    }
 
}