package com.engine.salary.util.excel;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.exception.ContextedRuntimeException;
import org.apache.poi.ss.usermodel.Sheet;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;

import static com.engine.salary.util.excel.ExcelSupport.EXCEL_TYPE_XLSX;

/**
 * Excel 解析工具类
 * <p>Copyright: Copyright (c) 2022</p>
 * <p>Company: 泛微软件</p>
 *
 * @author qiantao
 * @version 1.0
 **/
public class ExcelParseHelper {

    //待校验的行号
    private static final int PARSE_EXCEL_ROW_VALID_CELL_INDEX = 0;
    //字符开始下标
    private static final int CHARACTER_FIRST_INDEX = 0;

    /**
     * 将 Excel 解析为 JavaBean 对象
     *
     * @param file            excel文件
     * @param clazz           解析bean的类
     * @param sheetIndex      excel中第几个sheet，从0开始
     * @param rowIndex        从第几行开始解析,第一行是0
     * @param standardCellNum 模板验证，该sheet应有多少列
     * @return
     */
    public static <T> List<T> parse2Map(MultipartFile file, Class<T> clazz, int sheetIndex, int rowIndex, int standardCellNum) {
        List<List<String>> result = parse2Map(file, sheetIndex, rowIndex, standardCellNum);
        List<T> list = new ArrayList<T>();
        for (List<String> rowDatas : result) {
            T t = setField(clazz, rowDatas);
            list.add(t);
        }
        return list;
    }

    /**
     * 将 Excel 解析为 JavaBean 对象
     *
     * @param file            excel文件
     * @param clazz           解析bean的类
     * @param sheetIndex      excel中第几个sheet，从0开始
     * @param rowIndex        从第几行开始解析,第一行是0
     * @param standardCellNum 模板验证，该sheet应有多少列
     * @param fileName        文件名
     * @return
     */
    public static <T> List<T> parse2Map(InputStream file, Class<T> clazz, int sheetIndex, int rowIndex, int standardCellNum, String fileName) {
        List<List<String>> result = parse2Map(file, sheetIndex, rowIndex, standardCellNum, fileName);
        List<T> list = new ArrayList<T>();
        for (List<String> rowDatas : result) {
            T t = setField(clazz, rowDatas);
            list.add(t);
        }
        return list;
    }

    /**
     * <strong>获取excel数据。</strong>
     *
     * @param file       文件
     * @param sheetIndex 解析第几个sheet
     * @param rowIndex   从第几行开始解析，第一行为 0，依次类推
     * @return 二维数据集合
     */
    private static List<List<String>> parse2Map(MultipartFile file, int sheetIndex, int rowIndex, int standardCellNum) {
        Sheet sheet = ExcelSupport.parseFile(file, sheetIndex);
        int rowCount = sheet.getPhysicalNumberOfRows(); // 总行数
        int cellCount = sheet.getRow(PARSE_EXCEL_ROW_VALID_CELL_INDEX).getPhysicalNumberOfCells(); // 总列数

        Validate.isTrue(standardCellNum == cellCount, "Error in excel template! Page %s sheet should have %s column data, existing in %s column , please check the template!", sheetIndex, standardCellNum, cellCount);

        List<List<String>> result = new ArrayList<List<String>>();
        for (; rowIndex < rowCount; rowIndex++) {
            List<String> cellResult = new ArrayList<String>();
            for (int j = 0; j < cellCount; j++) {
                cellResult.add(ExcelSupport.getCellValue(sheet, rowIndex, j));
            }
            result.add(cellResult);
        }
        return result;
    }

