package com.njcn.gather.report.util;

import com.njcn.gather.tools.report.util.BookmarkUtil;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import org.docx4j.wml.CTBookmark;
import org.docx4j.wml.CTMarkupRange;
import org.docx4j.wml.ObjectFactory;
import org.docx4j.wml.P;
import org.docx4j.wml.R;
import org.docx4j.wml.Text;
import org.junit.Test;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class ItemAnchorSectionUtilTest {

    private final ObjectFactory f = new ObjectFactory();

    private P para(String text) {
        P p = f.createP();
        R r = f.createR();
        Text t = f.createText();
        t.setValue(text);
        r.getContent().add(t);
        p.getContent().add(r);
        return p;
    }

    private void addStart(P p, long id, String name) {
        CTBookmark b = f.createCTBookmark();
        b.setId(BigInteger.valueOf(id));
        b.setName(name);
        p.getContent().add(0, f.createPBookmarkStart(b));
    }

    private void addEnd(P p, long id) {
        CTMarkupRange e = f.createCTMarkupRange();
        e.setId(BigInteger.valueOf(id));
        p.getContent().add(f.createPBookmarkEnd(e));
    }

    private BookmarkUtil.BookmarkInfo addBookmarkPair(P p, long id, String name, MainDocumentPart mdp) {
        CTBookmark b = f.createCTBookmark();
        b.setId(BigInteger.valueOf(id));
        b.setName(name);
        p.getContent().add(0, f.createPBookmarkStart(b));
        addEnd(p, id);

        BookmarkUtil.BookmarkInfo info = new BookmarkUtil.BookmarkInfo();
        info.bookmark = b;
        info.parentParagraph = p;
        info.parentContainer = mdp;
        return info;
    }

    /**
     * 构造文档骨架（模拟 base 第 7 节）：
     * [0] 7.1 标题       {start sect_GRP_FUND}
     * [1] 7.1.1 标题     {start sect_V}
     * [2] item_V 锚点段  {end sect_V}
     * [3] 7.1.2 标题     {start sect_I}
     * [4] item_I 锚点段  {end sect_I, end sect_GRP_FUND}
     * [5] 7.2 标题       {start sect_FREQ}
     * [6] item_FREQ 锚点 {end sect_FREQ}
     */
    private MainDocumentPart buildDoc() throws Exception {
        WordprocessingMLPackage pkg = WordprocessingMLPackage.createPackage();
        MainDocumentPart mdp = pkg.getMainDocumentPart();
        P p71 = para("7.1 基波测试");
        addStart(p71, 100, "sect_GRP_FUND");
        P p711 = para("7.1.1 电压");
        addStart(p711, 101, "sect_V");
        P aV = para("");
        addEnd(aV, 101);
        P p712 = para("7.1.2 电流");
        addStart(p712, 102, "sect_I");
        P aI = para("");
        addEnd(aI, 102);
        addEnd(aI, 100);
        P p72 = para("7.2 频率");
        addStart(p72, 103, "sect_FREQ");
        P aF = para("");
        addEnd(aF, 103);
        mdp.getContent().addAll(Arrays.asList(p71, p711, aV, p712, aI, p72, aF));
        return mdp;
    }

    private MainDocumentPart docWithTexts(String... texts) throws Exception {
        WordprocessingMLPackage pkg = WordprocessingMLPackage.createPackage();
        MainDocumentPart mdp = pkg.getMainDocumentPart();
        for (String t : texts) {
            mdp.getContent().add(para(t));
        }
        return mdp;
    }

    @Test
    public void removeSingleChildSect() throws Exception {
        MainDocumentPart mdp = buildDoc();
        ItemAnchorSectionUtil.removeSectRanges(mdp, new LinkedHashSet<>(Collections.singletonList("V")));
        // 删除 [1][2] 两块，剩 5 块
        assertEquals(5, mdp.getContent().size());
        assertTrue(Docx4jUtilTextHelper.textOf(mdp, 0).startsWith("7.1 "));
        assertTrue(Docx4jUtilTextHelper.textOf(mdp, 1).startsWith("7.1.2"));
    }

    @Test
    public void removeWholeGroupWhenAllChildrenHidden() throws Exception {
        MainDocumentPart mdp = buildDoc();
        // GRP_FUND 子节全集是 V/I/P/ANGLE；文档里只有 V、I，但隐藏集合包含全部四个 → 整组删除
        ItemAnchorSectionUtil.removeSectRanges(mdp, new LinkedHashSet<>(Arrays.asList("V", "I", "P", "ANGLE")));
        // [0]~[4] 全删，剩 7.2 两块
        assertEquals(2, mdp.getContent().size());
        assertTrue(Docx4jUtilTextHelper.textOf(mdp, 0).startsWith("7.2"));
    }

    /** F6：父 sect_GRP_* 书签缺失（如 Word 重存丢书签）时，必须回退按子节各自删除，不能一个都删不掉 */
    @Test
    public void removeGroup_parentRangeMissing_fallsBackToChildren() throws Exception {
        WordprocessingMLPackage pkg = WordprocessingMLPackage.createPackage();
        MainDocumentPart mdp = pkg.getMainDocumentPart();
        P p71 = para("7.1 基波测试"); // 故意不挂 sect_GRP_FUND
        P p711 = para("7.1.1 电压");
        addStart(p711, 101, "sect_V");
        P aV = para("");
        addEnd(aV, 101);
        P p712 = para("7.1.2 电流");
        addStart(p712, 102, "sect_I");
        P aI = para("");
        addEnd(aI, 102);
        P p72 = para("7.2 频率");
        addStart(p72, 103, "sect_FREQ");
        P aF = para("");
        addEnd(aF, 103);
        mdp.getContent().addAll(Arrays.asList(p71, p711, aV, p712, aI, p72, aF));
        ItemAnchorSectionUtil.removeSectRanges(mdp, new LinkedHashSet<>(Arrays.asList("V", "I", "P", "ANGLE")));
        // 父范围定位失败 → 回退删 sect_V/sect_I 各自范围；组标题 7.1（无书签定位不了）与 7.2 保留
        assertEquals(3, mdp.getContent().size());
        assertTrue(Docx4jUtilTextHelper.textOf(mdp, 0).startsWith("7.1 "));
        assertTrue(Docx4jUtilTextHelper.textOf(mdp, 1).startsWith("7.2"));
    }

    @Test
    public void stripBookmarks_removesOnlyOurNamespace() throws Exception {
        MainDocumentPart mdp = buildDoc();
        // 额外放一个第三方书签，应保留
        P other = (P) mdp.getContent().get(0);
        CTBookmark foreign = f.createCTBookmark();
        foreign.setId(BigInteger.valueOf(999));
        foreign.setName("bgbh1");
        other.getContent().add(f.createPBookmarkStart(foreign));
        ItemAnchorSectionUtil.stripItemAnchorBookmarks(mdp);
        // 全文不再有 sect_/item_ 书签，但 bgbh1 保留
        java.util.List<String> names = Docx4jUtilTextHelper.allBookmarkNames(mdp);
        assertEquals(Collections.singletonList("bgbh1"), names);
    }

    @Test
    public void removeEmptyItemAnchorParagraph_removesStandaloneItemAnchorParagraph() throws Exception {
        MainDocumentPart mdp = docWithTexts("7.1.1 电压");
        P anchor = para("");
        BookmarkUtil.BookmarkInfo info = addBookmarkPair(anchor, 9000, "item_V", mdp);
        addEnd(anchor, 9011);
        mdp.getContent().add(anchor);

        assertTrue(ItemAnchorSectionUtil.removeEmptyItemAnchorParagraph(info));

        assertEquals(1, mdp.getContent().size());
        assertEquals("7.1.1 电压", Docx4jUtilTextHelper.textOf(mdp, 0));
    }

    @Test
    public void removeEmptyItemAnchorParagraph_keepsItemAnchorParagraphWithText() throws Exception {
        MainDocumentPart mdp = docWithTexts("7.1.1 电压");
        P anchor = para("保留文本");
        BookmarkUtil.BookmarkInfo info = addBookmarkPair(anchor, 9000, "item_V", mdp);
        mdp.getContent().add(anchor);

        assertTrue(!ItemAnchorSectionUtil.removeEmptyItemAnchorParagraph(info));

        assertEquals(2, mdp.getContent().size());
        assertEquals("保留文本", Docx4jUtilTextHelper.textOf(mdp, 1));
    }

    @Test
    public void removeEmptyItemAnchorParagraph_ignoresNonItemAnchor() throws Exception {
        MainDocumentPart mdp = docWithTexts("7.1.1 电压");
        P anchor = para("");
        BookmarkUtil.BookmarkInfo info = addBookmarkPair(anchor, 1, "data_line", mdp);
        mdp.getContent().add(anchor);

        assertTrue(!ItemAnchorSectionUtil.removeEmptyItemAnchorParagraph(info));

        assertEquals(2, mdp.getContent().size());
    }

    @Test
    public void renumber_afterMiddleRemoval() throws Exception {
        // 模拟 7.1.1 已被删除：7.1.2→7.1.1，7.1.4→…按序补位；7.2 及以后不变
        MainDocumentPart mdp = docWithTexts(
                "7．准确度检查：",
                "7.1 基波测试",
                "7.1.2 电流",
                "7.1.4 相位",
                "7.2 频率",
                "7.6.1 频率变化影响");
        ItemAnchorSectionUtil.renumberChapter7(mdp);
        assertEquals("7．准确度检查：", Docx4jUtilTextHelper.textOf(mdp, 0)); // 全角点不动
        assertEquals("7.1 基波测试", Docx4jUtilTextHelper.textOf(mdp, 1));
        assertEquals("7.1.1 电流", Docx4jUtilTextHelper.textOf(mdp, 2));
        assertEquals("7.1.2 相位", Docx4jUtilTextHelper.textOf(mdp, 3));
        assertEquals("7.2 频率", Docx4jUtilTextHelper.textOf(mdp, 4));
        assertEquals("7.2.1 频率变化影响", Docx4jUtilTextHelper.textOf(mdp, 5));
    }

    @Test
    public void renumber_afterLevel2Removal() throws Exception {
        // 模拟整个 7.1 组已删除：7.2→7.1，7.5.1→7.4.1 之类整体前移
        MainDocumentPart mdp = docWithTexts("7.2 频率", "7.3 闪变", "7.5 谐波", "7.5.1 谐波电压");
        ItemAnchorSectionUtil.renumberChapter7(mdp);
        assertEquals("7.1 频率", Docx4jUtilTextHelper.textOf(mdp, 0));
        assertEquals("7.2 闪变", Docx4jUtilTextHelper.textOf(mdp, 1));
        assertEquals("7.3 谐波", Docx4jUtilTextHelper.textOf(mdp, 2));
        assertEquals("7.3.1 谐波电压", Docx4jUtilTextHelper.textOf(mdp, 3));
    }

    @Test
    public void renumber_prefixSplitAcrossRuns() throws Exception {
        // 编号被拆在两个 run 里："7." + "2 频率"
        WordprocessingMLPackage pkg = WordprocessingMLPackage.createPackage();
        MainDocumentPart mdp = pkg.getMainDocumentPart();
        P p = f.createP();
        R r1 = f.createR();
        Text t1 = f.createText();
        t1.setValue("7.");
        r1.getContent().add(t1);
        R r2 = f.createR();
        Text t2 = f.createText();
        t2.setValue("2 频率");
        r2.getContent().add(t2);
        p.getContent().add(r1);
        p.getContent().add(r2);
        mdp.getContent().add(p);
        ItemAnchorSectionUtil.renumberChapter7(mdp);
        assertEquals("7.1 频率", Docx4jUtilTextHelper.textOf(mdp, 0));
    }
}
