package org.dromara.zhishu.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriterBuilder;
import com.opencsv.ICSVWriter;
import lombok.RequiredArgsConstructor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.json.utils.JsonUtils;

import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;

import com.alibaba.fastjson.JSON;
import org.dromara.zhishu.domain.vo.BookEditIsOnsaleVo;
import org.dromara.zhishu.domain.vo.BookEditPriceVo;
import org.dromara.zhishu.domain.vo.BookEditStockVo;
import org.dromara.zhishu.domain.vo.BookVo;
import org.dromara.zhishu.service.client.FileClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;


/**
 * EasyExcel工具类
 *
 * @author CWW
 * @date 2024/12/18 16:46
 */
@RequiredArgsConstructor
@Component
public class EasyExcelUtil {

    private static final String UTF_8 = "UTF-8";

    /**
     * 读取文件数据，并返回一个包含指定类型数据的列表。
     *
     * @param inputStream 文件对象输入流
     * @param clazz       数据对象的类型
     * @param <T>         泛型类型，表示数据对象的类型
     * @return 包含解析后数据对象的列表
     */
    public static <T> List<T> readMultipartFile(InputStream inputStream, Class<T> clazz) {
        // 用于存储解析后的数据对象
        final List<T> dataList = new ArrayList<>();

        EasyExcel.read(inputStream, clazz, new AnalysisEventListener<T>() {
            @Override
            public void invoke(T data, AnalysisContext analysisContext) {
                // 每一条数据解析都会调用此方法
                // 将解析后的数据对象添加到 dataList中
                dataList.add(data);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                // 读取完成后可以进行一些操作
            }
        }).sheet(0).doRead(); // 读取 Excel 文件中的第一个工作表

        // 返回解析后数据对象的列表
        return dataList;
    }


