package com.ema.common.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.BigExcelWriter;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import com.ema.common.exceptions.MyAssert;
import lombok.extern.slf4j.Slf4j;

import java.io.InputStream;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * Excel文件工具类
 */
@Slf4j
public class ExcelFileUtil {

    /**
     * 一次性写出Excel文件，适用于数据量较少的情况
     *
     * @param outPath  输出路径：/xxx/xxx/xxx.xlsx
     * @param titleMap 标题映射：TableMap<"title", "标题">
     * @param dataList 数据列表
     * @param <T>      数据类型
     */
    public static <T> void writeExcelFile(String outPath, LinkedHashMap<String, String> titleMap, List<T> dataList) {
        BigExcelWriter writer = ExcelUtil.getBigWriter(outPath);
        writeHeader(titleMap, writer, null, null);
        writeContent(dataList, writer);
        writer.close();
    }

    /**
     * 写入Excel表头
     *
     * @param titleMap   标题映射：TableMap<"title", "标题">
     * @param writer     写入器: BigExcelWriter writer = ExcelUtil.getBigWriter(outPath);
     * @param sheetIndex Sheet的索引，从0开始，如果为null，则默认写入第一个sheet
     * @param sheetName  Sheet的名称，如果为null，则默认
     */
    public static void writeHeader(LinkedHashMap<String, String> titleMap,
                                                 BigExcelWriter writer, Integer sheetIndex, String sheetName) {
        MyAssert.notNull(writer, "BigExcelWriter must not be null");
        MyAssert.notNull(titleMap, "TitleMap must not be null");

        if (sheetIndex != null && sheetIndex >= 0) {
            writer.setSheet(sheetIndex);
        }
        if (StrUtil.isNotBlank(sheetName)) {
            writer.renameSheet(sheetName);
        }
        //设置是否只保留别名中的字段值，如果为true，则不设置alias的字段将不被输出，false表示原样输出
        writer.setOnlyAlias(true);
        //自定义标题别名
        writer.setHeaderAlias(titleMap);
        writer.writeRow(titleMap, true);
        writer.setCurrentRow(1);
    }

    /**
     * 写入Excel内容
     * 所有数据写完后，需要手动 writer.close(); 关闭 BigExcelWriter
     * 注意：
     * 1、如果数据量过大，请不要一次性写入，否则可能会导致内存溢出，请循环分批写入
     * 2、如果总数据量过超过50万，建议请使用 CsvFileUtil
     *
     * @param dataList 数据列表
     * @param writer   写入器
     */
    public static <T> void writeContent(List<T> dataList, BigExcelWriter writer) {
        if (CollUtil.isNotEmpty(dataList)) {
            List<List<T>> splitList = ListUtil.split(dataList, 1000);
            for (List<T> list : splitList) {
                writer.write(list, false);
            }
        }
    }

    /**
     * 验证文件是否正确
     *
     * @param fileName 文件名
     */
    public static void verifyExcelFile(String fileName) {
        MyAssert.isTrue(StrUtil.isNotEmpty(fileName), "没有导入文件");
        MyAssert.isTrue(fileName.endsWith(".xls")
                || fileName.endsWith(".xlsx"), "文件格式不正确,请上传 .xlsx | .xls");
    }

    /**
     * 读取excel文件，返回数据集合
     *
     * @param inputStream 文件流
     * @param clazz       数据类型
     * @param titleMap    标题映射：TableMap<"title", "标题">, 如果为null，则不做映射
     * @return 返回数据集合
     */
    public static <T> List<T> readExcelFile(InputStream inputStream, Class<T> clazz, Map<String, String> titleMap) {
       if (CollUtil.isEmpty(titleMap)) {
           ExcelReader reader = ExcelUtil.getReader(inputStream);
           return reader.readAll(clazz);
       }

        List<T> beanList = CollUtil.toList();
        List<Map<String, Object>> mapList = readExcelFile(inputStream);
        for (Map<String, Object> map : mapList) {
            //将map中的key转换为titleMap中的key
            for (String key : titleMap.keySet()) {
                String value = titleMap.get(key);
                if (map.containsKey(value)) {
                    map.put(key, map.get(value));
                    map.remove(value);
                }
            }
            //将map转换为bean
            beanList.add(BeanUtil.mapToBeanIgnoreCase(map, clazz, true));
        }
        return beanList;
    }

    /**
     * 获取Excel数据
     *
     * @param inputStream 文件流
     * @return
     */
    public static List<Map<String, Object>> readExcelFile(InputStream inputStream) {
        ExcelReader reader = ExcelUtil.getReader(inputStream);
        return reader.readAll();
    }
}
