package com.njcn.harmonic.utils;

import cn.hutool.core.collection.CollUtil;
import com.njcn.harmonic.pojo.dto.report.TableMergeRule;
import com.njcn.oss.constant.OssPath;
import com.njcn.oss.utils.FileStorageUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.util.*;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
@RequiredArgsConstructor
public class WordUtil2 {


    private final FileStorageUtil fileStorageUtil;


    // 保留原有getWord方法（兼容旧调用，无合并规则时直接执行）
    public void getWord(String path, Map<String, Object> params, String fileName,
                        List<List<String[]>> tableList, HttpServletResponse response) throws Exception {
        this.getWord(path, params, fileName, tableList, null, response);
    }

    public void getWord(String path, Map<String, Object> params, String fileName,List<List<String[]>> tableList,List<TableMergeRule> mergeRules, HttpServletResponse response)
            throws Exception {
        path = ClearPathUtil.cleanString(path);
        InputStream inStream = null;
        CustomXWPFDocument doc = null;
        //读取报告模板
        try {
            inStream =  new ClassPathResource(path).getInputStream();;
            doc = new CustomXWPFDocument(inStream);
            // 替换表格里面的变量
            if(CollUtil.isNotEmpty(tableList)){
                this.replaceInTable(doc, params,tableList);
            }else{
                this.replaceInTable(doc, params);
            }
            // 替换文本里面的变量
            this.replaceInPara(doc, params);

            //新增：执行动态合并规则
            if(CollUtil.isNotEmpty(mergeRules)) {
                this.dynamicMergeTables(doc, mergeRules);
            }

        } catch (IOException e) {
            e.printStackTrace();
            getError("获取报告模板异常，原因为：" + e);
        } finally {
            if (null != inStream) {
                inStream.close();
            }
        }

        try {
            ServletOutputStream outputStream = response.getOutputStream();
            response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
            response.setContentType("application/octet-stream;charset=UTF-8");
            doc.write(outputStream);
            outputStream.close();
        } catch (Exception e) {
            getError("输出稳态报告异常，原因为：" + e);
        } finally {
            if (doc != null) {
                doc.close();
            }
        }
    }

    public InputStream getReportFile(String path, Map<String, Object> params,  List<List<String[]>> tableList)
            throws Exception {
        path = ClearPathUtil.cleanString(path);
        InputStream inStream = null,in = null;
        CustomXWPFDocument doc = null;
        //读取报告模板
        try {
            inStream =  new ClassPathResource(path).getInputStream();
            doc = new CustomXWPFDocument(inStream);
            this.replaceInTable(doc,params,tableList);
            this.replaceInPara(doc, params);
        } catch (IOException e) {
            getError("获取报告模板异常，原因为：" + e);
        } finally {
            if (null != inStream) {
                inStream.close();
            }
        }
        try {
            //临时缓冲区
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            doc.write(out);
            byte[] docByteAry = out.toByteArray();
            in = new ByteArrayInputStream(docByteAry);
            out.close();
        } catch (Exception e) {
            getError("输出稳态报告异常，原因为：" + e);
        } finally {
            if (doc != null) {
                doc.close();
            }
        }
        return in;
    }



    public String getReportFileUrl(String path,String fileName,List<List<String[]>> tableList, Map<String, Object> params)
            throws Exception {
        path = ClearPathUtil.cleanString(path);
        InputStream inStream = null,in = null;
        CustomXWPFDocument doc = null;
        //读取报告模板
        try {
            inStream =  new ClassPathResource(path).getInputStream();;
            doc = new CustomXWPFDocument(inStream);
            if(CollUtil.isNotEmpty(tableList)){
                this.replaceInTable(doc, params,tableList);
            }else{
                this.replaceInTable(doc, params);
            }
            this.replaceInPara(doc, params); // 替换文本里面的变量
            //临时缓冲区
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            doc.write(out);
            byte[] docByteAry = out.toByteArray();
            in = new ByteArrayInputStream(docByteAry);
            out.close();
        } catch (IOException e) {
            getError("获取报告模板异常，原因为：" + e);
        } finally {
            if (null != inStream) {
                inStream.close();
            }
            if (doc != null) {
                doc.close();
            }
        }
        //上传文件服务器
        return fileStorageUtil.uploadStreamSpecifyName(in, OssPath.ONLINE_REPORT, fileName);
    }


