package com.ruoyi.ccdi.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.ruoyi.ccdi.handler.DictDropdownWriteHandler;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * EasyExcel工具类
 *
 * @author ruoyi
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class EasyExcelUtil {

    /**
     * 导出Excel
     *
     * @param response     响应对象
     * @param list         数据列表
     * @param clazz        实体类
     * @param sheetName    工作表名称
     * @param <T>          泛型
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> list, Class<T> clazz, String sheetName) {
        try {
            setResponseHeader(response, sheetName);
            EasyExcel.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .doWrite(list);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel失败", e);
        }
    }

    /**
     * 导出Excel（指定数据）
     *
     * @param response     响应对象
     * @param list         数据列表
     * @param clazz        实体类
     * @param sheetName    工作表名称
     * @param fileName     文件名称
     * @param <T>          泛型
     */
    public static <T> void exportExcel(HttpServletResponse response, List<T> list, Class<T> clazz, String sheetName, String fileName) {
        try {
            setResponseHeader(response, fileName);
            EasyExcel.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .doWrite(list);
        } catch (IOException e) {
            throw new RuntimeException("导出Excel失败", e);
        }
    }

    /**
     * 导入Excel
     *
     * @param fileName 文件名
     * @param clazz    实体类
     * @param <T>      泛型
     * @return 数据列表
     */
    public static <T> List<T> importExcel(String fileName, Class<T> clazz) {
        try {
            return EasyExcel.read(fileName).head(clazz).sheet().doReadSync();
        } catch (Exception e) {
            throw new RuntimeException("导入Excel失败", e);
        }
    }

    /**
     * 导入Excel（通过输入流）
     *
     * @param inputStream 输入流
     * @param clazz       实体类
     * @param <T>         泛型
     * @return 数据列表
     */
    public static <T> List<T> importExcel(java.io.InputStream inputStream, Class<T> clazz) {
        try {
            return EasyExcel.read(inputStream).head(clazz).sheet().doReadSync();
        } catch (Exception e) {
            throw new RuntimeException("导入Excel失败", e);
        }
    }

    /**
     * 下载导入模板
     *
     * @param response  响应对象
     * @param clazz     实体类
     * @param sheetName 工作表名称
     * @param <T>       泛型
     */
    public static <T> void importTemplateExcel(HttpServletResponse response, Class<T> clazz, String sheetName) {
        try {
            setResponseHeader(response, sheetName + "模板");
            EasyExcel.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .doWrite(List.of());
        } catch (IOException e) {
            throw new RuntimeException("下载导入模板失败", e);
        }
    }

    /**
     * 下载导入模板（带自定义处理器）
     *
     * @param response  响应对象
     * @param clazz     实体类
     * @param sheetName 工作表名称
     * @param handlers  自定义写入处理器
     * @param <T>       泛型
     */
    @SafeVarargs
    public static <T> void importTemplateExcel(HttpServletResponse response, Class<T> clazz, String sheetName,
                                                  WriteHandler... handlers) {
        try {
            setResponseHeader(response, sheetName + "模板");
            var writerBuilder = EasyExcel.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
            // 注册所有自定义处理器
            for (WriteHandler handler : handlers) {
                writerBuilder.registerWriteHandler(handler);
            }
            writerBuilder.doWrite(List.of());
        } catch (IOException e) {
            throw new RuntimeException("下载导入模板失败", e);
        }
    }

    /**
     * 设置响应头
     *
     * @param response 响应对象
     * @param fileName 文件名
     */
    private static void setResponseHeader(HttpServletResponse response, String fileName) {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName + ".xlsx");
    }

    /**
     * 下载带字典下拉框的导入模板
     * 自动解析实体类中的@DictDropdown注解，为对应字段添加下拉框
     *
     * @param response  响应对象
     * @param clazz     实体类
     * @param sheetName 工作表名称
     * @param <T>       泛型
     */
    public static <T> void importTemplateWithDictDropdown(HttpServletResponse response, Class<T> clazz, String sheetName) {
        try {
            setResponseHeader(response, sheetName + "模板");
            EasyExcel.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .registerWriteHandler(new DictDropdownWriteHandler(clazz))
                    .doWrite(List.of());
        } catch (IOException e) {
            throw new RuntimeException("下载带字典下拉框的导入模板失败", e);
        }
    }

    /**
     * 下载带字典下拉框的导入模板（指定文件名）
     * 自动解析实体类中的@DictDropdown注解，为对应字段添加下拉框
     *
     * @param response  响应对象
     * @param clazz     实体类
     * @param sheetName 工作表名称
     * @param fileName  文件名称
     * @param <T>       泛型
     */
    public static <T> void importTemplateWithDictDropdown(HttpServletResponse response, Class<T> clazz,
                                                           String sheetName, String fileName) {
        try {
            setResponseHeader(response, fileName);
            EasyExcel.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .registerWriteHandler(new DictDropdownWriteHandler(clazz))
                    .doWrite(List.of());
        } catch (IOException e) {
            throw new RuntimeException("下载带字典下拉框的导入模板失败", e);
        }
    }

    /**
     * 导出Excel（带字典下拉框）
     * 导出的数据包含实际值，但模板中有下拉框供后续编辑使用
     *
     * @param response  响应对象
     * @param list      数据列表
     * @param clazz     实体类
     * @param sheetName 工作表名称
     * @param <T>       泛型
     */
    public static <T> void exportExcelWithDictDropdown(HttpServletResponse response, List<T> list,
                                                        Class<T> clazz, String sheetName) {
        try {
            setResponseHeader(response, sheetName);
            EasyExcel.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .registerWriteHandler(new DictDropdownWriteHandler(clazz))
                    .doWrite(list);
        } catch (IOException e) {
            throw new RuntimeException("导出带字典下拉框的Excel失败", e);
        }
    }

    /**
     * 导出Excel（带字典下拉框，指定文件名）
     * 导出的数据包含实际值，但模板中有下拉框供后续编辑使用
     *
     * @param response  响应对象
     * @param list      数据列表
     * @param clazz     实体类
     * @param sheetName 工作表名称
     * @param fileName  文件名称
     * @param <T>       泛型
     */
    public static <T> void exportExcelWithDictDropdown(HttpServletResponse response, List<T> list,
                                                        Class<T> clazz, String sheetName, String fileName) {
        try {
            setResponseHeader(response, fileName);
            EasyExcel.write(response.getOutputStream(), clazz)
                    .sheet(sheetName)
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .registerWriteHandler(new DictDropdownWriteHandler(clazz))
                    .doWrite(list);
        } catch (IOException e) {
            throw new RuntimeException("导出带字典下拉框的Excel失败", e);
        }
    }
}
