package com.gzzm.lobster.parse;

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * MarkdownBuilder —— 带偏移记录的 markdown 拼接器 /
 * StringBuilder wrapper that tracks character offsets and pairs them with outline sections.
 *
 * <p>典型用法：
 * <pre>
 *   MarkdownBuilder mb = new MarkdownBuilder();
 *   mb.appendFrontMatter(meta);
 *   OutlineSection sec = mb.openSection("s1", 1, "背景");
 *   mb.appendLine("正文...");
 *   mb.closeSection(sec);
 *   String md = mb.toMarkdown();
 * </pre>
 *
 * <p>偏移单位是 Java {@code String#length()}（UTF-16 code units），与
 * {@link String#substring(int, int)} 严格对齐——LLM 后续用 offset 读就稳定.
 */
public final class MarkdownBuilder {

    private final StringBuilder sb = new StringBuilder();
    private final Deque<OutlineSection> stack = new ArrayDeque<>();

    public int cursor() { return sb.length(); }

    public MarkdownBuilder append(String s) {
        if (s != null && !s.isEmpty()) sb.append(s);
        return this;
    }

    public MarkdownBuilder appendLine(String s) {
        if (s != null) sb.append(s);
        sb.append('\n');
        return this;
    }

    public MarkdownBuilder appendBlankLine() {
        int n = sb.length();
        if (n == 0 || sb.charAt(n - 1) != '\n') sb.append('\n');
        sb.append('\n');
        return this;
    }

    /** 开启一节，返回 section 对象（已填 startChar）；调用方后续手动 {@link #closeSection}. */
    public OutlineSection openSection(String id, Integer level, String title) {
        OutlineSection sec = new OutlineSection(id, level, title, sb.length());
        stack.push(sec);
        return sec;
    }

    /** 关闭一节——填 endChar. */
    public void closeSection(OutlineSection sec) {
        if (sec == null) return;
        sec.setEndChar(sb.length());
        stack.remove(sec);
    }

    /** 关闭栈顶所有未关闭的 section，兜底用. */
    public void closeAllOpen() {
        while (!stack.isEmpty()) {
            OutlineSection sec = stack.pop();
            sec.setEndChar(sb.length());
        }
    }

    public String toMarkdown() { return sb.toString(); }

    public int length() { return sb.length(); }
}
