package com.skyversation.poiaddr.util;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * exportDataToZip 方法：
 * 首先创建保存 XLSX 文件的目录。
 * 将数据按每 10000 条进行分割。
 * 调用 createXlsxFile 方法将每组数据生成 XLSX 文件。
 * 调用 zipDirectory 方法将生成的 XLSX 文件目录打包成 ZIP 文件。
 * 设置响应头，将 ZIP 文件流输出给客户端。
 * 最后删除临时文件和目录。
 * createXlsxFile 方法：
 * 使用 Apache POI 库创建 XLSX 文件。
 * 生成表头，将数据填充到表格中。
 * 自动调整列宽。
 * zipDirectory 方法：
 * 使用 ZipOutputStream 将指定目录下的文件打包成 ZIP 文件。
 * deleteDirectory 方法：
 * 递归删除指定目录及其所有子文件和子目录。
 */
public class DataExportUtil {
    public static void exportDataToZip(List<Map<String, Object>> dataList, String filePath, HttpServletResponse response,String outFileType) throws IOException {
        // 创建保存 XLSX 文件的目录
        File directory = new File(filePath);
        if (!directory.exists()) {
            directory.mkdirs();
        }

        // 分割数据，每 10000 条数据一组
        List<List<Map<String, Object>>> dataChunks = new ArrayList<>();
        for (int i = 0; i < dataList.size(); i += 10000) {
            dataChunks.add(dataList.subList(i, Math.min(i + 10000, dataList.size())));
        }
        switch (outFileType) {
            case "xlsx":
                // 生成 XLSX 文件
                for (int i = 0; i < dataChunks.size(); i++) {
                    List<Map<String, Object>> chunk = dataChunks.get(i);
                    String fileName = filePath + File.separator + "data_" + (i + 1) + ".xlsx";
                    createXlsxFile(chunk, fileName);
                }
                break;
            case "csv":
                // 生成 CSV 文件
                for (int i = 0; i < dataChunks.size(); i++) {
                    List<Map<String, Object>> chunk = dataChunks.get(i);
                    String fileName = filePath + File.separator + "data_" + (i + 1) + ".csv";
                    createCsvFile(chunk, fileName);
                }
                break;
            case "geojson":
                break;
        }

        // 创建 ZIP 文件
        String zipFilePath = filePath + File.separator + "data.zip";
        zipDirectory(directory, zipFilePath);

        // 设置响应头
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=data.zip");

        // 输出 ZIP 文件流
        try (ServletOutputStream outputStream = response.getOutputStream();
             FileInputStream fileInputStream = new FileInputStream(zipFilePath)) {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
            outputStream.flush();
        }

        // 删除临时文件和目录
        deleteDirectory(directory);
    }

    private static void createXlsxFile(List<Map<String, Object>> data, String filePath) throws IOException {
        try (Workbook workbook = new XSSFWorkbook();
             FileOutputStream fileOut = new FileOutputStream(filePath)) {
            Sheet sheet = workbook.createSheet("Data");

            // 创建表头
            Row headerRow = sheet.createRow(0);
            Set<String> headers = new LinkedHashSet<>();
            for (Map<String, Object> rowData : data) {
                headers.addAll(rowData.keySet());
            }
            int columnIndex = 0;
            for (String header : headers) {
                Cell cell = headerRow.createCell(columnIndex++);
                cell.setCellValue(header);
            }

            // 填充数据
            for (int i = 0; i < data.size(); i++) {
                Row row = sheet.createRow(i + 1);
                columnIndex = 0;
                for (String header : headers) {
                    Cell cell = row.createCell(columnIndex++);
                    Object value = data.get(i).get(header);
                    if (value != null) {
                        cell.setCellValue(value.toString());
                    }
                }
            }

            // 自动调整列宽
            for (int i = 0; i < headers.size(); i++) {
                sheet.autoSizeColumn(i);
            }

            workbook.write(fileOut);
        }
    }

    private static void createCsvFile(List<Map<String, Object>> data, String filePath) throws IOException {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(filePath))) {
            // 获取表头
            Set<String> headers = new LinkedHashSet<>();
            for (Map<String, Object> rowData : data) {
                headers.addAll(rowData.keySet());
            }

            // 写入表头
            writer.write(String.join(",", headers));
            writer.newLine();

            // 写入数据
            for (Map<String, Object> rowData : data) {
                List<String> values = new ArrayList<>();
                for (String header : headers) {
                    Object value = rowData.get(header);
                    values.add(value != null ? value.toString() : "");
                }
                writer.write(String.join(",", values));
                writer.newLine();
            }
        }
    }

    private static void zipDirectory(File directory, String zipFilePath) throws IOException {
        try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFilePath))) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (!file.isDirectory()) {
                        try (FileInputStream fileIn = new FileInputStream(file)) {
                            ZipEntry zipEntry = new ZipEntry(file.getName());
                            zipOut.putNextEntry(zipEntry);

                            byte[] bytes = new byte[1024];
                            int length;
                            while ((length = fileIn.read(bytes)) >= 0) {
                                zipOut.write(bytes, 0, length);
                            }
                        }
                    }
                }
            }
        }
    }

    private static boolean deleteDirectory(File directory) {
        if (directory.exists()) {
            File[] files = directory.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        deleteDirectory(file);
                    } else {
                        file.delete();
                    }
                }
            }
        }
        return directory.delete();
    }
}