    private static void getError(String e) {
        log.error(e);
    }
    /**
     * 替换段落里面的变量
     *
     * @param doc    要替换的文档
     * @param params 参数
     */
    private void replaceInPara(CustomXWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFParagraph> iterator = doc.getParagraphsIterator();
        List<XWPFParagraph> paragraphList = new ArrayList<>();
        XWPFParagraph para;
        while (iterator.hasNext()) {
            para = iterator.next();
            paragraphList.add(para);
        }
        processParagraphs(paragraphList, params, doc);
    }

    private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params) {
        Iterator<XWPFTable> it = doc.getTablesIterator();
        while (it.hasNext()) {
            XWPFTable table = it.next();
            List<XWPFTableRow> rows = table.getRows();
            for (XWPFTableRow row : rows) {
                List<XWPFTableCell> cells = row.getTableCells();
                for (XWPFTableCell cell : cells) {
                    List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
                    processParagraphs(paragraphListTable, params, doc);
                }
            }
        }
    }

    public static void processParagraphs(List<XWPFParagraph> paragraphList, Map<String, Object> param,
                                         CustomXWPFDocument doc) {
        if (paragraphList != null && paragraphList.size() > 0) {
            for (XWPFParagraph paragraph : paragraphList) {
                List<XWPFRun> runs = paragraph.getRuns();
                if (runs.size() > 0) {
                    for (XWPFRun run : runs) {
                        String bflag = "";
                        String text = run.getText(0);
                        if (text != null) {
                            boolean isSetText = false;
                            for (Entry<String, Object> entry : param.entrySet()) {
                                String key = entry.getKey();
                                if (text.indexOf(key) != -1) {
                                    isSetText = true;
                                    Object value = entry.getValue();
                                    if (value instanceof String) {// 文本替换
                                        text = text.replace(key, value.toString());
                                    } else if (value instanceof Map) {// 图片替换
                                        text = text.replace(key, "");
                                        Map pic = (Map) value;
                                        int width = Integer.parseInt(pic.get("width").toString());
                                        int height = Integer.parseInt(pic.get("height").toString());
                                        int picType = getPictureType(pic.get("type").toString());
                                        byte[] byteArray = (byte[]) pic.get("content");
                                        ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                                        try {
                                            String s = doc.addPictureData(byteInputStream, picType);

                                            doc.createPicture(doc.getAllPictures().size() - 1, width, height,
                                                    s,paragraph);
                                            bflag = "break";
                                        } catch (Exception e) {
                                            getError("文本替换发生异常，异常是" + e.getMessage());
                                        }
                                    }
                                }
                            }
                            if (isSetText) {
                                run.setText(text, 0);
                            }
                        }
                        if (bflag == "break") {
                            break;
                        }
                    }
                }
            }
        }
    }



