package org.dromara.zhishu.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.utils.IOUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
public class ExcelFileHandlerUtil {

    /**
     * 处理导出
     *
     * @param data
     * @param type
     * @param shopId
     */
    public static <T> void handleExport(List<T> data, String basePath, String type, String shopId, Class<T> clazz) {
        if (ObjectUtil.isNotEmpty(data)) {
            // 拼接文件名称
            String fileName = type + "_" + shopId + "_0.xlsx";
            // 拼接导出Excel的默认第一个sheet页名称
            String sheetName = type + "_" + shopId + "_0";
            // 用EasyExcel导出工具类，将数据写入Excel文件
            EasyExcelUtil.exportToExcel(basePath, fileName, data, sheetName, clazz);
        }
    }

    // 方法一：同步写入 Excel 文件
    public static <T> void syncWriteRecord(String baseDir, String prefix, List<T> records, String sheetName) {
        try {
            // 构造完整文件路径
            String filename = prefix + "_0.xlsx";
            File dir = new File(baseDir);
            if (!dir.exists()) {
                dir.mkdirs(); // 如果目录不存在，则创建目录
            }
            String fullPath = baseDir + filename;
            File file = new File(fullPath);

            Workbook workbook;
            Sheet sheet;

            // 判断文件是否存在并打开或创建工作簿
            if (file.exists()) {
                try (FileInputStream fis = new FileInputStream(file)) {
                    workbook = WorkbookFactory.create(fis);
                }
            } else {
                workbook = new XSSFWorkbook();
            }

            // 确保有指定名称的sheet，如果没有就创建
            if (workbook.getSheetIndex(sheetName) == -1) {
                sheet = workbook.createSheet(sheetName);
                createHeaderRow(sheet, records.get(0));
            } else {
                sheet = workbook.getSheet(sheetName);
            }

            // 写入数据行
            int nextRowNum = sheet.getLastRowNum() + 1;
            for (T record : records) {
                Row row = sheet.createRow(nextRowNum++);
                writeDataRow(row, record);
            }

            // 写入文件
            try (FileOutputStream fos = new FileOutputStream(file)) {
                workbook.write(fos);
            }

            workbook.close();
        } catch (Exception e) {
            log.error("写入Excel错误-{}", JSONObject.toJSONString(e));
        }
    }