    /**
     * 将数据列表写入 Excel 文件，并设置表头
     *
     * @param filePath 文件路径
     * @param data     数据列表
     * @param headMap  表头配置，键为字段名，值为表头显示名称
     */
    public static void writeExcel(String filePath, List<Map<String, String>> data, Map<String, String> headMap) {
        try (
                Workbook workbook = new XSSFWorkbook();
                FileOutputStream fileOut = new FileOutputStream(filePath)) {
            Sheet sheet = workbook.createSheet("Sheet1");
            // 创建表头
            Row headerRow = sheet.createRow(0);
            Cell headerCell = headerRow.createCell(0);
            headerCell.setCellValue("书号");
            Cell headerCell4 = headerRow.createCell(1);
            headerCell4.setCellValue("书名");
            Cell headerCell1 = headerRow.createCell(2);
            headerCell1.setCellValue("价格");
            Cell headerCell2 = headerRow.createCell(3);
            headerCell2.setCellValue("库存");
            Cell headerCell3 = headerRow.createCell(4);
            headerCell3.setCellValue("日志");
            Cell headerCell5 = headerRow.createCell(5);
            headerCell5.setCellValue("三方平台商品id");
            workbook.write(fileOut);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将 Map 列表转换为 JSON 字符串并存储到文本文件中
     *
     * @param fileName 文件名称
     * @param data     数据列表
     */
    public static void writeJsonToFile(String fileName, List<Map<String, Object>> data) {
        try {
            String filePath = UrlUtil.getUrl() + fileName;
            // 获取文件路径的父目录
            Path parentDir = Paths.get(filePath).getParent();
            // 如果父目录不存在，则创建它
            if (parentDir != null && !Files.exists(parentDir)) {
                Files.createDirectories(parentDir);
            }

            // 将 Map 列表转换为 JSON 字符串
            String jsonString = JSON.toJSONString(data);

            // 将 JSON 字符串写入文件
            try (FileWriter fileWriter = new FileWriter(filePath)) {
                fileWriter.write(jsonString);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void writeJsonToFile(String fileName, String jsonString) {
        try {
            jsonString = jsonString == null ? "" : jsonString;
            String filePath = UrlUtil.getUrl() + fileName;
            // 获取文件路径的父目录
            Path parentDir = Paths.get(filePath).getParent();
            // 如果父目录不存在，则创建它
            if (parentDir != null && !Files.exists(parentDir)) {
                Files.createDirectories(parentDir);
            }

            // 将 JSON 字符串写入文件
            try (FileWriter fileWriter = new FileWriter(filePath)) {
                fileWriter.write(jsonString);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取text文本文件中的字符串
     *
     * @param filePath
     * @return
     */
    public static List<Map<String, Object>> readFileContent(String filePath) {
        StringBuilder contentBuilder = new StringBuilder();
        try {
            Path path = Paths.get(filePath);
            List<String> lines = Files.readAllLines(path);
            for (String line : lines) {
                contentBuilder.append(line).append(System.lineSeparator());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        List<Map<String, Object>> mapList = JsonUtils.parseObject(contentBuilder.toString(), List.class);
        return mapList;
    }

    public static String readFileContentString(String filePath) {
        StringBuilder contentBuilder = new StringBuilder();
        try {
            Path path = Paths.get(filePath);
            // 先判断文件是否存在
            if (!Files.exists(path)) {
                return "";
            }
            List<String> lines = Files.readAllLines(path);
            for (String line : lines) {
                contentBuilder.append(line).append(System.lineSeparator());
            }
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
        return contentBuilder.toString();
    }


    /**
     * 根据文件路径持续写入数据
     *
     * @param excelFilePath 文件路径
     */
    public static void continuousWriting(String excelFilePath, List<String> list) {
        try (
                FileInputStream fis = new FileInputStream(excelFilePath);
                Workbook workbook = new XSSFWorkbook(fis)) {
            // 获取第一个工作表
            Sheet sheet = workbook.getSheetAt(0);
            // 创建一行
            Row row = sheet.createRow(sheet.getLastRowNum() + 1);
            // 创建单元格并写入数据
            for (int i = 0; i < list.size(); i++) {
                Cell cell = row.createCell(i);
                cell.setCellValue(list.get(i));
            }
            // 写入文件
            try (FileOutputStream fos = new FileOutputStream(excelFilePath)) {
                workbook.write(fos);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 导出数据到Excel文件的通用方法。
     *
     * @param <T>       数据对象类型
     * @param basePath  文件保存的基础路径
     * @param fileName  文件名（包括扩展名）
     * @param data      要导出的数据列表
     * @param sheetName Excel工作表名称
     * @param headClazz 数据对象类，用于映射Excel列标题
     */
    public static <T> void exportToExcel(String basePath, String fileName, List<T> data, String sheetName, Class<T> headClazz) {
        // 确保目录存在
        new File(basePath).mkdirs();

        String filePath = basePath + fileName;

        try {
            // 使用 EasyExcel 写入数据
            EasyExcel.write(filePath, headClazz)
                    .sheet(sheetName.length() > 31 ? sheetName.substring(0, 31) : sheetName)
                    .doWrite(data);
        } catch (Exception e) {
            // 处理异常
            e.printStackTrace();
            // 可以在这里添加更多的错误处理逻辑，例如记录日志、通知等
            throw new RuntimeException("导出Excel文件失败", e);
        }
    }

    public static List<Map<String, Object>> convertExcelToMap(String filePath) {
        List<Map<String, Object>> result = new ArrayList<>();

        try (FileInputStream fileInputStream = new FileInputStream(new File(filePath));
             Workbook workbook = new XSSFWorkbook(fileInputStream)) {

            Sheet sheet = workbook.getSheetAt(0); // 获取第一个工作表
            Iterator<Row> rowIterator = sheet.iterator();

            // 获取表头（第一行）
            Row headerRow = rowIterator.next();
            List<String> headers = new ArrayList<>();
            for (Cell cell : headerRow) {
                headers.add(cell.getStringCellValue());
            }

            // 遍历数据行
            while (rowIterator.hasNext()) {
                Row row = rowIterator.next();
                Map<String, Object> rowMap = new HashMap<>();

                for (int i = 0; i < headers.size(); i++) {
                    Cell cell = row.getCell(i, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
                    rowMap.put(headers.get(i), getCellValue(cell));
                }

                result.add(rowMap);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }


    /**
     * 从URL读取Excel文件并转换为指定类型的对象列表
     * @param url Excel文件的URL地址
     * @param clazz 要转换的目标类类型
     * @param <T> 泛型类型
     * @return 读取后的对象列表
     * @throws RuntimeException 当读取失败时抛出
     */
    public static <T> List<T> readExcelFromUrl(String url, Class<T> clazz) {
        try (InputStream inputStream = new URL(url).openStream()) {
            return EasyExcel.read(inputStream)
                    .head(clazz)
                    .sheet()
                    .doReadSync();
        } catch (Exception e) {
            throw new RuntimeException("读取Excel失败: " + e.getMessage(), e);
        }
    }



    private static Object getCellValue(Cell cell) {
        switch (cell.getCellType()) {
            case STRING:
                return cell.getStringCellValue();
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    return cell.getLocalDateTimeCellValue();
                } else {
                    return cell.getNumericCellValue();
                }
            case BOOLEAN:
                return cell.getBooleanCellValue();
            case FORMULA:
                return cell.getCellFormula();
            default:
                return "";
        }
    }

    public static void writeTxtMsgLog(Long taskId, Integer totalNum, Integer executedNum) {
        StringBuffer stringBuffer = new StringBuffer();
        String str = "开始执行文件" + System.lineSeparator() +
                "开始进行数据加载" + System.lineSeparator() +
                "数据加载完毕" + System.lineSeparator() +
                "正在执行" + System.lineSeparator() +
                "已执行数据：" + executedNum + "条" + System.lineSeparator() +
                "待执行数据：" + (totalNum - executedNum) + "条" + System.lineSeparator();
        stringBuffer.append(str);
        if (executedNum.equals(totalNum)) {
            stringBuffer.append("文件执行完成");
        }
        // 记录日志
        EasyExcelUtil.writeJsonToFile(taskId + "-msg.txt", stringBuffer.toString());
    }

    public static void writeTxtLog(Long taskId, Long shopId, String shopName, Integer total, Integer executedNum) {
        List<Map<String, Object>> mapList = new ArrayList<>();
        Map<String, Object> map = new HashMap<>();
        map.put("shopId", shopId);
        map.put("shopName", shopName);
        map.put("num", executedNum);
        // 计算百分比
        map.put("progress", CountUtils.calculatePercentage(total, executedNum));
        map.put("createTime", DateUtilsUtils.getNowDate());
        map.put("updateTime", DateUtilsUtils.getNowDate());
        map.put("taskId", taskId);
        mapList.add(map);
        // 写入txt文件中
        EasyExcelUtil.writeJsonToFile(taskId + ".txt", mapList);
    }

    /**
     * 存储任务中店铺里的日志类型进度
     *
     * @param taskId
     * @param shopId
     * @param shopName
     * @param logName
     * @param mark
     * @param total
     */
    public static void writeDetailTxtLog(Long taskId, Long shopId, String shopName, Integer total, Integer executedNum, String logName, String mark) {
        String filePath = UrlUtil.getUrl() + taskId + "" + shopId + ".txt";
        // true为txt文件中不存在店铺信息，false代表存在
        Boolean bool = true;
        List<Map<String, Object>> mapList = new ArrayList<>();
        if (FileUtils.isFileExists(filePath)) {
            mapList = EasyExcelUtil.readFileContent(filePath);
            // txt文本不为空，则遍历List对象，查看是否存在店铺id，存在，则数量+1,不存在则添加
            for (Map<String, Object> map : mapList) {
                if (map.get("logName").equals(logName)) {
                    bool = false;
                    map.put("num", executedNum);
                    map.put("progress", CountUtils.calculatePercentage(total, executedNum));
                }
            }
        }
        if (bool) {
            Map<String, Object> map = new HashMap<>();
            map.put("shopId", shopId);
            map.put("shopName", shopName);
            map.put("num", executedNum);
            // 计算百分比
            map.put("progress", CountUtils.calculatePercentage(total, executedNum));
            map.put("taskId", taskId);
            map.put("logName", logName);
            map.put("mark", mark);
            mapList.add(map);
        }
        // 写入txt文件中
        EasyExcelUtil.writeJsonToFile(taskId + "" + shopId + ".txt", mapList);
    }



    /**
     * 将 List<Map<String, Object>> 转换为CSV并上传到资源服务器
     *
     * @param fileName 文件名（如 output.csv）
     * @param dataList 数据列表
     * @param headers  表头字段（用于生成列名）
     * @return 是否上传成功
     */
    public static boolean writeMapsToCsvWithHeader(String fileName, List<Map<String, Object>> dataList,
                                                   List<String> headers, FileClient fileClient,Long mallId) {
        try {
            // 1. 将数据转换为CSV格式的字符串
            StringWriter stringWriter = new StringWriter();
            try (ICSVWriter csvWriter = new CSVWriterBuilder(stringWriter)
                    .build()) {
                // 写入表头
                csvWriter.writeNext(headers.toArray(new String[0]));
                // 写入数据
                for (Map<String, Object> data : dataList) {
                    String[] row = new String[headers.size()];
                    for (int j = 0; j < headers.size(); j++) {
                        String header = headers.get(j);
                        Object value = data.get(header);
                        // 如果是 sku_list 字段，转换为 JSON 字符串
                        if ("sku_list".equals(header) && value instanceof List<?>) {
                            row[j] = JSON.toJSONString(value); // 转换为 JSON 字符串
                        } else {
                            row[j] = value != null ? value.toString() : "";
                        }
                    }
                    csvWriter.writeNext(row);
                }
            }
            // 2. 将CSV内容转换为Base64
            String csvContent = stringWriter.toString();
            String base64Content = Base64.getEncoder().encodeToString(csvContent.getBytes(StandardCharsets.UTF_8));

            // 3. 上传到资源服务器
            R<String> result = fileClient.upload(UrlUtil.getFileServiceUrl(), base64Content, "ShopGoodsDataCSV/"+mallId, fileName);

            return R.isSuccess(result);

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
    // public static void writeMapsToCsvWithHeader(String filePath, List<Map<String, Object>> dataList, List<String> headers, boolean append) {
    //     Path path = Paths.get(filePath);
    //     Path parentDir = path.getParent();
    //
    //     try {
    //         // 1. 创建父目录（如果不存在）
    //         if (parentDir != null && !Files.exists(parentDir)) {
    //             Files.createDirectories(parentDir);
    //         }
    //
    //         // 2. 创建文件（如果不存在）
    //         if (!Files.exists(path)) {
    //             Files.createFile(path);
    //         }
    //
    //         // 3. 使用 BufferedWriter + OpenCSV 高性能写入
    //         try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8);
    //              ICSVWriter csvWriter = new CSVWriterBuilder(writer)
    //                      .withQuoteChar('"')        // 替换为实际字符
    //                      .withEscapeChar('\\')      // 替换为实际字符
    //                      .build()) {
    //
    //             // 写入表头（仅在文件为空或非追加模式下写入）
    //             if (!append || Files.size(path) == 0) {
    //                 csvWriter.writeNext(headers.toArray(new String[0]));
    //             }
    //
    //             // 4. 倒序遍历 dataList
    //             for (int i = dataList.size() - 1; i >= 0; i--) {
    //                 Map<String, Object> data = dataList.get(i);
    //                 String[] row = new String[headers.size()];
    //                 for (int j = 0; j < headers.size(); j++) {
    //                     Object value = data.get(headers.get(j));
    //                     row[j] = value != null ? value.toString() : "";
    //                 }
    //                 csvWriter.writeNext(row);
    //             }
    //
    //         }
    //     } catch (IOException e) {
    //         e.printStackTrace();
    //     }
    // }

    /**
     * 分页读取 CSV 文件内容
     *
     * @param fileBase64Str 文件Base64编码
     * @param page       当前页码（从1开始）
     * @param pageSize   每页行数
     * @param hasHeader  是否有表头（true=第一行是标题）
     * @return 当前页的数据（List<Map<String, Object>>），key是列名，value是单元格值
     */
    public static List<Map<String, Object>> readCsvWithPagination(String fileBase64Str,
                                                                  int page,
                                                                  int pageSize,
                                                                  boolean hasHeader) {
        List<Map<String, Object>> currentPageData = new ArrayList<>();

        if (page < 1 || pageSize < 1) {
            throw new IllegalArgumentException("页码和每页行数必须大于等于1");
        }

        try {

            // 如果是Base64编码，需要解码
            byte[] decodedBytes = Base64.getDecoder().decode(fileBase64Str);
            String csvContent = new String(decodedBytes, StandardCharsets.UTF_8);

            // 使用CSVReader读取内容
            try (CSVReader reader = new CSVReader(new StringReader(csvContent))) {
                List<String[]> allRows = reader.readAll();
                int totalRows = allRows.size();

                // 计算起始行号（跳过表头）
                int startRow = hasHeader ? 1 : 0;
                int endRow = Math.min(startRow + (page - 1) * pageSize + pageSize, totalRows);

                // 获取当前页的行数据
                for (int i = startRow + (page - 1) * pageSize; i < endRow; i++) {
                    String[] row = allRows.get(i);
                    Map<String, Object> rowMap = new HashMap<>();

                    // 如果有表头，则用表头字段作为key，否则用"col_0", "col_1"...
                    if (hasHeader && !allRows.isEmpty()) {
                        String[] header = allRows.get(0);
                        for (int j = 0; j < row.length; j++) {
                            if (j < header.length) {
                                rowMap.put(header[j], j < row.length ? row[j] : "");
                            } else {
                                rowMap.put("col_" + j, j < row.length ? row[j] : "");
                            }
                        }
                    } else {
                        for (int j = 0; j < row.length; j++) {
                            rowMap.put("col_" + j, row[j]);
                        }
                    }

                    currentPageData.add(rowMap);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return currentPageData;
    }
    // public static List<Map<String, Object>> readCsvWithPagination(String filePath, int page, int pageSize, boolean hasHeader) {
    //     List<Map<String, Object>> currentPageData = new ArrayList<>();
    //     Path path = Paths.get(filePath);
    //
    //     if (page < 1 || pageSize < 1) {
    //         throw new IllegalArgumentException("页码和每页行数必须大于等于 1");
    //     }
    //
    //     try (CSVReader reader = new CSVReader(new FileReader(path.toFile()))) {
    //         List<String[]> allRows = reader.readAll();
    //         int totalRows = allRows.size();
    //
    //         // 计算起始行号（跳过表头）
    //         int startRow = hasHeader ? 1 : 0;
    //         int endRow = Math.min(startRow + (page - 1) * pageSize + pageSize, totalRows);
    //
    //         // 获取当前页的行数据
    //         for (int i = startRow + (page - 1) * pageSize; i < endRow; i++) {
    //             String[] row = allRows.get(i);
    //             Map<String, Object> rowMap = new HashMap<>();
    //
    //             // 如果有表头，则用表头字段作为 key，否则用 "col_0", "col_1" ...
    //             if (hasHeader && !allRows.isEmpty()) {
    //                 String[] header = allRows.get(0);
    //                 for (int j = 0; j < row.length; j++) {
    //                     if (j < header.length) {
    //                         rowMap.put(header[j], j < row.length ? row[j] : "");
    //                     } else {
    //                         rowMap.put("col_" + j, j < row.length ? row[j] : "");
    //                     }
    //                 }
    //             } else {
    //                 for (int j = 0; j < row.length; j++) {
    //                     rowMap.put("col_" + j, row[j]);
    //                 }
    //             }
    //
    //             currentPageData.add(rowMap);
    //         }
    //
    //     } catch (Exception e) {
    //         e.printStackTrace();
    //     }
    //
    //     return currentPageData;
    // }

    /**
     * 获取 CSV 文件中除第一行标题外的总行数
     *
     * @param base64Content CSV 文件的 Base64 编码内容
     * @return 除标题行外的数据行数（-1 表示出错，0 表示文件为空）
     */
    public static int getRowCountWithoutHeader(String base64Content) {
        if (base64Content == null || base64Content.isEmpty()) {
            System.err.println("Base64 内容为空");
            return -1;
        }

        try {
            // 解码 Base64 内容
            byte[] decodedBytes = Base64.getDecoder().decode(base64Content);
            String csvContent = new String(decodedBytes, StandardCharsets.UTF_8);

            try (CSVReader reader = new CSVReader(new StringReader(csvContent))) {
                List<String[]> allRows = reader.readAll();

                if (allRows.isEmpty()) {
                    System.out.println("CSV 内容为空");
                    return 0;
                }

                // 假设第一行是标题行，返回剩余行数
                return allRows.size() - 1;
            }
        } catch (IllegalArgumentException e) {
            System.err.println("Base64 解码失败");
            e.printStackTrace();
            return -1;
        } catch (Exception e) {
            System.err.println("解析 CSV 内容失败");
            e.printStackTrace();
            return -1;
        }
    }
    // public static int getRowCountWithoutHeader(String filePath) {
    //     Path path = Paths.get(filePath);
    //
    //     if (!Files.exists(path)) {
    //         System.err.println("文件不存在：" + filePath);
    //         return -1;
    //     }
    //
    //     try (CSVReader reader = new CSVReader(new FileReader(path.toFile()))) {
    //         List<String[]> allRows = reader.readAll();
    //
    //         // 如果文件有标题行，则减去一行
    //         if (!allRows.isEmpty()) {
    //             return allRows.size() - 1; // 减去标题行
    //         } else {
    //             System.out.println("文件为空");
    //             return 0;
    //         }
    //     } catch (Exception e) {
    //         System.err.println("读取文件失败：" + filePath);
    //         e.printStackTrace();
    //         return -1;
    //     }
    // }

    /**
     * 如果文件存在，则删除该文件
     *
     * @param filePath 文件路径
     * @return 如果文件被成功删除或不存在，返回 true；否则返回 false
     */
    public static boolean deleteFileIfExist(String filePath) {
        Path path = Paths.get(filePath);

        try {
            if (Files.exists(path)) {
                Files.delete(path); // 删除文件
                System.out.println("文件已删除：" + filePath);
                return true;
            } else {
                System.out.println("文件不存在，无需删除：" + filePath);
                return true;
            }
        } catch (IOException e) {
            System.err.println("删除文件失败：" + filePath);
            e.printStackTrace();
            return false;
        }
    }
}