/*    public static void processParagraphs(List<XWPFParagraph> paragraphList, Map<String, Object> param,
                                         CustomXWPFDocument doc) {
        if (paragraphList != null && !paragraphList.isEmpty()) {
            for (XWPFParagraph paragraph : paragraphList) {
                // 先获取整个段落的完整文本
                String fullText = paragraph.getText();

                // 检查是否包含变量
                boolean hasVariable = false;
                for (String key : param.keySet()) {
                    if (fullText.contains(key)) {
                        hasVariable = true;
                        break;
                    }
                }

                if (hasVariable) {
                    // 进行变量替换
                    String replacedText = fullText;
                    boolean hasImage = false;

                    for (Entry<String, Object> entry : param.entrySet()) {
                        String key = entry.getKey();
                        if (replacedText.contains(key)) {
                            Object value = entry.getValue();
                            if (value instanceof String) {
                                replacedText = replacedText.replace(key, value.toString());
                            } else if (value instanceof Map) {
                                // 处理图片：需要特殊处理
                                hasImage = true;
                                break;
                            }
                        }
                    }

                    if (!hasImage) {
                        // 如果没有图片，直接替换整个段落文本
                        // 清空原有 Runs
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (int i = runs.size() - 1; i >= 0; i--) {
                            paragraph.removeRun(i);
                        }

                        // 创建新的 Run
                        XWPFRun newRun = paragraph.createRun();
                        newRun.setText(replacedText);
                    } else {
                        // 有图片的情况：逐个 Run 处理（保留原逻辑）
                        List<XWPFRun> runs = paragraph.getRuns();
                        for (XWPFRun run : runs) {
                            String text = run.getText(0);
                            if (text != null) {
                                for (Entry<String, Object> entry : param.entrySet()) {
                                    String key = entry.getKey();
                                    if (text.contains(key)) {
                                        // ... 原图片处理逻辑
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }*/

    /**
     * 替换段落里面的变量
     *
     * @param para   要替换的段落
     * @param params 参数
     */
    private void replaceInPara(XWPFParagraph para, Map<String, Object> params, CustomXWPFDocument doc) {
        List<XWPFRun> runs;
        Matcher matcher;
        // if (this.matcher(para.getParagraphText()).find()) {
        runs = para.getRuns();
        int start = -1;
        int end = -1;
        String str = "";
        for (int i = 0; i < runs.size(); i++) {
            XWPFRun run = runs.get(i);
            String runText = run.toString();
            if ('$' == runText.charAt(0) && '{' == runText.charAt(1)) {
                start = i;
            }
            if ((start != -1)) {
                str += runText;
            }
            if ('}' == runText.charAt(runText.length() - 1)) {
                if (start != -1) {
                    end = i;
                    break;
                }
            }
        }

        for (int i = start; i <= end; i++) {
            para.removeRun(i);
            i--;
            end--;
        }

        for (Entry<String, Object> entry : params.entrySet()) {
            String key = entry.getKey();
            if (str.indexOf(key) != -1) {
                Object value = entry.getValue();
                if (value instanceof String) {
                    str = str.replace(key, value.toString());
                    para.createRun().setText(str, 0);
                    break;
                } else if (value instanceof Map) {
                    str = str.replace(key, "");
                    Map pic = (Map) value;
                    int width = Integer.parseInt(pic.get("width").toString());
                    int height = Integer.parseInt(pic.get("height").toString());
                    int picType = getPictureType(pic.get("type").toString());
                    byte[] byteArray = (byte[]) pic.get("content");
                    ByteArrayInputStream byteInputStream = new ByteArrayInputStream(byteArray);
                    try {
                        // int ind = doc.addPicture(byteInputStream,picType);
                        // doc.createPicture(ind, width , height,para);
                        String s = doc.addPictureData(byteInputStream, picType);
                        doc.createPicture(doc.getAllPictures().size() - 1, width, height,s, para);
                        para.createRun().setText(str, 0);
                        break;
                    } catch (Exception e) {
                        getError("文件替换发生异常，异常是" + e.getMessage());
                    }
                }
            }
        }
        // }
    }


    /**
     * 替换表格里面的变量
     *
     * @param doc    要替换的文档
     * @param tableList    存放数据顺序要与表格一致
     * @param params 参数
     */
    private void replaceInTable(CustomXWPFDocument doc, Map<String, Object> params, List<List<String[]>> tableList) {
        Iterator<XWPFTable> iterator = doc.getTablesIterator();
        XWPFTable table;
        List<XWPFTableRow> rows;
        List<XWPFTableCell> cells;
        List<XWPFParagraph> paras;
        Integer num=0;
        while (iterator.hasNext()) {
            table = iterator.next();
            if (table.getRows().size() > 1) {
                // 判断表格是需要替换还是需要插入，判断逻辑有$为替换，表格无$为插入
                if (this.matcher(table.getText()).find()||this.matcherS(table.getText()).find()) {
                    rows = table.getRows();
                    for (XWPFTableRow row : rows) {
                        cells = row.getTableCells();
                        for (XWPFTableCell cell : cells) {
                            List<XWPFParagraph> paragraphListTable = cell.getParagraphs();
                            processParagraphs(paragraphListTable, params, doc);
                        }
                    }
                }else {
                    if (CollUtil.isNotEmpty(tableList.get(num))){
                        insertTable(table, tableList.get(num)); // 插入数据

                    }
                    num++;
                }
            }
        }
    }

    /**
     * 为表格插入数据，行数不够添加新行
     *
     * @param table     需要插入数据的表格
     * @param tableList 插入数据集合
     */
    private static void insertTable(XWPFTable table, List<String[]> tableList) {
        //删除占位符行数
        table.removeRow(1);
        if (CollUtil.isNotEmpty(tableList)) {
            // 创建行,根据需要插入的数据添加新行，不处理表头
            for (int i = 0; i < tableList.size(); i++) {
                XWPFTableRow row = table.createRow();
                List<XWPFTableCell> cells = row.getTableCells();
                String[] rowData = tableList.get(i);

                for (int j = 0; j < cells.size(); j++) {
                    String s = tableList.get(i)[j];
                    XWPFTableCell cell = cells.get(j);
                    cell.removeParagraph(0);
                    XWPFParagraph paragraph = cell.addParagraph();
                    paragraph.setAlignment(ParagraphAlignment.CENTER);
                    // 在段落中添加文本
                    XWPFRun run = paragraph.createRun();
                    // 处理普通数据
                    if (j < rowData.length && !rowData[j].startsWith("merge:")) {
                        run.setText(rowData[j] == null ? "" : rowData[j]);
                    }
                    // 处理合并配置
                    else if (j < rowData.length && rowData[j].startsWith("merge:")) {
                        String[] mergeParams = rowData[j].replace("merge:", "").split(",");
                        int rowMerge = Integer.parseInt(mergeParams[0]);
                        int colMerge = Integer.parseInt(mergeParams[1]);
                        // 调用合并方法
                        setCellMerge(cell, rowMerge, colMerge);
                        // 清空跨列后续单元格
                        for (int k = j + 1; k < j + colMerge && k < cells.size(); k++) {
                            cells.get(k).removeParagraph(0);
                        }
                    }
                }
            }
        }
    }



    /**
     * 正则匹配字符串
     *
     * @param str
     * @return
     */
    private Matcher matcher(String str) {
        Pattern pattern = Pattern.compile("\\$\\{(.+?)\\}", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }
    private Matcher matcherS(String str) {
        Pattern pattern = Pattern.compile("\\$(.*?)\\$");
        Matcher matcher = pattern.matcher(str);
        return matcher;
    }

    /**
     * 根据图片类型，取得对应的图片类型代码
     *
     * @param picType
     * @return int
     */
    private static int getPictureType(String picType) {
        int res = CustomXWPFDocument.PICTURE_TYPE_PICT;
        if (picType != null) {
            if (picType.equalsIgnoreCase("image/png")) {
                res = CustomXWPFDocument.PICTURE_TYPE_PNG;
            } else if (picType.equalsIgnoreCase("image/dib")) {
                res = CustomXWPFDocument.PICTURE_TYPE_DIB;
            } else if (picType.equalsIgnoreCase("image/emf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_EMF;
            } else if (picType.equalsIgnoreCase("image/jpg") || picType.equalsIgnoreCase("image/jpeg")) {
                res = CustomXWPFDocument.PICTURE_TYPE_JPEG;
            } else if (picType.equalsIgnoreCase("image/wmf")) {
                res = CustomXWPFDocument.PICTURE_TYPE_WMF;
            }
        }
        return res;
    }


    /**
     * 适配 POI 4.1.1 的单元格合并核心方法
     * @param cell 要合并的单元格
     * @param rowMerge 纵向合并行数（1=不合并，>1=合并行数）
     * @param colMerge 横向合并列数（1=不合并，>1=合并列数）
     */
    private static void setCellMerge(XWPFTableCell cell, int rowMerge, int colMerge) {
        CTTc ctTc = cell.getCTTc();
        CTTcPr tcPr = ctTc.getTcPr() == null ? ctTc.addNewTcPr() : ctTc.getTcPr();
        tcPr.addNewVAlign().setVal(STVerticalJc.CENTER);

        // 1. 横向合并（跨列）- POI 4.1.1 写法
        if (colMerge > 1) {
            // 直接创建 gridSpan 并设置值
            tcPr.addNewGridSpan().setVal(BigInteger.valueOf(colMerge));
        }

        // 2. 纵向合并（跨行）- POI 4.1.1 写法
        if (rowMerge > 1) {
            // 起始单元格标记为 RESTART
            tcPr.addNewVMerge().setVal(STMerge.RESTART);
        }
    }

    /**
     * 适配 POI 4.1.1 的表格单元格合并工具方法
     * @param table Word表格对象
     * @param rowIndex 行索引（从0开始）
     * @param colIndex 列索引（从0开始）
     * @param mergeRows 跨行数（如2表示合并当前行和下一行）
     * @param mergeCols 跨列数（如2表示合并当前列和下一列）
     */
    public static void mergeTableCell(XWPFTable table, int rowIndex, int colIndex, int mergeRows, int mergeCols) {
        // 参数校验
        if (table == null || rowIndex < 0 || colIndex < 0 || mergeRows < 1 || mergeCols < 1) {
            log.error("表格合并参数无效：行{} 列{} 跨行{} 跨列{}", rowIndex, colIndex, mergeRows, mergeCols);
            return;
        }

        // 获取目标行和单元格
        XWPFTableRow targetRow = table.getRow(rowIndex);
        if (targetRow == null) {
            log.error("指定行不存在：{}", rowIndex);
            return;
        }
        XWPFTableCell targetCell = targetRow.getCell(colIndex);
        if (targetCell == null) {
            log.error("指定列不存在：{}", colIndex);
            return;
        }

        // 设置起始单元格的合并属性
        setCellMerge(targetCell, mergeRows, mergeCols);

        // 处理跨行合并的后续单元格（标记为 CONTINUE）
        for (int i = rowIndex + 1; i < rowIndex + mergeRows; i++) {
            XWPFTableRow row = table.getRow(i);
            if (row == null) break;

            XWPFTableCell cell = row.getCell(colIndex);
            if (cell == null) continue;

            CTTc ctTc = cell.getCTTc();
            CTTcPr tcPr = ctTc.getTcPr() == null ? ctTc.addNewTcPr() : ctTc.getTcPr();
            // 后续单元格标记为 CONTINUE
            tcPr.addNewVMerge().setVal(STMerge.CONTINUE);
        }

        // 处理跨列合并的后续单元格（清空并标记为 CONTINUE）
        for (int j = colIndex + 1; j < colIndex + mergeCols; j++) {
            XWPFTableCell cell = targetRow.getCell(j);
            if (cell == null) break;

            CTTc ctTc = cell.getCTTc();
            CTTcPr tcPr = ctTc.getTcPr() == null ? ctTc.addNewTcPr() : ctTc.getTcPr();
            tcPr.addNewVMerge().setVal(STMerge.CONTINUE);
            // 清空跨列后续单元格的内容
            cell.removeParagraph(0);
        }
    }


    // ========== 新增：动态合并核心方法 ==========
    /**
     * 动态执行多个表格的合并规则
     * @param doc Word文档对象
     * @param mergeRules 合并规则列表
     */
    public void dynamicMergeTables(CustomXWPFDocument doc, List<TableMergeRule> mergeRules) {
        // 遍历文档中的所有表格，按规则合并
        Iterator<XWPFTable> tableIterator = doc.getTablesIterator();
        int currentTableIndex = 0; // 当前遍历到的表格索引（从0开始）
        while (tableIterator.hasNext()) {
            XWPFTable table = tableIterator.next();
            // 查找当前表格是否有匹配的合并规则
            for (TableMergeRule rule : mergeRules) {
                if (rule.isEnable() && rule.getTableIndex().equals(currentTableIndex)) {
                    // 执行该表格的合并规则
                    mergeTableByRule(table, rule);
                    break; // 一个表格只执行一条规则，避免重复处理
                }
            }
            currentTableIndex++;
        }
    }

    /**
     * 根据单条规则合并指定表格（支持指定起始行）
     * @param table 要合并的表格
     * @param rule 合并规则
     */
    private void mergeTableByRule(XWPFTable table, TableMergeRule rule) {
        if (table == null || rule == null || !rule.isEnable()) {
            return;
        }

        int totalRows = table.getRows().size();
        int mergeCol = rule.getMergeColIndex();
        int startRow = rule.getStartRow(); // 新增：获取指定的起始行
        int mergeRowsPerGroup = rule.getMergeRowsPerGroup();

        // 参数校验：起始行不能大于总行数、行数不足、列索引非法、合并行数小于2
        if (startRow >= totalRows || totalRows < 2 || mergeCol < 0 || mergeRowsPerGroup < 2) {
            log.warn("表格{}合并规则无效：总行数{} 起始行{} 合并列{} 每组行数{}",
                    rule.getTableIndex(), totalRows, startRow, mergeCol, mergeRowsPerGroup);
            return;
        }

        // 核心改动：从startRow开始，每mergeRowsPerGroup行合并一组
        for (int currentStart = startRow; currentStart < totalRows; currentStart += mergeRowsPerGroup) {
            // 最后一组可能不足N行，取实际行数
            int actualMergeRows = Math.min(mergeRowsPerGroup, totalRows - currentStart);
            // 复用原有mergeTableCell方法执行合并
            mergeTableCell(table, currentStart, mergeCol, actualMergeRows, 1);
        }
        log.info("表格{}合并完成：第{}列 从第{}行开始 每{}行合并一次",
                rule.getTableIndex(), mergeCol, startRow, mergeRowsPerGroup);
    }

}
