package com.ximai.common.utils.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.read.metadata.property.ExcelReadHeadProperty;
import com.alibaba.excel.util.ConverterUtils;
import com.alibaba.excel.util.StringUtils;
import com.ximai.common.exception.ServiceException;
import com.ximai.common.utils.MessageUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.context.NoSuchMessageException;
import org.springframework.dao.DataAccessException;

import java.sql.SQLException;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

public class ExcelReadListener<T> implements ReadListener<T> {
    private List<String> errors = new ArrayList<>();
    private int rowNow = 1;
    private BiConsumer<T,Boolean> reader;
    private Boolean isUpdateSupport;


    public ExcelReadListener(Consumer<T> reader) {
        this.reader = new BiConsumer<T, Boolean>() {
            @Override
            public void accept(T t, Boolean aBoolean) {
                this.accept(t, isUpdateSupport);
            }
        };
        this.isUpdateSupport = false;
    }

    public ExcelReadListener(BiConsumer<T, Boolean> reader, Boolean isUpdateSupport) {
        this.reader = reader;
        this.isUpdateSupport = isUpdateSupport;
    }

    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        ExcelReadHeadProperty excelHeadPropertyData = context.readSheetHolder().excelReadHeadProperty();
        //获取导入的头
        Map<Integer, Head> nowHeadMapData = excelHeadPropertyData.getHeadMap();
        // 国际化处理将名字转换回中文重新匹配,originExcelHeadPropertyData由表头类产生
        ExcelReadHeadProperty originExcelHeadPropertyData = new ExcelReadHeadProperty(context.currentReadHolder(), context.readSheetHolder().getClazz(), null);
        //key下标，val列信息
        Map<Integer, Head> originHeadMapData = originExcelHeadPropertyData.getHeadMap();
        //excel实际列名
        Map<Integer, String> dataMap = ConverterUtils.convertToStringMap(headMap, context);
        Map<Integer, Head> tmpHeadMap = new HashMap<>();
        //临时类
        Map<Integer, ExcelContentProperty> tmpContentPropertyMap = new HashMap<>();
        //循环匹配
        for (Map.Entry<Integer, Head> entry : originHeadMapData.entrySet()) {
            Head headData = entry.getValue();
            Optional<String[]> codesOpt = Optional.ofNullable(headData.getField().getAnnotation(I18nField.class)).map(I18nField::value);
            String headName = headData.getHeadNameList().get(0);
            if(codesOpt.isPresent()){
                try {
                    headName = MessageUtils.message(codesOpt.get()[0]);
                }catch (NoSuchMessageException e){
                    headName = headData.getHeadNameList().get(0);;
                }
            }
            for (Map.Entry<Integer, String> stringEntry : dataMap.entrySet()) {
                if (stringEntry == null) {
                    continue;
                }
                String headString = stringEntry.getValue().trim();
                //下标
                Integer stringKey = stringEntry.getKey();
                if (StringUtils.isEmpty(headString)) {
                    continue;
                }

                if (StringUtils.equals(headName,headString)) {
                    headData.setColumnIndex(stringKey);
                    tmpHeadMap.put(stringKey, headData);
                    break;
                }
            }
        }
        excelHeadPropertyData.setHeadMap(tmpHeadMap);
    }

    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        rowNow++;
        try {
            analysisContext.readRowHolder().getRowType();
            reader.accept(t, isUpdateSupport);
        } catch (Exception ex) {
            ex.printStackTrace();
            Exception outputException = getOutputException(ex);
            errors.add("行号:" + rowNow + outputException.getMessage());
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if(errors.size() >0){
            throw new ServiceException("1100"+String.join(";\n", errors));
        }
    }

    private Exception getOutputException(Exception ex){
        if(ex instanceof ServiceException){
            return ex;
        }
        if(ex instanceof DataAccessException){
            SQLException sqlException = deepFindSQLException((DataAccessException) ex);
            if(sqlException != null){
                switch (sqlException.getErrorCode()){
                    case 1062:
                    case 2627:
                        return new ServiceException("该数据已被添加，如需修改请使用编辑功能", ex);
                    case 1451:
                    case 547:
                        return new ServiceException("该数据已经被其他数据引用，不能删除", ex);
                    default:
                        break;
                }
            }
        }
        return ex;
    }

    private SQLException deepFindSQLException(DataAccessException ex) {
        int varDeep = 3;
        Throwable current = ex.getCause();
        while (varDeep > 0) {
            if(current == null){
                return null;
            }
            if(current instanceof SQLException) {
                return (SQLException) current;
            }
            current = current.getCause();
            varDeep--;
        }
        return null;
    }
}
