package com.hotent.util.wordexcelutils;/**
 * @program: cbjs-mvue-master
 * @description:
 * @author: zhao yue yue
 * @create: 2025-11-26 16:47
 */

import org.apache.poi.xwpf.usermodel.*;

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

/**
 *@author: zhao yue yue
 *@create: 2025-11-26 16:47
 */
public class SmartTemplateWriter {

    /**
     * 智能写入：保证数据写入 + 保持样式
     */
    public static boolean writeToTemplate(XWPFDocument templateDoc,
                                          Map<String, String> dataMap) {
        if (templateDoc == null || dataMap == null) return false;

        boolean allSuccess = true;

        // 遍历所有占位符
        for (Map.Entry<String, String> entry : dataMap.entrySet()) {
            String placeholder = entry.getKey(); // 如 ${name}
            String value = entry.getValue();

            boolean success = replacePlaceholderSmart(templateDoc, placeholder, value);
            if (!success) {
                System.err.println("警告: 占位符 " + placeholder + " 替换失败");
                allSuccess = false;
            }
        }

        return allSuccess;
    }

    /**
     * 智能替换占位符
     */
    private static boolean replacePlaceholderSmart(XWPFDocument doc, String placeholder, String value) {
        boolean found = false;

        // 1. 首先在段落中查找
        for (XWPFParagraph paragraph : doc.getParagraphs()) {
            if (replaceInParagraphSmart(paragraph, placeholder, value)) {
                found = true;
            }
        }

        // 2. 在表格中查找
        for (XWPFTable table : doc.getTables()) {
            for (XWPFTableRow row : table.getRows()) {
                for (XWPFTableCell cell : row.getTableCells()) {
                    for (XWPFParagraph paragraph : cell.getParagraphs()) {
                        if (replaceInParagraphSmart(paragraph, placeholder, value)) {
                            found = true;
                        }
                    }
                }
            }
        }

        // 3. 如果还没找到，使用强制写入（最后手段）
        if (!found) {
            found = forceWriteToFirstAvailable(doc, placeholder, value);
        }

        return found;
    }

    /**
     * 在段落中智能替换
     */
    private static boolean replaceInParagraphSmart(XWPFParagraph paragraph, String placeholder, String value) {
        List<XWPFRun> runs = paragraph.getRuns();
        boolean foundInThisParagraph = false;

        // 方法1：在单个Run中直接替换（最佳情况）
        for (XWPFRun run : runs) {
            String text = run.getText(0);
            if (text != null && text.contains(placeholder)) {
                replaceInSingleRun(run, placeholder, value);
                foundInThisParagraph = true;
                break; // 找到一个就退出，避免重复替换
            }
        }

        // 方法2：跨Run替换
        if (!foundInThisParagraph) {
            String fullText = paragraph.getText();
            if (fullText != null && fullText.contains(placeholder)) {
                foundInThisParagraph = replaceAcrossRuns(paragraph, placeholder, value);
            }
        }

        return foundInThisParagraph;
    }

    /**
     * 在单个Run中替换（保持格式）
     */
    private static void replaceInSingleRun(XWPFRun run, String placeholder, String value) {
        try {
            String text = run.getText(0);
            if (text != null && text.contains(placeholder)) {
                // 保存原始格式
                RunStyle originalStyle = saveRunStyle(run);

                // 执行替换
                String newText = text.replace(placeholder, value);
                run.setText(newText, 0);

                // 恢复格式
                restoreRunStyle(run, originalStyle);

                System.out.println("成功在单个Run中替换: " + placeholder);
            }
        } catch (Exception e) {
            System.err.println("单个Run替换失败: " + e.getMessage());
        }
    }