    /**
     * <strong>获取excel数据。</strong>
     *
     * @param file       文件
     * @param sheetIndex 解析第几个sheet
     * @param rowIndex   从第几行开始解析，第一行为 0，依次类推
     * @return 二维数据集合
     */
    private static List<List<String>> parse2Map(InputStream file, int sheetIndex, int rowIndex, int standardCellNum, String fileName) {
        Sheet sheet = ExcelSupport.parseFile(file, sheetIndex, fileName);
        int rowCount = sheet.getPhysicalNumberOfRows(); // 总行数
        int cellCount = sheet.getRow(PARSE_EXCEL_ROW_VALID_CELL_INDEX).getPhysicalNumberOfCells(); // 总列数

        Validate.isTrue(standardCellNum == cellCount, "Error in excel template! Page %s sheet should have %s column data, existing in %s column , please check the template!", sheetIndex, standardCellNum, cellCount);

        List<List<String>> result = new ArrayList<List<String>>();
        for (; rowIndex < rowCount; rowIndex++) {
            List<String> cellResult = new ArrayList<String>();
            for (int j = 0; j < cellCount; j++) {
                cellResult.add(ExcelSupport.getCellValue(sheet, rowIndex, j));
            }
            result.add(cellResult);
        }
        return result;
    }


    /**
     * 将sheet数据转为map
     *
     * @param file
     * @param sheetIndex sheet下标
     * @param rowIndex   从哪行开始解析
     * @return
     */
    public static List<Map<String, Object>> parse2Map(InputStream file, int sheetIndex, int rowIndex) {
        Sheet sheet = ExcelSupport.parseFile(file, sheetIndex, EXCEL_TYPE_XLSX);
        int rowCount = sheet.getPhysicalNumberOfRows(); // 总行数
        int cellCount = sheet.getRow(PARSE_EXCEL_ROW_VALID_CELL_INDEX).getPhysicalNumberOfCells(); // 总列数

        List<String> sheetHeader = ExcelSupport.getSheetHeader(sheet, PARSE_EXCEL_ROW_VALID_CELL_INDEX);

        List<Map<String, Object>> result = new ArrayList<>();
        for (; rowIndex < rowCount; rowIndex++) {
            Map<String, Object> cellResult = new HashMap<>();
            for (int j = 0; j < cellCount; j++) {
                String key = sheetHeader.get(j);
                cellResult.put(key, ExcelSupport.getCellValue(sheet, rowIndex, j));
            }
            result.add(cellResult);
        }
        return result;
    }


    /**
     * 将sheet数据转为map
     *
     * @param rowIndex 从哪行开始解析
     * @return
     */
    public static List<Map<String, Object>> parse2Map(Sheet sheet, int rowIndex) {
        int rowCount = sheet.getPhysicalNumberOfRows(); // 总行数
        int cellCount = sheet.getRow(PARSE_EXCEL_ROW_VALID_CELL_INDEX).getPhysicalNumberOfCells(); // 总列数

        List<String> sheetHeader = ExcelSupport.getSheetHeader(sheet, PARSE_EXCEL_ROW_VALID_CELL_INDEX);

        List<Map<String, Object>> result = new ArrayList<>();
        for (; rowIndex < rowCount; rowIndex++) {
            Map<String, Object> cellResult = new HashMap<>();
            for (int j = 0; j < cellCount; j++) {
                String key = sheetHeader.get(j);
                cellResult.put(key, ExcelSupport.getCellValue(sheet, rowIndex, j));
            }
            result.add(cellResult);
        }
        return result;
    }

    /**
     * 将sheet数据转为map
     *
     * @param rowIndex 从哪行开始解析
     * @param headerRowIndex 抽取列数的参考行
     * @return
     */
    public static List<Map<String, Object>> parse2Map(Sheet sheet, int rowIndex, int headerRowIndex) {
        int rowCount = sheet.getPhysicalNumberOfRows(); // 总行数
        int cellCount = sheet.getRow(headerRowIndex).getPhysicalNumberOfCells(); // 总列数

        List<String> sheetHeader = ExcelSupport.getSheetHeader(sheet, headerRowIndex);

        List<Map<String, Object>> result = new ArrayList<>();
        for (; rowIndex < rowCount; rowIndex++) {
            Map<String, Object> cellResult = new HashMap<>();
            for (int j = 0; j < cellCount; j++) {
                String key = sheetHeader.get(j);
                cellResult.put(key, ExcelSupport.getCellValue(sheet, rowIndex, j));
            }
            result.add(cellResult);
        }
        return result;
    }

