package com.ruoyi.cms.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;

/**
 * EasyExcel 工具类（基于 alibaba easyexcel 3.x+）
 */
public class EasyExcelUtils {

    /**
     * 读取 Excel 文件（一次性读取所有数据）
     *
     * @param file      上传的 Excel 文件
     * @param head      实体类字节码（需使用 @ExcelProperty 注解映射表头）
     * @param <T>       实体类泛型
     * @return 解析后的数据集
     */
    public static <T> List<T> readExcel(File file, Class<T> head) {
        return EasyExcel.read(file)
                .head(head)
                .sheet()
                .doReadSync();
    }

    /**
     * 读取 Excel 输入流（一次性读取所有数据）
     *
     * @param inputStream Excel 输入流（如 MultipartFile 的 getInputStream()）
     * @param head        实体类字节码
     * @param <T>         实体类泛型
     * @return 解析后的数据集
     */
    public static <T> List<T> readExcel(InputStream inputStream, Class<T> head) {
        return EasyExcel.read(inputStream)
                .head(head)
                .sheet()
                .doReadSync();
    }

    /**
     * 分批读取 Excel（适用于大数据量，避免内存溢出）
     *
     * @param inputStream Excel 输入流
     * @param head        实体类字节码
     * @param batchSize   每批处理的数据量
     * @param consumer    数据处理函数（如批量保存到数据库）
     * @param <T>         实体类泛型
     */
    public static <T> void readExcelByBatch(InputStream inputStream, Class<T> head, int batchSize, Consumer<List<T>> consumer) {
        EasyExcel.read(inputStream)
                .head(head)
                .sheet()
                .registerReadListener(new AnalysisEventListener<T>() {
                    private List<T> batchList; // 临时存储批数据

                    @Override
                    public void invoke(T data, AnalysisContext context) {
                        if (batchList == null) {
                            batchList = new java.util.ArrayList<>(batchSize);
                        }
                        batchList.add(data);
                        // 达到批处理量时执行消费逻辑
                        if (batchList.size() >= batchSize) {
                            consumer.accept(batchList);
                            batchList.clear(); // 清空集合，释放内存
                        }
                    }

                    @Override
                    public void doAfterAllAnalysed(AnalysisContext context) {
                        // 处理剩余不足一批的数据
                        if (batchList != null && !batchList.isEmpty()) {
                            consumer.accept(batchList);
                        }
                    }
                })
                .doRead();
    }

    /**
     * 生成 Excel 并写入到输出流（通用样式）
     *
     * @param outputStream 输出流（如 HttpServletResponse 的 getOutputStream()）
     * @param data         数据集
     * @param head         实体类字节码
     * @param sheetName    工作表名称
     * @param <T>          实体类泛型
     */
    public static <T> void writeExcel(OutputStream outputStream, List<T> data, Class<T> head, String sheetName) {
        // 构建通用样式策略（表头居中加粗，内容居中）
        HorizontalCellStyleStrategy styleStrategy = getDefaultStyleStrategy();

        ExcelWriterSheetBuilder writerBuilder = EasyExcel.write(outputStream, head)
                .sheet(sheetName)
                .registerWriteHandler(styleStrategy);

        writerBuilder.doWrite(data);
    }

    /**
     * 生成 Excel 并通过 HttpServletResponse 下载（前端直接触发下载）
     *
     * @param response  HttpServletResponse
     * @param data      数据集
     * @param head      实体类字节码
     * @param sheetName 工作表名称
     * @param fileName  下载的文件名（不带后缀）
     * @param <T>       实体类泛型
     * @throws IOException IO异常
     */
    public static <T> void downloadExcel(HttpServletResponse response, List<T> data, Class<T> head,
                                         String sheetName, String fileName) throws IOException {
        // 设置响应头，触发前端下载
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        // 文件名编码，避免中文乱码
        String encodedFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=UTF-8''" + encodedFileName + ".xlsx");

        // 写入数据到响应流
        writeExcel(response.getOutputStream(), data, head, sheetName);
    }

    /**
     * 填充 Excel 模板（适用于带固定格式的模板文件）
     *
     * @param templateInputStream 模板文件输入流
     * @param outputStream        输出流（如响应流或文件流）
     * @param dataMap             填充数据（key为模板中的占位符，value为填充值）
     * @param sheetName           工作表名称
     */
    public static void fillTemplate(InputStream templateInputStream, OutputStream outputStream,
                                    Map<String, Object> dataMap, String sheetName) {
        EasyExcel.write(outputStream)
                .withTemplate(templateInputStream)
                .sheet(sheetName)
                .doFill(dataMap);
    }

    /**
     * 获取默认单元格样式策略（表头加粗居中，内容居中）
     */
    private static HorizontalCellStyleStrategy getDefaultStyleStrategy() {
        // 表头样式
        WriteCellStyle headStyle = new WriteCellStyle();
        WriteFont headFont = new WriteFont();
        headFont.setBold(true); // 加粗
        headFont.setFontHeightInPoints((short) 11);
        headStyle.setWriteFont(headFont);
        headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER); // 水平居中
        headStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 垂直居中

        // 内容样式
        WriteCellStyle contentStyle = new WriteCellStyle();
        WriteFont contentFont = new WriteFont();
        contentFont.setFontHeightInPoints((short) 11);
        contentStyle.setWriteFont(contentFont);
        contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        // 返回样式策略
        return new HorizontalCellStyleStrategy(headStyle, contentStyle);
    }

    /**
     * 关闭流（工具类内部使用）
     */
    private static void closeStream(Closeable... closeables) {
        if (closeables != null) {
            for (Closeable closeable : closeables) {
                if (Objects.nonNull(closeable)) {
                    try {
                        closeable.close();
                    } catch (IOException e) {
                        // 日志记录（建议替换为实际项目的日志框架）
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}