package com.qmth.themis.business.util;

import com.qmth.themis.business.annotation.ExcelNotNull;
import com.qmth.themis.business.enums.AttachmentEnum;
import com.qmth.themis.common.enums.ExceptionResultEnum;
import com.qmth.themis.common.exception.BusinessException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.LinkedMultiValueMap;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @Description: excel util
 * @Param:
 * @return:
 * @Author: wangliang
 * @Date: 2020/4/19
 */
public class ExcelUtil {
    private final static Logger log = LoggerFactory.getLogger(ExcelUtil.class);

    /**
     * excel读取
     *
     * @param inputStream
     * @param clazz
     * @param callback
     * @return
     * @throws IOException
     */
    public static List<LinkedMultiValueMap<Integer, Object>> excelReader(InputStream inputStream, List<Class<?>> clazz, ExcelCallback callback) throws IOException {
        try {
            log.info("开始读取excel里的数据");
            long start = System.currentTimeMillis();
            //用流的方式先读取到你想要的excel的文件
            //获取整个excel
            XSSFWorkbook xb = new XSSFWorkbook(inputStream);
            int sheets = xb.getNumberOfSheets();
            List<LinkedMultiValueMap<Integer, Object>> finalOList = new ArrayList<>();
            List<LinkedMultiValueMap<Integer, String>> finalColumnNameList = new ArrayList<>();
            for (int y = 0; y < sheets; y++) {
                //获取第一个表单sheet
                XSSFSheet sheet = xb.getSheetAt(y);
                //获取最后一行
                int lastrow = sheet.getLastRowNum();
                //循环行数依次获取列数
                LinkedMultiValueMap<Integer, Object> oList = new LinkedMultiValueMap<>();
                LinkedMultiValueMap<Integer, String> columnNameList = new LinkedMultiValueMap<>();
                for (int i = 0; i < lastrow + 1; i++) {
                    //获取哪一行i
                    Row row = sheet.getRow(i);
                    if (Objects.nonNull(row)) {
                        //获取这一行的第一列
                        int firstcell = row.getFirstCellNum();//从第二行开始获取
                        //获取这一行的最后一列
                        int lastcell = row.getLastCellNum();
                        Object o = clazz.get(y).newInstance();
                        for (int j = firstcell; j < lastcell; j++) {
                            //获取第j列
                            Cell cell = row.getCell(j);
                            if (i == 0) {
                                if (!Objects.equals(AttachmentEnum.convertToName(String.valueOf(cell)), AttachmentEnum.extendColumn.name())) {
                                    columnNameList.add(y, AttachmentEnum.convertToName(String.valueOf(cell)));
                                } else {
                                    columnNameList.add(y, String.valueOf(cell));
                                }
                            } else {
                                if (Objects.nonNull(cell)) {
                                    Object obj = convert(cell);
                                    Field field = null;
                                    String fieldName = AttachmentEnum.convertToCode(columnNameList.get(y).get(j));
                                    if (Objects.equals(fieldName, AttachmentEnum.extendColumn.getCode())) {
                                        field = o.getClass().getDeclaredField(AttachmentEnum.extendColumn.name());
                                        field.setAccessible(true);
                                        Map map = (Map) field.get(o);
                                        if (Objects.isNull(map)) {
                                            map = new LinkedHashMap<>();
                                        }
                                        map.put(columnNameList.get(y).get(j), obj);
                                        field.set(o, map);
                                    } else {
                                        field = o.getClass().getDeclaredField(columnNameList.get(y).get(j));
                                        field.setAccessible(true);
                                        field.set(o, obj);
                                    }
                                }
                            }
                        }
                        if (i > 0) {
                            oList.add(y, o);
                        }
                    }
                }
                if (oList.size() == 0 && finalOList.size() == 0) {
                    throw new BusinessException(ExceptionResultEnum.EXCEL_NO);
                }
                if (oList.size() > 0) {
                    finalOList.add(oList);
                    finalColumnNameList.add(columnNameList);
                }
                log.info("读取了{}条数据", oList.get(0).size());
            }
            long end = System.currentTimeMillis();
            log.info("读取excel里的数据结束,============耗时============:{}秒", (end - start) / 1000);
            return callback.callback(finalOList, finalColumnNameList);
        } catch (Exception e) {
            log.error("excel读取报错", e);
            if (e instanceof IllegalArgumentException) {
                String errorColumn = e.getMessage().toString();
                if (errorColumn.indexOf("Can not set java.lang.String field") != -1 && errorColumn.indexOf("to java.lang.Long") != -1) {
                    String column = errorColumn.substring(errorColumn.indexOf("Can not set java.lang.String field") + 1, errorColumn.indexOf("to java.lang.Long"));
                    column = column.substring(column.lastIndexOf(".") + 1, column.length());
                    throw new BusinessException("excel列[" + AttachmentEnum.convertToCode(column) + "]为非文本格式");
                } else {
                    throw new BusinessException(e.getMessage());
                }
            } else {
                throw new BusinessException(e.getMessage());
            }
        } finally {
            if (Objects.nonNull(inputStream)) {
                inputStream.close();
            }
        }
    }

    /**
     * 类型转换
     *
     * @param cell
     * @return
     */
    private static Object convert(Cell cell) {
        switch (cell.getCellType()) {
            case Cell.CELL_TYPE_STRING:
                return String.valueOf(cell.getStringCellValue());
            case Cell.CELL_TYPE_BOOLEAN:
                return String.valueOf(cell.getBooleanCellValue());
            case Cell.CELL_TYPE_NUMERIC:
                return Math.round(cell.getNumericCellValue());
        }
        return null;
    }

    /**
     * 校验属性
     *
     * @param obj
     * @param index
     * @param sheetIndex
     * @return
     * @throws IllegalAccessException
     */
    public static List<ExcelError> checkExcelField(Object obj, int index, int sheetIndex) throws IllegalAccessException {
        List<ExcelError> excelErrorList = new ArrayList<>();
        Field[] fields = obj.getClass().getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            field.setAccessible(true);
            Annotation annotation = field.getAnnotation(ExcelNotNull.class);
            if (Objects.isNull(field.get(obj)) && Objects.nonNull(annotation)) {
                excelErrorList.add(new ExcelError(index + 1, "excel第" + (sheetIndex + 1) + "个sheet第" + (index + 1) + "行[" + AttachmentEnum.convertToCode(field.getName()) + "]为空"));
            }
        }
        return excelErrorList;
    }
}