    /**
     * 将sheet数据转为List
     *
     * @param rowIndex 从哪行开始解析
     * @return
     */
    public static List<List<String>> parse2List(Sheet sheet, int rowIndex) {
        int rowCount = sheet.getPhysicalNumberOfRows(); // 总行数
        int cellCount = sheet.getRow(PARSE_EXCEL_ROW_VALID_CELL_INDEX).getPhysicalNumberOfCells(); // 总列数

        List<List<String>> result = new ArrayList<List<String>>();
        for (; rowIndex < rowCount; rowIndex++) {
            List<String> cellResult = new ArrayList<String>();
            for (int j = 0; j < cellCount; j++) {
                cellResult.add(ExcelSupport.getCellValue(sheet, rowIndex, j));
            }
            result.add(cellResult);
        }
        return result;
    }

    /**
     * 将sheet数据转为List
     * @param rowIndex 从哪行开始解析
     * @param headerRowIndex 抽取列数的参考行
     * @return
     */
    public static List<List<String>> parse2List(Sheet sheet, int rowIndex, int headerRowIndex) {
        int rowCount = sheet.getPhysicalNumberOfRows(); // 总行数
        int cellCount = sheet.getRow(headerRowIndex).getPhysicalNumberOfCells(); // 总列数

        List<List<String>> result = new ArrayList<List<String>>();
        for (; rowIndex < rowCount; rowIndex++) {
            List<String> cellResult = new ArrayList<String>();
            for (int j = 0; j < cellCount; j++) {
                cellResult.add(ExcelSupport.getCellValue(sheet, rowIndex, j));
            }
            result.add(cellResult);
        }
        return result;
    }


    /**
     * 为对象的每一个属性赋值
     *
     * @param clazz
     * @param rowDatas
     * @param <T>
     * @return
     */
    private static <T> T setField(Class<T> clazz, List<String> rowDatas) {
        try {
            T obj = clazz.newInstance();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                if (excelProperty == null) {
                    continue;
                }
                int index = excelProperty.index();
                Object value = getFieldValue(field, rowDatas.get(index), excelProperty);
                field.setAccessible(true);
                field.set(obj, value);
            }
            return obj;
        } catch (Exception e) {
            throw new ExcelParseException(e.getMessage(), e);
        }
    }

    /**
     * <strong>获取javaBean属性值</strong>
     *
     * @param field         javaBean的对象属性
     * @param value         excel中对应的值
     * @param excelProperty javaBean中解析excel注解，包含日期格式、错误提示信息
     * @return
     */
    @SuppressWarnings("rawtypes")
    private static Object getFieldValue(Field field, String value, ExcelProperty excelProperty) {
        if (StringUtils.isEmpty(value)) {
            return null;
        }
        Object val = null;
        try {
            Class typeClass = field.getType();
            if (typeClass == Integer.class) {
                val = Integer.valueOf(value);
            } else if (typeClass == Long.class) {
                val = Long.valueOf(value);
            } else if (typeClass == Float.class) {
                val = Float.valueOf(value);
            } else if (typeClass == Double.class) {
                val = Double.valueOf(value);
            } else if (typeClass == Date.class) {
                val = ExcelSupport.getDate(value, excelProperty.format());
            } else if (typeClass == Short.class) {
                val = Short.valueOf(value);
            } else if (typeClass == Character.class) {
                val = value.charAt(CHARACTER_FIRST_INDEX);
            } else if (typeClass == BigDecimal.class) {
                val = new BigDecimal(value);
            } else {
                val = value;
            }
        } catch (Exception e) {
            throw new ContextedRuntimeException(excelProperty.msg(), e);
        }
        return val;
    }
}