    // 创建表头行（仅在新建 sheet 时调用）
    private static <T> void createHeaderRow(Sheet sheet, T record) {
        Row headerRow = sheet.createRow(0);
        Field[] fields = record.getClass().getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(fields[i].getName());
        }
    }

    // 将记录对象的数据写入到指定的行中
    private static <T> void writeDataRow(Row row, T record) throws IllegalAccessException {
        Field[] fields = record.getClass().getDeclaredFields();

        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
            Cell cell = row.createCell(i);
            Object value = fields[i].get(record);
            setValueToCell(cell, value);
        }
    }

    // 设置单元格值
    private static void setValueToCell(Cell cell, Object value) {
        if (value instanceof String) {
            cell.setCellValue((String) value);
        } else if (value instanceof Number) {
            cell.setCellValue(((Number) value).doubleValue());
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
        } else if (value instanceof java.util.Date) {
            cell.setCellValue((java.util.Date) value);
        } else {
            cell.setCellValue(value != null ? value.toString() : "");
        }
    }

    // 方法二：批量删除指定文件夹下名称前缀相同的文件
    public static void deleteFilesByPrefix(String baseDir, String prefix) {
        File dir = new File(baseDir);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }

        // 遍历目录下的所有文件
        for (File file : Objects.requireNonNull(dir.listFiles())) {
            if (file.isFile() && file.getName().startsWith(prefix)) {
                boolean deleted = file.delete();
                System.out.println((deleted ? "已删除：" : "删除失败：") + file.getAbsolutePath());
            }
        }
    }

    /**
     * 读取指定目录下以 prefix 开头的 Excel 文件，并将其解析为指定 Java 对象列表
     *
     * @param baseDir 目标目录
     * @param prefix  文件名前缀
     * @param clazz   Java对象类型
     * @return 解析后的Java对象列表
     */
    public static <T> List<T> readExcelWithPrefix(String baseDir, String prefix, Class<T> clazz) {
        List<T> result = new ArrayList<>();
        File dir = new File(baseDir);

        if (!dir.exists() || !dir.isDirectory()) {
            log.warn("目录不存在或无效: {}", baseDir);
            return result;
        }

        // 查找匹配的Excel文件
        File targetFile = null;
        for (File file : Objects.requireNonNull(dir.listFiles())) {
            if (file.isFile() && file.getName().startsWith(prefix) && file.getName().endsWith(".xlsx")) {
                targetFile = file;
                break;
            }
        }

        if (targetFile == null) {
            log.info("未找到以 {} 开头的 Excel 文件", prefix);
            return result;
        }

        try (Workbook workbook = WorkbookFactory.create(targetFile)) {
            Sheet sheet = workbook.getSheetAt(0); // 只读取第一个sheet
            Iterator<Row> rowIterator = sheet.iterator();

            if (!rowIterator.hasNext()) {
                log.warn("Excel 文件为空: {}", targetFile.getAbsolutePath());
                return result;
            }

            Row headerRow = rowIterator.next(); // 跳过表头
            Map<Integer, String> headerMap = new HashMap<>();

            // 构建列索引 -> 字段名的映射
            headerRow.forEach(cell -> {
                int columnIndex = cell.getColumnIndex();
                String fieldName = cell.getStringCellValue();
                headerMap.put(columnIndex, fieldName);
            });

            // 获取类字段并设置可访问
            Field[] fields = clazz.getDeclaredFields();
            Map<String, Field> fieldMap = new HashMap<>();
            for (Field field : fields) {
                field.setAccessible(true);
                fieldMap.put(field.getName(), field);
            }

            // 读取数据行
            while (rowIterator.hasNext()) {
                Row dataRow = rowIterator.next();
                T instance = clazz.getDeclaredConstructor().newInstance();

                dataRow.forEach(cell -> {
                    int colIndex = cell.getColumnIndex();
                    String fieldName = headerMap.get(colIndex);
                    Field field = fieldMap.get(fieldName);

                    if (field != null) {
                        try {
                            Object value = getCellValue(cell, field.getType());
                            field.set(instance, value);
                        } catch (Exception e) {
                            log.warn("设置字段 [{}] 值失败: {}", fieldName, e.getMessage());
                        }
                    }
                });

                result.add(instance);
            }

            log.info("成功从文件 {} 中读取 {} 条记录", targetFile.getName(), result.size());

        } catch (Exception e) {
            log.error("读取Excel文件异常: {}", JSONObject.toJSONString(e));
        }

        return result;
    }

    /**
     * 根据 Cell 和目标类型提取对应值
     */
    private static Object getCellValue(Cell cell, Class<?> targetType) throws Exception {
        if (targetType.equals(String.class)) {
            return cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.toString();
        } else if (Number.class.isAssignableFrom(targetType)) {
            if (cell.getCellType() == CellType.NUMERIC) {
                return cell.getNumericCellValue();
            } else {
                String text = cell.toString();
                if (targetType.equals(Integer.class) || targetType.equals(int.class)) {
                    return Integer.parseInt(text);
                } else if (targetType.equals(Long.class) || targetType.equals(long.class)) {
                    return Long.parseLong(text);
                } else if (targetType.equals(Double.class) || targetType.equals(double.class)) {
                    return Double.parseDouble(text);
                } else if (targetType.equals(Float.class) || targetType.equals(float.class)) {
                    return Float.parseFloat(text);
                }
            }
        } else if (targetType.equals(Boolean.class) || targetType.equals(boolean.class)) {
            return cell.getBooleanCellValue();
        } else if (targetType.equals(java.util.Date.class)) {
            return cell.getDateCellValue();
        }

        return cell.toString(); // 默认返回字符串表示
    }

    /**
     * 根据指定目录和多个 type 类型，读取所有符合命名规则的 Excel 文件，
     * 并按照 type -> shopId -> 数据列表 的结构返回。
     *
     * @param baseDir 目标目录
     * @param types   允许读取的 type 列表（如 ["New", "Update"]）
     * @param clazz   Java对象类型
     * @return 嵌套Map: Map<type, Map<shopId, List<T>>>
     */
    public static <T> Map<String, Map<Long, List<T>>> readExcelByTypeAndShopId(
            String baseDir,
            Collection<String> types,
            Class<T> clazz) {
        Map<String, Map<Long, List<T>>> result = new HashMap<>();

        File dir = new File(baseDir);
        if (!dir.exists() || !dir.isDirectory()) {
            log.warn("目录不存在或无效: {}", baseDir);
            return result;
        }

        for (File file : Objects.requireNonNull(dir.listFiles())) {
            if (!file.isFile() || !file.getName().endsWith(".xlsx")) continue;

            String fileName = file.getName();
            if (!fileName.startsWith("Error_")) continue;

            String[] parts = fileName.replace(".xlsx", "").split("_");
            if (parts.length < 5) continue; // Error + type + shopId + threadId + index

            String type = parts[1];
            String shopIdStr = parts[2];

            if (!types.contains(type)) continue;

            Long shopId;
            try {
                shopId = Long.valueOf(shopIdStr);
            } catch (NumberFormatException e) {
                log.warn("无法解析 shopId: {}", shopIdStr);
                continue;
            }

            // 构造 prefix（去掉最后的 _0.xlsx）
            String prefix = fileName.substring(0, fileName.lastIndexOf('_'));

            List<T> dataList = readExcelWithPrefix(baseDir, prefix, clazz);

            result.computeIfAbsent(type, k -> new ConcurrentHashMap<>())
                    .computeIfAbsent(shopId, k -> new ArrayList<>())
                    .addAll(dataList);
        }

        log.info("成功读取 {} 个类型的数据", result.size());
        return result;
    }

    /**
     * 追加数据到Excel文件（保持与EasyExcel相同的表头格式）
     * @param baseDir 基础目录
     * @param prefix 文件名前缀（格式：Type_ShopId）
     * @param records 要追加的数据列表
     * @param clazz 数据类型
     */
    public static <T> void appendToExcel(String baseDir, String prefix, List<T> records, Class<T> clazz) {
        if (CollUtil.isEmpty(records)) {
            return;
        }

        FileOutputStream fos = null;
        Workbook workbook = null;
        try {
            // 构造完整文件路径
            String filename = prefix + ".xlsx";
            File dir = new File(baseDir);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            String fullPath = baseDir + File.separator + filename;
            File file = new File(fullPath);

            // 1. 准备表头（模仿EasyExcel的样式）
            List<String> headers = new ArrayList<>();
            Map<String, Integer> fieldOrder = new HashMap<>();

            // 解析类注解获取表头信息（与EasyExcel相同逻辑）
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                if (excelProperty != null) {
                    String[] value = excelProperty.value();
                    String header = value.length > 0 ? value[0] : field.getName();
                    headers.add(header);
                    fieldOrder.put(field.getName(), excelProperty.index());
                }
            }

            // 2. 处理工作簿
            if (file.exists()) {
                // 已有文件：追加模式
                try (FileInputStream fis = new FileInputStream(file)) {
                    workbook = WorkbookFactory.create(fis);
                }

                Sheet sheet = workbook.getSheet(prefix);
                if (sheet == null) {
                    sheet = workbook.createSheet(prefix);
                    // 创建与EasyExcel相同的表头
                    createEasyExcelStyleHeader(sheet, headers);
                }
            } else {
                // 新文件
                workbook = new XSSFWorkbook();
                Sheet sheet = workbook.createSheet(prefix);
                // 创建与EasyExcel相同的表头
                createEasyExcelStyleHeader(sheet, headers);
            }

            // 3. 追加数据
            Sheet sheet = workbook.getSheet(prefix);
            int lastRowNum = sheet.getLastRowNum();
            int startRow = (lastRowNum == 0 && sheet.getRow(0) != null) ? 1 : lastRowNum + 1;

            // 按字段顺序写入数据
            for (int i = 0; i < records.size(); i++) {
                T record = records.get(i);
                Row row = sheet.createRow(startRow + i);

                for (Field field : fields) {
                    field.setAccessible(true);
                    ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                    if (excelProperty != null) {
                        int colIndex = excelProperty.index();
                        Object value = field.get(record);
                        Cell cell = row.createCell(colIndex);
                        setCellValue(cell, value);
                    }
                }
            }

            // 4. 写入文件（增量写入）
            fos = new FileOutputStream(file);
            workbook.write(fos);

        } catch (Exception e) {
            log.error("追加数据到Excel文件错误", e);
            throw new RuntimeException("导出Excel文件失败", e);
        } finally {
            IOUtils.closeQuietly(workbook);
            IOUtils.closeQuietly(fos);
        }
    }

    /**
     * 创建与EasyExcel风格一致的表头
     */
    private static void createEasyExcelStyleHeader(Sheet sheet, List<String> headers) {
        Row headerRow = sheet.createRow(0);

        // 设置表头样式（模仿EasyExcel的默认样式）
        CellStyle headerStyle = sheet.getWorkbook().createCellStyle();
        Font font = sheet.getWorkbook().createFont();
        font.setBold(true);
        font.setFontHeightInPoints((short) 12);
        headerStyle.setFont(font);
        headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        headerStyle.setBorderBottom(BorderStyle.THIN);

        // 写入表头
        for (int i = 0; i < headers.size(); i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers.get(i));
            cell.setCellStyle(headerStyle);
            // 自动调整列宽
            sheet.autoSizeColumn(i);
        }
    }

    /**
     * 设置单元格值（类型处理）
     */
    private static void setCellValue(Cell cell, Object value) {
        if (value == null) {
            cell.setCellValue("");
        } else if (value instanceof Number) {
            cell.setCellValue(((Number) value).doubleValue());
        } else if (value instanceof Boolean) {
            cell.setCellValue((Boolean) value);
        } else if (value instanceof Date) {
            cell.setCellValue((Date) value);
            // 设置日期格式
            CellStyle style = cell.getSheet().getWorkbook().createCellStyle();
            style.setDataFormat(cell.getSheet().getWorkbook().createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));
            cell.setCellStyle(style);
        } else {
            cell.setCellValue(value.toString());
        }
    }
}