package com.qmth.themis.common.util;

import com.qmth.themis.common.exception.BusinessException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

public class FileUtil {

    /**
     * 读取文件内容
     *
     * @param file
     * @return
     * @throws IOException
     */
    public static String readFileContent(File file) {
        StringBuilder content = new StringBuilder();
        try {
            if (file.exists() && file.isFile()) {
                content.append(FileUtils.readFileToString(file, StandardCharsets.UTF_8));
            }
        } catch (IOException e) {
            throw new BusinessException("读取文件出错" + e.getMessage());
        }
        return content.toString();
    }

    /**
     * 创建文件目录
     */
    public static boolean makeDirs(String path) {
        if (path == null || "".equals(path)) {
            return false;
        }
        File folder = new File(path);
        if (!folder.exists()) {
            return folder.mkdirs();
        }
        return true;
    }

    /**
     * 解压文件
     *
     * @param targetDir 解压目录
     * @param zipFile   待解压的ZIP文件
     * @throws IOException
     */
    public static List<File> unZip(File targetDir, File zipFile) throws IOException {
        if (targetDir == null) {
            throw new BusinessException("解压目录不能为空！");
        }

        if (zipFile == null) {
            throw new BusinessException("待解压的文件不能为空！");
        }

        if (!zipFile.exists()) {
            throw new BusinessException("待解压的文件不存在！" + zipFile.getAbsolutePath());
        }

        String zipName = zipFile.getName().toLowerCase();
        if (zipFile.isDirectory() || zipName.indexOf(".zip") < 0) {
            throw new BusinessException("待解压的文件格式错误！");
        }

        if (!targetDir.exists()) {
            targetDir.mkdir();
        }

        List<File> result = new LinkedList<>();
        try (ZipFile zip = new ZipFile(zipFile, StandardCharsets.UTF_8);) {
            Enumeration entries = zip.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) entries.nextElement();

                // Linux中需要替换掉路径的反斜杠
                String entryName = (File.separator + entry.getName()).replaceAll("\\\\", "/");

                String filePath = targetDir.getAbsolutePath() + entryName;
                File target = new File(filePath);
                if (entry.isDirectory()) {
                    target.mkdirs();
                } else {
                    File dir = target.getParentFile();
                    if (!dir.exists()) {
                        dir.mkdirs();
                    }

                    try (OutputStream os = new FileOutputStream(target); InputStream is = zip.getInputStream(entry);) {
                        IOUtils.copy(is, os);
                        os.flush();
                    }
                    result.add(target);
                }
            }
        }
        return result;
    }

    /**
     * 文件压缩
     *
     * @param target 目录或文件 @param zipFile 压缩后的ZIP文件 @throws IOException @throws
     */
    public static boolean doZip(File target, File zipFile) throws IOException {
        if (target == null || !target.exists()) {
            throw new BusinessException("目录或文件不能为空！");
        }

        if (zipFile == null) {
            throw new BusinessException("待压缩的文件不能为空！");
        }

        try (OutputStream outStream = new FileOutputStream(zipFile);
             ZipOutputStream zipOutStream = new ZipOutputStream(outStream, StandardCharsets.UTF_8);) {
            if (!zipFile.exists()) {
                boolean ok = zipFile.createNewFile();
                if (!ok) {
                    throw new BusinessException("压缩的文件创建失败！");
                }
            }

            if (target.isDirectory()) {
                File[] files = target.listFiles();
                if (files.length == 0) {
                    throw new BusinessException("文件夹内未找到任何文件！");
                }

                for (File file : files) {
                    doZip(zipOutStream, file, null);
                }
            } else {
                doZip(zipOutStream, target, null);
            }
        }
        return true;
    }

    /**
     * @param resultFile
     * @param sourseFiles
     * @return
     * @throws IOException
     */
    public static boolean doZip(File resultFile, List<File> sourseFiles) {
        if (sourseFiles == null || sourseFiles.size() == 0) {
            throw new BusinessException("待压缩的文件不能为空！");
        }

        if (resultFile == null) {
            throw new BusinessException("压缩后的文件不能为空！");
        }
        ZipOutputStream zipOutStream = null;
        try {
            OutputStream outStream = new FileOutputStream(resultFile);
            zipOutStream = new ZipOutputStream(outStream, StandardCharsets.UTF_8);
            if (!resultFile.exists()) {
                boolean ok = resultFile.createNewFile();
                if (!ok) {
                    throw new BusinessException("压缩的文件创建失败！");
                }
            }

            for (File file : sourseFiles) {
                doZip(zipOutStream, file, null);
            }
        } catch (IOException e) {
            throw new BusinessException("压缩的文件创建失败！" + e.getMessage());
        } finally {
            if (zipOutStream != null) {
                try {
                    zipOutStream.close();
                } catch (IOException e) {
                }
            }
        }
        return true;
    }

    private static void doZip(ZipOutputStream zipOutStream, File target, String parentDir) throws IOException {
        if (parentDir == null) {
            parentDir = "";
        }

        if (!"".equals(parentDir) && !parentDir.endsWith(File.separator)) {
            parentDir += File.separator;
        }

        if (target.isDirectory()) {
            File[] files = target.listFiles();
            if (files.length > 0) {
                for (File file : files) {
                    doZip(zipOutStream, file, parentDir + target.getName());
                }
            } else {
                zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
                zipOutStream.closeEntry();
            }
        } else {
            try (InputStream is = new FileInputStream(target);) {
                zipOutStream.putNextEntry(new ZipEntry(parentDir + target.getName()));
                int len;
                byte[] bytes = new byte[1024];
                while ((len = is.read(bytes)) > 0) {
                    zipOutStream.write(bytes, 0, len);
                }
            }
            zipOutStream.closeEntry();
        }
    }

    public static void deleteFolder(String path) {
        File file = new File(path);
        if (file.exists()) {
            if (file.isFile()) {
                deleteFile(path);
            } else {
                deleteDirectory(path);
            }
        }
    }

    public static void deleteFile(String path) {
        File file = new File(path);
        deleteFile(file);
    }

    public static void deleteFile(File file) {
        if (file.isFile() && file.exists()) {
            file.delete();
        }
    }

    public static void deleteDirectory(File dirFile) {
        if (!dirFile.exists() || !dirFile.isDirectory()) {
            return;
        }
        File[] files = dirFile.listFiles();
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                if (files[i].isFile()) {
                    deleteFile(files[i]);
                } else {
                    deleteDirectory(files[i]);
                }
            }
        }

        dirFile.delete();
    }

    public static void deleteDirectory(String path) {
        if (!path.endsWith(File.separator)) {
            path = path + File.separator;
        }
        File dirFile = new File(path);
        deleteDirectory(dirFile);
    }

    private static Cipher initAESCipher(String sKey, String vector, int cipherMode) throws Exception {
        byte[] raw;
        raw = sKey.getBytes();
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(vector.getBytes());
        cipher.init(cipherMode, skeySpec, iv);
        return cipher;
    }

    /**
     * 加密文件
     *
     * @param sourceFile  源文件
     * @param encryptFile 加密后的文件
     * @param sKey        密钥
     * @param vc          向量
     * @throws Exception
     */
    public static void encryptFile(File sourceFile, File encryptFile, String sKey, String vc) {
        decryptOrEncryptFile(Cipher.ENCRYPT_MODE, sourceFile, encryptFile, sKey, vc);
    }

    private static void decryptOrEncryptFile(int cipherMode, File sourceFile, File targetFile, String sKey, String vc) {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            Cipher cipher = initAESCipher(sKey, vc, cipherMode);
            inputStream = new FileInputStream(sourceFile);
            outputStream = new FileOutputStream(targetFile);
            CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
            byte[] buffer = new byte[1024];
            int r;
            while ((r = inputStream.read(buffer)) >= 0) {
                cipherOutputStream.write(buffer, 0, r);
            }
            cipherOutputStream.close();
        } catch (Exception e) {
            throw new BusinessException("加解密文件失败！" + e.getMessage());
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
            }
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
            }
        }
    }

    /**
     * 文件转数组
     *
     * @param file
     * @return
     * @throws IOException
     */
    public static byte[] fileConvertToByteArray(File file) throws IOException {
        byte[] data = null;
        FileInputStream fis = null;
        ByteArrayOutputStream baos = null;
        try {
            fis = new FileInputStream(file);
            baos = new ByteArrayOutputStream();
//            int len;
//            byte[] buffer = new byte[1024];
//            while ((len = fis.read(buffer)) != -1) {
//                baos.write(buffer, 0, len);
//            }
            IOUtils.copy(fis, baos);
            data = baos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (Objects.nonNull(fis)) {
                fis.close();
            }
            if (Objects.nonNull(baos)) {
                baos.flush();
                baos.close();
            }
        }
        return data;
    }
}