import remarkFrontmatter from 'remark-frontmatter';
import remarkParse from 'remark-parse';
import { unified } from 'unified';
import YAML from 'yaml';
import { extractPlainText } from './chunker-utils.js';
const MAX_SPLIT_DEPTH = 3;
/**
 * Markdown Heading Chunker.
 *
 * Chunks by ## and ### heading boundaries.
 * Extracts YAML frontmatter as metadata on every chunk.
 */
export class MarkdownChunker {
    strategy = 'markdown-heading';
    onWarn;
    constructor(onWarn) {
        this.onWarn = onWarn;
    }
    chunk(content, filePath, type) {
        const tree = unified().use(remarkParse).use(remarkFrontmatter, ['yaml']).parse(content);
        const metadata = this.extractFrontmatter(tree, filePath);
        const chunks = [];
        const lines = content.split('\n');
        // Heading breadcrumb stack: index = depth-1, value = heading text.
        // When we encounter a heading of depth N, we set that slot and truncate deeper levels.
        const breadcrumbs = [];
        let sectionNodes = [];
        let sectionStartLine = 1;
        const getBreadcrumb = () => breadcrumbs.filter(Boolean).join(' > ');
        const flush = () => {
            if (sectionNodes.length === 0 && breadcrumbs.filter(Boolean).length === 0)
                return;
            const sectionText = sectionNodes
                .map((n) => nodeToSourceText(n, lines))
                .filter(Boolean)
                .join('\n\n');
            if (!sectionText.trim())
                return;
            const label = getBreadcrumb() || filePath;
            const contextPrefix = `File: ${filePath} | Section: ${label}`;
            const endLine = sectionNodes.length > 0
                ? (sectionNodes[sectionNodes.length - 1].position?.end.line ?? sectionStartLine)
                : sectionStartLine;
            chunks.push({
                content: sectionText.trim(),
                contextPrefix,
                filePath,
                type,
                strategy: this.strategy,
                label,
                startLine: sectionStartLine,
                endLine,
                metadata,
            });
        };
        for (const node of tree.children) {
            // Skip YAML frontmatter node
            if (node.type === 'yaml')
                continue;
            if (node.type === 'heading') {
                const h = node;
                // Stop at bibliography sections — prevents citation noise in vector index (#963)
                const headingText = extractPlainText(h.children).toLowerCase();
                if (headingText === 'works cited' || headingText === 'references') {
                    flush();
                    break;
                }
                // Only split on headings up to depth 3
                if (h.depth <= MAX_SPLIT_DEPTH) {
                    flush();
                    const text = extractPlainText(h.children);
                    breadcrumbs[h.depth - 1] = text;
                    breadcrumbs.length = h.depth; // truncate deeper levels
                    sectionNodes = [];
                    sectionStartLine = h.position?.start.line ?? 1;
                    continue;
                }
            }
            sectionNodes.push(node);
        }
        // Flush final section
        flush();
        return chunks;
    }
    extractFrontmatter(tree, filePath) {
        const yamlNode = tree.children.find((n) => n.type === 'yaml');
        if (!yamlNode || !('value' in yamlNode))
            return {};
        try {
            const parsed = YAML.parse(yamlNode.value);
            if (typeof parsed !== 'object' || parsed === null)
                return {};
            const result = {};
            for (const [key, value] of Object.entries(parsed)) {
                result[key] = String(value);
            }
            return result;
        }
        catch (err) {
            const msg = `Failed to parse frontmatter in ${filePath}: ${err instanceof Error ? err.message : String(err)}`;
            if (this.onWarn) {
                this.onWarn(msg);
            }
            else {
                console.warn(`[Totem Warning] ${msg}`);
            }
            return {};
        }
    }
}
function nodeToSourceText(node, lines) {
    if (node.position) {
        const startIdx = node.position.start.line - 1;
        const endIdx = node.position.end.line;
        return lines.slice(startIdx, endIdx).join('\n');
    }
    if ('value' in node && typeof node.value === 'string')
        return node.value;
    return '';
}
//# sourceMappingURL=markdown-chunker.js.map