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.text.csv.CsvReader;
import cn.hutool.core.text.csv.CsvRow;
import cn.hutool.core.text.csv.CsvUtil;
import cn.hutool.core.text.csv.CsvWriter;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import com.ema.common.exceptions.MyAssert;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileReader;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * CSV文件工具类
 */
@Slf4j
public class CsvFileUtil {

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


    /**
     * 写入csv表头
     *
     * @param outPath  输出路径
     * @param titleMap 标题映射：TableMap<"title", "标题">
     * @return CsvWriter
     */
    public static CsvWriter writeHeader(String outPath, LinkedHashMap<String, String> titleMap) {
        CsvWriter writer = CsvUtil.getWriter(outPath, CharsetUtil.CHARSET_UTF_8);
        writer.writeHeaderLine(getTitles(titleMap));
        return writer;
    }

    /**
     * 写入csv内容，每1000条数据进行切分写出，可循环调用写出大量数据到csv文件
     * 所有数据写完后，需要手动 writer.close(); 关闭 CsvWriter
     *
     * @param titleMap 标题映射：TableMap<"title", "标题">
     * @param dataList 数据列表
     * @param writer   CsvWriter
     */
    public static <T> void writeContent(LinkedHashMap<String, String> titleMap, List<T> dataList, CsvWriter writer) {
        //构建csv内容行文件
        if (CollUtil.isNotEmpty(dataList)) {
            // 将一个数据列表按照每1000条数据进行切分，并将每条数据转换为特定格式存储到CSV行中
            String[] fields = ArrayUtil.toArray(titleMap.keySet(), String.class);

            List<List<T>> splitList = ListUtil.split(dataList, 1000);
            for (List<T> list : splitList) {
                // 组装csv内容行
                List<List<String>> writeLine = CollUtil.toList();
                for (T bean : list) {
                    List<String> oneLine = CollUtil.toList();
                    Map<String, Object> beanMap = BeanUtil.beanToMap(bean, fields);
                    for (String field : fields) {
                        oneLine.add(DataConvertUtil.toStr(beanMap.get(field)));
                    }
                    writeLine.add(oneLine);
                }
                writer.write(writeLine);
                writer.flush();
            }
            log.info("写入CSV文件成功，共{}条数据", dataList.size());
        }
    }


    /**
     * 构建CSV标题行
     *
     * @param titleMap
     * @return
     */
    private static String[] getTitles(LinkedHashMap<String, String> titleMap) {
        MyAssert.isTrue(titleMap != null && !titleMap.isEmpty(), "标题映射为空，无法构建CSV标题行");
        return ArrayUtil.toArray(titleMap.values(), String.class);
    }


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

    /**
     * 读取csv文件
     *
     * @param file csv文件
     * @return List<Map<String, String>>
     */
    @SneakyThrows
    public static List<Map<String, String>> readCsvFile(File file) {
        List<Map<String, String>> result = CollUtil.newArrayList();
        CsvReader csvReader = CsvUtil.getReader(new FileReader(file)); // 读取csv文件
        csvReader.setContainsHeader(true); // 设置标题行

        // 读取数据行
        List<CsvRow> csvRows = csvReader.read().getRows();

        csvRows.forEach(csvRow -> result.add(csvRow.getFieldMap()));
        // 关闭csv文件
        csvReader.close();

        return result;
    }

    /**
     * 读取csv文件，并转换为bean
     * @param file  csv文件
     * @param clazz       数据类型
     * @param titleMap    标题映射：TableMap<"title", "标题">, 如果为null，则不做映射
     * @param <T> bean类型
     * @return List<T>
     */
    @SneakyThrows
    public static <T> List<T> readCsvFile(File file, Class<T> clazz, Map<String, String> titleMap) {
        List<T> result = CollUtil.newArrayList();

        CsvReader csvReader = CsvUtil.getReader(new FileReader(file)); // 读取csv文件
        csvReader.setContainsHeader(true); // 设置标题行

        // 读取数据行
        List<CsvRow> csvRows = csvReader.read().getRows();
        for (CsvRow csvRow : csvRows) {
            Map<String, String> fieldMap = csvRow.getFieldMap();
            if (titleMap != null) {
                //将map中的key转换为titleMap中的key
                for (String key : titleMap.keySet()) {
                    String value = titleMap.get(key);
                    if (fieldMap.containsKey(value)) {
                        fieldMap.put(key, fieldMap.get(value));
                        fieldMap.remove(value);
                    }
                }
            }
            //将map转换为bean
            result.add(BeanUtil.mapToBeanIgnoreCase(fieldMap, clazz, true));
        }

        // 关闭csv文件
        csvReader.close();

        return result;
    }

}
