package com.chushang.common.excel.utils;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.chushang.common.core.exception.ResultException;
import com.chushang.common.core.util.StringUtils;
import com.chushang.common.excel.convert.ExcelBigNumberConvert;
import com.chushang.common.excel.listener.DataListener;
import com.chushang.common.excel.listener.DefaultExcelListener;
import com.chushang.common.excel.listener.ImportListener;
import com.chushang.common.excel.service.ExcelImporter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.function.Consumer;

/**
 * by zhaowenyuan create 2021/12/27 10:49
 * 表格工具
 */
@Slf4j
public class ExcelUtils {
    /**
     * 导出文件时为Writer生成OutputStream.
     *
     * @param response response
     * @return ""
     */
    public static OutputStream getOutputStream(HttpServletResponse response){
        try {
            response.setContentType("application/vnd.ms-excel; charset=utf-8");
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment; filename=filename.xlsx");
            response.setHeader("Pragma", "public");
            response.setHeader("Cache-Control", "no-store");
            response.addHeader("Cache-Control", "max-age=0");
            return response.getOutputStream();
        } catch (IOException e) {
            throw new ResultException("导出excel表格失败!", e);
        }
    }

    @SneakyThrows
    public static <T> void exportList(HttpServletResponse response, Class<T> clazz, List<T> list, String sheetName){
        OutputStream outputStream = getOutputStream(response);
        exportList(outputStream, clazz, list, sheetName);
    }

    @SneakyThrows
    public static <T> void exportList(OutputStream outputStream, Class<T> clazz, List<T> list, String sheetName){
        EasyExcel.write(outputStream, clazz)
                // 自动适配
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                // 大数值自动转换 防止失真
                .registerConverter(new ExcelBigNumberConvert())
                .autoCloseStream(false)
                .sheet(sheetName)
                .doWrite(list);
    }
    public static <T> File exportExcel(List<T> list, Class<T> clazz, String path) {
        File file = FileUtil.touch(path);
        try (OutputStream outputStream = FileUtil.getOutputStream(file)) {
            exportList(outputStream, clazz, list, "");
        } catch (Exception e) {
            log.error("#MerInfoServiceImpl.exportMer# :", e);
            Assert.isTrue(Boolean.FALSE, "文件生成失败");
        }
        return file;
    }
    /**
     * 导出excel
     *
     * @param response  响应类
     * @param fileName  文件名
     * @param sheetName sheet名
     * @param dataList  数据列表
     * @param clazz     class类
     * @param <T>       泛型
     */
    @SneakyThrows
    public static <T> void exportList(HttpServletResponse response, String fileName, String sheetName, List<T> dataList, Class<T> clazz) {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("UTF-8");
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        exportList(response, clazz, dataList, sheetName);
    }

    /**
     * 使用校验监听器 异步导入 同步返回
     *
     * @param is         输入流
     * @param clazz      对象类型
     * @param isValidate 是否 Validator 检验 默认为是
     * @param consumer   消费者
     * @param sheetNo    表号
     * @param sheetName  表名称
     */
    public static <T> void importExcel(InputStream is, Class<T> clazz, boolean isValidate, Consumer<List<T>> consumer, Integer sheetNo, String sheetName) {
        DefaultExcelListener<T> listener = new DefaultExcelListener<>(consumer, isValidate);
        EasyExcel.read(is, clazz, listener).sheet(sheetNo, sheetName).doRead();
    }

    /**
     * 使用校验监听器 异步导入 同步返回
     *
     * @param is       输入流
     * @param clazz    对象类型
     * @param consumer 消费者
     */
    public static <T> void importExcel(InputStream is, Class<T> clazz, Consumer<List<T>> consumer) {
        importExcel(is, clazz, Boolean.TRUE, consumer, null, null);
    }

    /**
     * 读取并导入数据
     *
     * @param excel    excel文件
     * @param consumer 导入逻辑类
     * @param clazz      泛型
     */
    public static <T> void importExcel(MultipartFile excel, Class<T> clazz, Consumer<List<T>> consumer) throws IOException {
        importExcel(excel.getInputStream(), clazz, consumer);
    }

    /**
     * 读取excel的所有sheet数据
     *
     * @param excel excel文件
     * @return List<Object>
     */
    public static <T> List<T> read(MultipartFile excel, Class<T> clazz) {
        DataListener<T> dataListener = new DataListener<>();
        ExcelReaderBuilder builder = getReaderBuilder(excel, dataListener, clazz);
        if (builder == null) {
            return null;
        }
        builder.doReadAll();
        return dataListener.getDataList();
    }

    /**
     * 读取excel的指定sheet数据
     *
     * @param excel   excel文件
     * @param sheetNo sheet序号(从0开始)
     * @return List<Object>
     */
    public static <T> List<T> read(MultipartFile excel, int sheetNo, Class<T> clazz) {
        return read(excel, sheetNo, 1, clazz);
    }

    /**
     * 读取excel的指定sheet数据
     *
     * @param excel         excel文件
     * @param sheetNo       sheet序号(从0开始)
     * @param headRowNumber 表头行数
     * @return List<Object>
     */
    public static <T> List<T> read(MultipartFile excel, int sheetNo, int headRowNumber, Class<T> clazz) {
        DataListener<T> dataListener = new DataListener<>();
        ExcelReaderBuilder builder = getReaderBuilder(excel, dataListener, clazz);
        if (builder == null) {
            return null;
        }
        builder.sheet(sheetNo).headRowNumber(headRowNumber).doRead();
        return dataListener.getDataList();
    }

    public static <T> List<T> read(InputStream inputStream, Class<T> clazz){
        DataListener<T> dataListener = new DataListener<>();
        ExcelReaderBuilder builder = getReaderBuilder(inputStream, dataListener, clazz);
        builder.doReadAll();
        return dataListener.getDataList();
    }

    /**
     * 读取并导入数据
     *
     * @param excel    excel文件
     * @param importer 导入逻辑类
     * @param <T>      泛型
     */
    public static <T> void save(MultipartFile excel, ExcelImporter<T> importer, Class<T> clazz) {
        ImportListener<T> importListener = new ImportListener<>(importer);
        ExcelReaderBuilder builder = getReaderBuilder(excel, importListener, clazz);
        if (builder != null) {
            builder.doReadAll();
        }
    }
    public static <T> void save(List<T> rowList, ExcelImporter<T> importer) {
        importer.save(rowList);
    }


    public static <T> ExcelReaderBuilder getReaderBuilder(InputStream inputStream, ReadListener<T> readListener, Class<T> clazz) {
        return EasyExcel.read(inputStream, clazz, readListener);
    }

    /**
     * 获取构建类
     *
     * @param excel        excel文件
     * @param readListener excel监听类
     * @return ExcelReaderBuilder
     */
    public static <T> ExcelReaderBuilder getReaderBuilder(MultipartFile excel, ReadListener<T> readListener, Class<T> clazz) {
        String filename = excel.getOriginalFilename();
        if (StringUtils.isEmpty(filename)) {
            throw new RuntimeException("请上传文件!");
        }
        if ((!StringUtils.endsWithIgnoreCase(filename, ".xls")
                && !StringUtils.endsWithIgnoreCase(filename, ".xlsx"))) {
            throw new RuntimeException("请上传正确的excel文件!");
        }
        InputStream inputStream;
        try {
            inputStream = new BufferedInputStream(excel.getInputStream());
            return getReaderBuilder(inputStream, readListener, clazz);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