    /**
     * 跨Run替换（复杂但必要）
     */
    private static boolean replaceAcrossRuns(XWPFParagraph paragraph, String placeholder, String value) {
        try {
            List<XWPFRun> runs = paragraph.getRuns();
            if (runs.isEmpty()) return false;

            // 构建完整文本并记录Run信息
            StringBuilder fullText = new StringBuilder();
            List<RunInfo> runInfos = new ArrayList<>();

            int currentPos = 0;
            for (XWPFRun run : runs) {
                String text = run.getText(0);
                if (text == null) text = "";

                RunInfo info = new RunInfo();
                info.run = run;
                info.text = text;
                info.startPos = currentPos;
                info.endPos = currentPos + text.length();
                runInfos.add(info);

                fullText.append(text);
                currentPos += text.length();
            }

            String completeText = fullText.toString();
            int startIndex = completeText.indexOf(placeholder);

            if (startIndex == -1) return false;

            int endIndex = startIndex + placeholder.length();

            // 找到受影响的Run
            List<RunInfo> affectedRuns = new ArrayList<>();
            for (RunInfo info : runInfos) {
                if (info.endPos > startIndex && info.startPos < endIndex) {
                    affectedRuns.add(info);
                }
            }

            if (affectedRuns.isEmpty()) return false;

            // 执行跨Run替换
            return executeCrossRunReplacement(paragraph, runInfos, affectedRuns,
                    startIndex, endIndex, placeholder, value);

        } catch (Exception e) {
            System.err.println("跨Run替换失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 执行跨Run替换
     */
    private static boolean executeCrossRunReplacement(XWPFParagraph paragraph,
                                                      List<RunInfo> allRunInfos,
                                                      List<RunInfo> affectedRuns,
                                                      int startIndex, int endIndex,
                                                      String placeholder, String value) {
        try {
            // 保存第一个受影响Run的格式作为基准
            XWPFRun baseRun = affectedRuns.get(0).run;
            RunStyle baseStyle = saveRunStyle(baseRun);

            // 构建新文本
            String fullText = getParagraphFullText(paragraph);
            String newFullText = fullText.replace(placeholder, value);

            // 清除段落内容但保留段落属性
            clearParagraphContent(paragraph);

            // 重新创建Run并设置文本
            XWPFRun newRun = paragraph.createRun();
            newRun.setText(newFullText);

            // 应用基准格式
            restoreRunStyle(newRun, baseStyle);

            System.out.println("跨Run替换成功: " + placeholder);
            return true;

        } catch (Exception e) {
            System.err.println("执行跨Run替换失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 强制写入：最后手段
     */
    private static boolean forceWriteToFirstAvailable(XWPFDocument doc, String placeholder, String value) {
        System.out.println("使用强制写入: " + placeholder);

        // 找到第一个可用的段落
        for (XWPFParagraph paragraph : doc.getParagraphs()) {
            if (isParagraphWritable(paragraph)) {
                return forceWriteToParagraph(paragraph, placeholder, value);
            }
        }

        // 如果段落都不可写，创建新段落
        return createNewParagraphWithData(doc, value);
    }

    /**
     * 强制写入到段落
     */
    private static boolean forceWriteToParagraph(XWPFParagraph paragraph, String placeholder, String value) {
        try {
            // 保存第一个Run的格式
            RunStyle style = null;
            if (!paragraph.getRuns().isEmpty()) {
                style = saveRunStyle(paragraph.getRuns().get(0));
            }

            // 清除内容
            clearParagraphContent(paragraph);

            // 写入新内容
            XWPFRun newRun = paragraph.createRun();
            newRun.setText(value);

            // 恢复格式
            if (style != null) {
                restoreRunStyle(newRun, style);
            }

            return true;

        } catch (Exception e) {
            System.err.println("强制写入失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 创建新段落并写入数据
     */
    private static boolean createNewParagraphWithData(XWPFDocument doc, String value) {
        try {
            XWPFParagraph newParagraph = doc.createParagraph();
            XWPFRun newRun = newParagraph.createRun();
            newRun.setText(value);
            return true;
        } catch (Exception e) {
            System.err.println("创建新段落失败: " + e.getMessage());
            return false;
        }
    }

    /**
     * 工具方法
     */
    private static String getParagraphFullText(XWPFParagraph paragraph) {
        StringBuilder sb = new StringBuilder();
        for (XWPFRun run : paragraph.getRuns()) {
            String text = run.getText(0);
            if (text != null) {
                sb.append(text);
            }
        }
        return sb.toString();
    }

    private static void clearParagraphContent(XWPFParagraph paragraph) {
        for (XWPFRun run : paragraph.getRuns()) {
            run.setText("", 0);
        }
    }

    private static boolean isParagraphWritable(XWPFParagraph paragraph) {
        return paragraph != null;
    }

    // 保存和恢复Run样式的方法（使用之前的安全版本）
    private static RunStyle saveRunStyle(XWPFRun run) {
        RunStyle style = new RunStyle();
        if (run == null) return style;

        style.bold = run.isBold();
        style.italic = run.isItalic();
        style.fontFamily = run.getFontFamily();
        style.fontSize = run.getFontSize();
        style.color = run.getColor();
        style.underline = run.getUnderline();
        return style;
    }

    private static void restoreRunStyle(XWPFRun run, RunStyle style) {
        if (run == null || style == null) return;

        run.setBold(style.bold);
        run.setItalic(style.italic);
        if (style.fontFamily != null) run.setFontFamily(style.fontFamily);
        if (style.fontSize != null) run.setFontSize(style.fontSize);
        if (style.color != null) run.setColor(style.color);
        if (style.underline != null) run.setUnderline(style.underline);
    }

    /**
     * 辅助类
     */
    static class RunStyle {
        boolean bold;
        boolean italic;
        String fontFamily;
        Integer fontSize;
        String color;
        UnderlinePatterns underline;
    }

    static class RunInfo {
        XWPFRun run;
        String text;
        int startPos;
        int endPos;
    }



}
