
package com.tmerclub.cloud.common.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.tmerclub.cloud.common.handler.ExcelMergeHandler;
import com.tmerclub.cloud.common.handler.SheetWrite;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 使用说明：
 * 导出：
 * 1.第一列是作为合并单元格的判断字段， 所以一定要为具有唯一标识的字符串
 * 2.列的宽度是自适应的，不需要额外定义
 * 3.WebConfig 类中的 supportedMediaTypes.add(MediaType.ALL) 不要去掉，虽然不影响使用，但是会报
 * No converter for [class com.tmerclub.cloud.common.response.ServerResponseEntity] with preset Content-Type 'application/vnd.ms-excel;charset=utf-8
 * <p>
 * 导入：
 *
 * @author YXF
 * @date 2021/3/9
 */
@SuppressWarnings("rawtypes")
public class ExcelUtil {

    private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class);
    private static final String SHEET_NAME = "sheet";

    /**
     * 导出方法,导出包含字段
     *
     * @param response
     * @param excelName        excel文件名
     * @param mergeRowIndex    从哪一行开始填充数据
     * @param mergeColumnIndex 需要合并的列
     * @return
     * @throws Exception
     */
    public static ExcelWriterBuilder getExcelWriterMerge(HttpServletResponse response, String excelName, int mergeRowIndex, int[] mergeColumnIndex,
                                                         Set<String> includeColumnFieldNames, Class clazz) throws Exception {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        excelName = URLEncoder.encode(excelName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + excelName + ExcelTypeEnum.XLSX.getValue());

        return EasyExcel.write(response.getOutputStream(), clazz)
                // 指定字段导出
                .includeColumnFieldNames(includeColumnFieldNames)
                // 自定义合并规则
                .registerWriteHandler(new ExcelMergeHandler(mergeRowIndex, mergeColumnIndex))
                // 宽度自适应
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
    }

    /**
     * 导出方法
     *
     * @param response
     * @param excelName        excel文件名
     * @param mergeRowIndex    从哪一行开始填充数据
     * @param mergeColumnIndex 需要合并的列
     * @return
     * @throws Exception
     */
    public static ExcelWriterBuilder getExcelWriterMerge(HttpServletResponse response, String excelName, int mergeRowIndex, int[] mergeColumnIndex) throws Exception {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        excelName = URLEncoder.encode(excelName, "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + excelName + ExcelTypeEnum.XLSX.getValue());

        return EasyExcel.write(response.getOutputStream())
                // 自定义合并规则
                .registerWriteHandler(new ExcelMergeHandler(mergeRowIndex, mergeColumnIndex))
                // 宽度自适应
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
    }

    /**
     * 导出模板
     *
     * @param excelWriterBuilder
     * @param map                自定义下拉框 key:列的索引  value：下拉框中多选值的数组
     * @return
     */
    public static ExcelWriterBuilder getExcelModel(ExcelWriterBuilder excelWriterBuilder, Map<Integer, String[]> map, Integer firstRow) {
        if (MapUtil.isEmpty(map)) {
            return excelWriterBuilder;
        }
        SheetWrite sheetWrite = new SheetWrite(map, firstRow);
        excelWriterBuilder.registerWriteHandler(sheetWrite);
        return excelWriterBuilder;
    }


    /**
     * 通用导出方法
     *
     * @param response
     * @param list             导出的数据列表
     * @param excelName        excel文件名
     * @param mergeRowIndex    从哪一行开始填充数据
     * @param mergeColumnIndex 需要合并的列
     * @param clazz            excel对象
     */
    public static void soleExcel(HttpServletResponse response, List list, String excelName, int mergeRowIndex, int[] mergeColumnIndex, Class clazz) {
        ExcelWriter excelWriter = null;
        try {
            // 先执行合并策略
            excelWriter = getExcelWriterMerge(response, excelName, mergeRowIndex, mergeColumnIndex).build();
            // 业务代码
            if (CollUtil.isNotEmpty(list)) {
                // 进行写入操作
                WriteSheet sheetWriter = EasyExcel.writerSheet(SHEET_NAME).head(clazz).build();
                excelWriter.write(list, sheetWriter);
            }
        } catch (Exception e) {
            log.error("导出excel失败", e);
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    /**
     * excel导出(携带处理器)
     */
    public static void soleExcelWithHandler(HttpServletResponse response,
                                            List list,
                                            String excelName,
                                            int mergeRowIndex,
                                            int[] mergeColumnIndex,
                                            RowWriteHandler rowWriteHandler,
                                            Class clazz) {
        ExcelWriter excelWriter = null;
        try {
            // 先执行合并策略
            excelWriter = getExcelWriterMerge(response, excelName, mergeRowIndex, mergeColumnIndex).build();
            // 业务代码
            if (CollUtil.isNotEmpty(list)) {
                // 进行写入操作
                WriteSheet sheetWriter = EasyExcel.writerSheet(SHEET_NAME).head(clazz)
                        .registerWriteHandler(rowWriteHandler).build();
                excelWriter.write(list, sheetWriter);
            }
        } catch (Exception e) {
            log.error("导出excel失败", e);
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

    /**
     * 指定列导出方法
     *
     * @param response
     * @param list                    导出的数据列表
     * @param excelName               excel文件名
     * @param mergeRowIndex           从哪一行开始填充数据
     * @param mergeColumnIndex        需要合并的列
     * @param includeColumnFieldNames 指定需要导出的列名
     * @param clazz                   excel对象
     */
    public static void soleExcel(HttpServletResponse response, List list, String excelName, int mergeRowIndex, int[] mergeColumnIndex, Set<String> includeColumnFieldNames, Class clazz) {
        ExcelWriter excelWriter = null;
        try {
            // 先执行合并策略
            excelWriter = getExcelWriterMerge(response, excelName, mergeRowIndex, mergeColumnIndex, includeColumnFieldNames, clazz).build();
            // 业务代码
            if (CollUtil.isNotEmpty(list)) {
                // 进行写入操作
                WriteSheet sheetWriter = EasyExcel.writerSheet(SHEET_NAME).head(clazz).build();
                excelWriter.write(list, sheetWriter);
            }
        } catch (Exception e) {
            log.error("导出excel失败", e);
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }
}
