package com.chushang.common.excel.listener;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.read.listener.PageReadListener;
import com.alibaba.excel.util.ConverterUtils;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validator;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * <h1>Excel 导入监听</h1>
 *
 * <p>
 * 对分页监听的增强
 * </p>
 *
 * @author 单傲
 * @version 1.0
 * @Date 2023-04-18 16:29
 **/
@Slf4j
public class DefaultExcelListener<T> extends PageReadListener<T> {

    private final Boolean isValidate;

    public DefaultExcelListener(Consumer<List<T>> consumer, boolean isValidate) {
        super(consumer);
        this.isValidate = isValidate;
        PageReadListener.BATCH_COUNT = 30000;
    }

    /**
     * <h2>异常处理</h2>
     *
     * @param exception 异常
     * @param context   Excel 上下文
     * @author 单傲
     * @date 2023/4/18 16:32
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        String errMsg;

        if (exception instanceof ExcelDataConvertException convert) {

            Integer index = convert.getColumnIndex();
            errMsg = StrUtil.format("【{}】行，【{}】列的数据解析异常", convert.getRowIndex() + 1, index + 1);

        } else if (exception instanceof ConstraintViolationException violation) {
            Set<ConstraintViolation<?>> violations = Optional
                    .ofNullable(violation.getConstraintViolations()).orElse(CollUtil.newHashSet());

            String collect = violations.stream().map(ConstraintViolation::getMessage)
                    .filter(Objects::nonNull).collect(Collectors.joining(", "));

            errMsg = StrUtil.format("【{}】行输入有误：【{}】", context.readRowHolder()
                    .getRowIndex() + 1, collect);
        } else {
            errMsg = exception.getMessage();
        }

        log.error("#DefaultExcelListener.onException# ：{}", errMsg, exception);

        Assert.isTrue(false, errMsg);
    }

    /**
     * <h2>处理解析的数据</h2>
     *
     * @param t               解析到的数据
     * @param analysisContext 分析上下文
     * @author 单傲
     * @date 2023/4/19 16:43
     */
    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        log.info("解析到一条数据【{}】", JSON.toJSONString(t));

        //去掉Unicode特殊字符
        // UnicodeUtils.removeUnicodeSpecialCharacters(t.getClass(), t);

        // 处理数据校验
        if (isValidate) {
            Set<ConstraintViolation<T>> validate = SpringUtil.getBean(Validator.class)
                    .validate(t);
            if (CollUtil.isNotEmpty(validate)) {
                throw new ConstraintViolationException("参数校验异常", validate);
            }
        }
        super.invoke(t, analysisContext);
    }

    /**
     * <h2>解析头数据</h2>
     *
     * @param headMap 头数据
     * @param context 分析上下文
     * @author 单傲
     * @date 2023/4/19 16:43
     */
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        log.info("解析到表头数据: 【{}】", JSON.toJSONString(ConverterUtils.convertToStringMap(headMap, context)));
    }

    /**
     * <h2>解析完成后调用</h2>
     *
     * @param analysisContext 分析上下文
     * @author 单傲
     * @date 2023/4/19 16:43
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        super.doAfterAllAnalysed(analysisContext);
        log.info("#DefaultExcelListener.onException# ：文件处理完成");
    }
}
