package cn.com.qmth.examcloud.tool.utils;

import com.google.common.io.Files;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class FileHelper {

    private static final Logger log = LoggerFactory.getLogger(FileHelper.class);

    /**
     * 获取文件后缀名，包括"."
     */
    public static String getFileSuffix(String fileName) {
        if (StringUtils.isNotEmpty(fileName)) {
            int index = fileName.lastIndexOf(".");
            if (index > 0) {
                return fileName.substring(index).toLowerCase();
            }
        }

        return "";
    }

    /**
     * 创建文件目录
     */
    public static void makeDirs(String dirPath) {
        if (StringUtils.isBlank(dirPath)) {
            return;
        }
        File dir = new File(dirPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }
    }

    /**
     * 读取文件内容（一次性读取）
     */
    public static List<String> readAllLines(File file) {
        try {
            return Files.readLines(file, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new StatusException("读取文件失败！ " + e.getMessage());
        }
    }

    /**
     * 读取文件内容（迭代按行读取）
     */
    public static LineIterator readLines(File file) throws IOException {
        return FileUtils.lineIterator(file, StandardCharsets.UTF_8.name());
    }

    /**
     * 写入文件内容
     */
    public static void writeLines(File file, List<String> lines) {
        writeLines(file, lines, false);
    }

    /**
     * 写入文件内容（支持追加）
     */
    public static void writeLines(File file, List<String> lines, boolean append) {
        if (!file.exists()) {
            makeDirs(file.getParent());
        }

        try {
            if (lines == null) {
                lines = new ArrayList<>();
            }
            FileUtils.writeLines(file, StandardCharsets.UTF_8.name(), lines, append);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new StatusException("写入文件失败！ " + e.getMessage());
        }
    }

    /**
     * 文件压缩
     *
     * @param sourceFile 待压缩的文件或目录
     * @param zipFile    压缩后的文件
     */
    public static void zip(File sourceFile, File zipFile) {
        if (!sourceFile.exists()) {
            throw new StatusException("待压缩的文件或目录不存在！");
        }

        try (OutputStream outStream = new FileOutputStream(zipFile);
             ZipOutputStream zipStream = new ZipOutputStream(outStream, StandardCharsets.UTF_8);) {
            if (sourceFile.isDirectory()) {
                File[] subFiles = sourceFile.listFiles();
                if (subFiles.length == 0) {
                    // 空文件夹
                    return;
                }

                for (File subFile : subFiles) {
                    doZip(zipStream, subFile, null);
                }
            } else {
                doZip(zipStream, sourceFile, null);
            }
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new StatusException("文件压缩失败！");
        }
    }

    private static void doZip(ZipOutputStream zipStream, File curFile, String dirName) throws IOException {
        if (dirName == null) {
            dirName = "";
        }

        String entryName = dirName + curFile.getName();
        log.debug("doZip: {}", entryName);

        if (curFile.isDirectory()) {
            File[] subFiles = curFile.listFiles();
            if (subFiles.length == 0) {
                // 空文件夹
                return;
            }

            String subDirName = entryName + File.separator;
            for (File subFile : subFiles) {
                doZip(zipStream, subFile, subDirName);
            }
        } else {
            try (InputStream is = new FileInputStream(curFile);) {
                zipStream.putNextEntry(new ZipEntry(entryName));

                int len;
                byte[] bytes = new byte[1024];
                while ((len = is.read(bytes)) > 0) {
                    zipStream.write(bytes, 0, len);
                }

                zipStream.closeEntry();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
    }

}
