package com.tofly.feesapi.common.Excel;

import cn.hutool.core.io.FileUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.tofly.common.core.util.StringUtil;
import lombok.SneakyThrows;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.jxls.common.Context;
import org.jxls.transform.poi.PoiTransformer;
import org.jxls.util.JxlsHelper;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.PropertyPlaceholderHelper;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;

public class ExcleFileUtil {
    public static InputStream getResourcesFileInputStream(String fileName) {
        return Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName);
    }


    public static String getPath() {
        return ExcleFileUtil.class.getResource("/").getPath();
    }


    public static File createNewFile(String pathName) {
        File file = new File(getPath() + pathName);
        if (file.exists()) {
            file.delete();
        } else {
            if (!file.getParentFile().exists()) {
                file.getParentFile().mkdirs();
            }
        }
        return file;
    }

    public static File readFile(String pathName) {
        return new File(getPath() + pathName);
    }

    public static File readUserHomeFile(String pathName) {
        return new File(System.getProperty("user.home") + File.separator + pathName);
    }


    /**
     * Excel  导出公共方法 List<HashMap<String, Object>> dataList
     */
    public static void exportExcel(List<HashMap<String, Object>> dataList, String filePath, String sheetName) {
        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(filePath).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
            excelWriter.write(dataList, writeSheet);
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }


    /**
     * Excel  导出公共方法
     */
    public static void exportExcel1(HttpServletResponse response, List<Object> dataList, Class<?> clazz, String sheetName, String fileName) throws IOException {
        // 设置响应头
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        // 使用 Easy Excel 写入数据到 Excel 文件
        ExcelWriter writer = EasyExcel.write(response.getOutputStream(), clazz).build();
        WriteSheet sheet = EasyExcel.writerSheet(sheetName).build();
        writer.write(dataList, sheet);
        writer.finish();
    }

    /**
     * exportExcel 导出数据 list
     */
    public static void exportExcel(HttpServletResponse response, List<?> list, String fileName, String sheetName, Class<?> clazz) throws IOException {
        // 创建 ExcelWriter 对象
        // 根据用户传入字段 假设我们要忽略 date
        ExcelWriter writer = EasyExcel.write(response.getOutputStream(), clazz).build();
        WriteSheet sheet = EasyExcel.writerSheet(sheetName).build();    // 写入数据
        writer.write(list, sheet);
        // 设置文件名称和响应头
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        //关闭流
        writer.finish();
    }

    /**
     * exportExcel 导出数据 list 可排除不展示数据
     */
    public static void exportExcel(HttpServletResponse response, List<?> list, String fileName, String sheetName, Class<?> clazz, Set<String> excludeColumnFieldNames) throws IOException {
        // 创建 ExcelWriter 对象
        // 根据用户传入字段 假设我们要忽略 date
        ExcelWriter writer = EasyExcel.write(response.getOutputStream(), clazz).excludeColumnFieldNames(excludeColumnFieldNames).build();
        WriteSheet sheet = EasyExcel.writerSheet(sheetName).build();    // 写入数据
        writer.write(list, sheet);
        // 设置文件名称和响应头
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        //关闭流
        writer.finish();
    }


    /**
     * 根据导出数据分多个sheet
     */
    public static void exportExcel(HttpServletResponse response, List<?> dataList, Class<?> clazz, int sheetSize, String fileName) throws IOException {
        // 创建 ExcelWriter 对象
        ExcelWriter writer = EasyExcel.write(response.getOutputStream(), clazz).build();
        // 数据总行数
        int dataSize = dataList.size();
        // 计算 sheet 数量
        int sheetCount = dataSize / sheetSize + (dataSize % sheetSize == 0 ? 0 : 1);
        if (dataSize == 0) {
            WriteSheet sheet = EasyExcel.writerSheet(1, "Sheet1").build();
            writer.write(new ArrayList<>(), sheet);
        } else {
            // 分批写入数据
            for (int sheetNo = 1; sheetNo <= sheetCount; sheetNo++) {
                // 创建当前 sheet 对应的 WriteSheet 对象
                WriteSheet sheet = EasyExcel.writerSheet(sheetNo, "Sheet" + sheetNo).build();
                // 计算当前 sheet 中的数据范围
                int fromIndex = (sheetNo - 1) * sheetSize;
                int toIndex = Math.min(sheetNo * sheetSize, dataSize);
                // 写入数据
                List<?> sheetDataList = dataList.subList(fromIndex, toIndex);
                writer.write(sheetDataList, sheet);
            }
        }

        // 设置文件名称和响应头
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        // 关闭 ExcelWriter 对象
        writer.finish();
    }

    public static void exportExcel(HttpServletResponse response, List<?> dataList, Class<?> clazz, String fileName, String requiredFields) throws IOException {
        exportExcel(response, dataList, clazz, fileName, requiredFields, null);
    }

    public static void exportExcel(HttpServletResponse response, List<?> dataList, Class<?> clazz, String fileName, String requiredFields, Properties properties) throws IOException {
        if (StringUtil.isNotEmpty(requiredFields)) {
            try {
                clazz = AnnotationAwareClassGenerator.createDynamicClass(Arrays.asList(requiredFields.split(",")), clazz);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        // 创建 ExcelWriter 对象
        ExcelWriter writer = EasyExcel.write(response.getOutputStream(), clazz).build();
        // 每个 sheet 的行数
        int sheetSize = 65000;
        // 数据总行数
        int dataSize = dataList.size();
        // 计算 sheet 数量
        int sheetCount = dataSize / sheetSize + (dataSize % sheetSize == 0 ? 0 : 1);
        // 如果 dataList 为空，写入一个空的 sheet

        CellWriteHandler cellWriteHandler = properties == null ? null : new CellWriteHandler() {
            private final PropertyPlaceholderHelper placeholderHelper = new PropertyPlaceholderHelper("${", "}");

            @Override
            public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
                if (isHead) {
                    List<String> headNameList = head.getHeadNameList();
                    if (headNameList != null) {
                        headNameList.forEach(e -> {
                            int i = headNameList.indexOf(e);
                            String newHead = placeholderHelper.replacePlaceholders(e, properties);
                            headNameList.set(i, newHead);
                        });
                    }
                }
            }
        };
        if (dataSize == 0) {
            ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.writerSheet(1, "Sheet1");
            if (properties != null)
                excelWriterSheetBuilder.registerWriteHandler(cellWriteHandler);
            WriteSheet sheet = excelWriterSheetBuilder.build();
            writer.write(new ArrayList<>(), sheet);
        } else {
            // 分批写入数据
            for (int sheetNo = 1; sheetNo <= sheetCount; sheetNo++) {
                ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.writerSheet(sheetNo, "Sheet" + sheetNo);
                //自动调整列宽
                //excelWriterSheetBuilder.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
                if (properties != null)
                    excelWriterSheetBuilder.registerWriteHandler(cellWriteHandler);

                // 创建当前 sheet 对应的 WriteSheet 对象
                WriteSheet sheet = excelWriterSheetBuilder.build();
                // 计算当前 sheet 中的数据范围
                int fromIndex = (sheetNo - 1) * sheetSize;
                int toIndex = Math.min(sheetNo * sheetSize, dataSize);
                // 写入数据
                List<?> sheetDataList = dataList.subList(fromIndex, toIndex);
                writer.write(sheetDataList, sheet);
            }
        }

        // 设置文件名称和响应头
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        // 关闭 ExcelWriter 对象
        writer.finish();
    }

    /**
     * 去读 excle文件数据，返回list,根据实体类
     */
    public static <T> List<T> readExcel(String filePath, Class<T> clazz) {
        String fullPath = ExcleFileUtil.getPath() + filePath;
        ;
        List<T> list = EasyExcel.read(fullPath).head(clazz).sheet().doReadSync();
        return list;
    }


    public static List<Map<Integer, String>> readExcel(String filePath) {
        return EasyExcel.read(filePath).sheet().doReadSync();
    }

    /**
     * 导出EXCEL给前端直接下载
     */
    @SneakyThrows(Exception.class)
    public static void export(Map<String, Object> dataMap, ExcelExportFileTypeEnum excelExportFileTypeEnum,
                              String exportPath, HttpServletResponse response) {
        // 处理导出
        handleExport(dataMap, excelExportFileTypeEnum, exportPath);
        try (OutputStream outputStream = response.getOutputStream();
             InputStream inputStream = new FileInputStream(exportPath)) {
            // ======================= ↓↓↓↓↓↓ 响应给前端 ↓↓↓↓↓↓ =======================
            // 文件名 - 解决中文乱码问题
            String filename = URLEncoder.encode(excelExportFileTypeEnum.getTemplateFile().substring(1), "UTF-8");
            // 设置响应编码
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/x-download");
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);

            byte[] buffer = new byte[1024];
            int i = -1;
            while ((i = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, i);
            }
            outputStream.flush();
        }
    }

    /**
     * 处理导出数据逻辑
     */
    @SneakyThrows(Exception.class)
    private static File handleExport(Map<String, Object> dataMap,
                                     ExcelExportFileTypeEnum excelExportFileTypeEnum, String exportPath) {
        String s = Constant.DEFAULT_REPORT_EXPORT_FOLDER + excelExportFileTypeEnum.getTemplateFile();
        Resource resource = new ClassPathResource(s);
        File exportFile = null;
        try (InputStream templateInputStream = resource.getInputStream(); OutputStream out = new FileOutputStream(exportPath)) {
            // 创建文件
            exportFile = FileUtil.touch(exportPath);
            // 列表数据将存储到指定的excel文件路径
            // 这里的context是jxls框架上的context内容
            Context context = PoiTransformer.createInitialContext();
            // 将列表参数放入context中
            context.putVar("dataMap", dataMap);
            Workbook workbook = WorkbookFactory.create(templateInputStream);
            // Changing name of the first sheet
            workbook.setSheetName(0, excelExportFileTypeEnum.getSheetName());
            PoiTransformer transformer = PoiTransformer.createTransformer(workbook);
            transformer.setOutputStream(out);
            // 将列表数据按照模板文件中的格式生成
            JxlsHelper.getInstance().processTemplate(context, transformer);
        }

        return exportFile;
    }
}
