package com.yonge.toolset.utils.easyexcel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * Excel公共监听类
 *
 * @author hgw
 * Created by 2021-07-22
 */
public class ExcelDataReader<T> extends AnalysisEventListener<T> {
    private static final Logger log = LoggerFactory.getLogger(ExcelDataReader.class);
    //数据
    private final List<ExcelDataReaderProperty<T>> dataList = new ArrayList<>();
    //文件名称
    private final String fileName;
    //行数最大行数限制
    private final int maxRows;
    //当前的sheetNo是第几个
    private Integer currentSheetNo = 0;
    //当前传输行数
    private final AtomicInteger rows = new AtomicInteger(0);
    //异步执行的集合
    private final List<CompletableFuture<ExcelDataReaderProperty<T>>> futureList = new ArrayList<>();
    //校验
    private final Validator vf;

    public ExcelDataReader(int maxRows, String fileName) {
        this.maxRows = maxRows;
        this.fileName = fileName;
        this.vf = Validation.buildDefaultValidatorFactory().getValidator();
    }

    public List<ExcelDataReaderProperty<T>> getDataList() {
        return dataList;
    }

    public String getFileName() {
        return fileName;
    }

    public int getRows() {
        return rows.get();
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        //支持多sheet数据导入，每次换sheet后要清除上一个sheet的数据
        Integer sheetNo = context.readSheetHolder().getSheetNo();
        if (Objects.nonNull(sheetNo) && sheetNo > currentSheetNo) {
            futureList.clear();
            currentSheetNo = sheetNo;
        }
        if (rows.get() > maxRows) {
            throw new RuntimeException("导入的数据超过最大行数限制！" + rows.get());
        }
        CompletableFuture<ExcelDataReaderProperty<T>> future = CompletableFuture.supplyAsync(() -> executed(data, context));
        futureList.add(future);
        rows.incrementAndGet();
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        Integer sheetNo = context.readSheetHolder().getSheetNo();
        //因为空白的sheet也会读取但是不会进入上面的invoke方法(坑)，所以不会清除futureList所以空白的sheet可以直接跳过
        if (Objects.nonNull(sheetNo) && sheetNo > currentSheetNo) {
            return;
        }
        futureList.forEach(f -> dataList.add(f.join()));
        log.info("ExcelDataReader >> fileName {} >> dataList size {}", fileName, dataList.size());
    }

    private ExcelDataReaderProperty<T> executed(T data, AnalysisContext context) {
        ExcelDataReaderProperty<T> property = new ExcelDataReaderProperty<>();
        property.setClazz(data);
        property.setRowIndex(context.readRowHolder().getRowIndex());
        //执行注解校验
        Set<ConstraintViolation<T>> set = vf.validate(data);
        if (CollectionUtils.isNotEmpty(set)) {
            String collect = set.stream()
                    .map(ConstraintViolation::getMessage)
                    .collect(Collectors.joining(", "));
            property.setErrorMessage(collect);
        }
        return property;
    }
}
