package shop.alien.util.excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import shop.alien.util.common.WebTool;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * EasyExcel 工具类
 * 封装常用的 Excel 导入导出功能
 *
 * @author system
 * @since 2025-01-XX
 */
@Slf4j
public class EasyExcelUtil {

    /**
     * 导出 Excel 文件到 HTTP 响应（自动获取响应对象）
     *
     * @param dataList  要导出的数据列表
     * @param clazz     数据类型的 Class 对象
     * @param fileName  导出的文件名（不带扩展名）
     * @param sheetName 工作表名称
     * @param <T>       数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void exportExcel(List<T> dataList, Class<T> clazz, String fileName, String sheetName) throws IOException {
        HttpServletResponse response = WebTool.getResponse();
        exportExcel(response, dataList, clazz, fileName, sheetName);
    }

    /**
     * 导出 Excel 文件到 HTTP 响应
     *
     * @param response  HttpServletResponse 对象
     * @param dataList  要导出的数据列表
     * @param clazz     数据类型的 Class 对象
     * @param fileName  导出的文件名（不带扩展名）
     * @param sheetName 工作表名称
     * @param <T>       数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> dataList, Class<T> clazz,
                                       String fileName, String sheetName) throws IOException {
        if (dataList == null || dataList.isEmpty()) {
            log.warn("[EasyExcelUtil] 导出数据为空: {}", fileName);
            throw new IllegalArgumentException("导出数据为空");
        }

        // 设置响应头
        setupResponse(response, fileName);

        // 导出Excel
        EasyExcel.write(response.getOutputStream(), clazz)
                .sheet(sheetName != null ? sheetName : "Sheet1")
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自动列宽
                .doWrite(dataList);

        log.info("[EasyExcelUtil] 导出成功: {}, 共 {} 条数据", fileName, dataList.size());
    }

    /**
     * 导出 Excel 文件到 HTTP 响应（使用默认工作表名称）
     *
     * @param response HttpServletResponse 对象
     * @param dataList 要导出的数据列表
     * @param clazz    数据类型的 Class 对象
     * @param fileName 导出的文件名（不带扩展名）
     * @param <T>      数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> dataList, Class<T> clazz, String fileName) throws IOException {
        exportExcel(response, dataList, clazz, fileName, "Sheet1");
    }

    /**
     * 导出 Excel 文件到本地文件
     *
     * @param filePath  文件路径
     * @param dataList  要导出的数据列表
     * @param clazz     数据类型的 Class 对象
     * @param sheetName 工作表名称
     * @param <T>       数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void exportExcelToFile(String filePath, List<T> dataList, Class<T> clazz, String sheetName) throws IOException {
        if (dataList == null || dataList.isEmpty()) {
            log.warn("[EasyExcelUtil] 导出数据为空: {}", filePath);
            throw new IllegalArgumentException("导出数据为空");
        }

        EasyExcel.write(filePath, clazz)
                .sheet(sheetName != null ? sheetName : "Sheet1")
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .doWrite(dataList);

        log.info("[EasyExcelUtil] 导出到文件成功: {}, 共 {} 条数据", filePath, dataList.size());
    }

    /**
     * 导出 Excel 文件到本地文件（使用默认工作表名称）
     *
     * @param filePath 文件路径
     * @param dataList 要导出的数据列表
     * @param clazz    数据类型的 Class 对象
     * @param <T>      数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void exportExcelToFile(String filePath, List<T> dataList, Class<T> clazz) throws IOException {
        exportExcelToFile(filePath, dataList, clazz, "Sheet1");
    }

    /**
     * 导出 Excel 文件到输出流
     *
     * @param outputStream 输出流
     * @param dataList     要导出的数据列表
     * @param clazz        数据类型的 Class 对象
     * @param sheetName    工作表名称
     * @param <T>          数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void exportExcelToStream(OutputStream outputStream, List<T> dataList, Class<T> clazz, String sheetName) throws IOException {
        if (dataList == null || dataList.isEmpty()) {
            log.warn("[EasyExcelUtil] 导出数据为空");
            throw new IllegalArgumentException("导出数据为空");
        }

        EasyExcel.write(outputStream, clazz)
                .sheet(sheetName != null ? sheetName : "Sheet1")
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .doWrite(dataList);

        log.info("[EasyExcelUtil] 导出到流成功, 共 {} 条数据", dataList.size());
    }

    /**
     * 导入 Excel 文件
     *
     * @param file         上传的 MultipartFile 文件
     * @param clazz        数据类型的 Class 对象
     * @param readListener 读取监听器
     * @param <T>          数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void importExcel(MultipartFile file, Class<T> clazz, ReadListener<T> readListener) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("上传文件为空");
        }

        log.info("[EasyExcelUtil] 开始导入Excel文件: {}", file.getOriginalFilename());

        EasyExcel.read(file.getInputStream(), clazz, readListener)
                .sheet()
                .doRead();

        log.info("[EasyExcelUtil] Excel文件导入完成: {}", file.getOriginalFilename());
    }

    /**
     * 导入 Excel 文件（指定Sheet）
     *
     * @param file         上传的 MultipartFile 文件
     * @param clazz        数据类型的 Class 对象
     * @param readListener 读取监听器
     * @param sheetNo      Sheet索引（从0开始）
     * @param <T>          数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void importExcel(MultipartFile file, Class<T> clazz, ReadListener<T> readListener, int sheetNo) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("上传文件为空");
        }

        log.info("[EasyExcelUtil] 开始导入Excel文件: {}, Sheet: {}", file.getOriginalFilename(), sheetNo);

        EasyExcel.read(file.getInputStream(), clazz, readListener)
                .sheet(sheetNo)
                .doRead();

        log.info("[EasyExcelUtil] Excel文件导入完成: {}", file.getOriginalFilename());
    }

    /**
     * 导入 Excel 文件（指定Sheet名称）
     *
     * @param file         上传的 MultipartFile 文件
     * @param clazz        数据类型的 Class 对象
     * @param readListener 读取监听器
     * @param sheetName    Sheet名称
     * @param <T>          数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void importExcel(MultipartFile file, Class<T> clazz, ReadListener<T> readListener, String sheetName) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new IllegalArgumentException("上传文件为空");
        }

        log.info("[EasyExcelUtil] 开始导入Excel文件: {}, Sheet: {}", file.getOriginalFilename(), sheetName);

        EasyExcel.read(file.getInputStream(), clazz, readListener)
                .sheet(sheetName)
                .doRead();

        log.info("[EasyExcelUtil] Excel文件导入完成: {}", file.getOriginalFilename());
    }

    /**
     * 下载导入模板（只有表头，无数据）
     *
     * @param response HttpServletResponse 对象
     * @param clazz    数据类型的 Class 对象
     * @param fileName 文件名（不带扩展名）
     * @param <T>      数据类型
     * @throws IOException 如果发生 IO 异常
     */
    public static <T> void downloadTemplate(HttpServletResponse response, Class<T> clazz, String fileName) throws IOException {
        setupResponse(response, fileName);

        // 导出空列表，只有表头
        EasyExcel.write(response.getOutputStream(), clazz)
                .sheet("Sheet1")
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .doWrite(new java.util.ArrayList<>());

        log.info("[EasyExcelUtil] 模板下载成功: {}", fileName);
    }

    /**
     * 设置 HTTP 响应头
     *
     * @param response HttpServletResponse 对象
     * @param fileName 文件名（不带扩展名）
     * @throws IOException 如果发生 IO 异常
     */
    private static void setupResponse(HttpServletResponse response, String fileName) throws IOException {
        response.reset(); // 重置响应，清除之前可能设置的响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");

        // 使用 RFC 5987 标准格式，更好地支持中文文件名
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
        response.setHeader("Content-Disposition", "attachment;filename=\"" + encodedFileName + ".xlsx\";filename*=utf-8''" + encodedFileName + ".xlsx");

        // 设置缓存控制
        response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
        response.setHeader("Pragma", "no-cache");
        response.setDateHeader("Expires", 0);
    }
}

