// Copyright (c) 2026 Modelware. All rights reserved.
import MarkdownIt from 'markdown-it';
import { displayLabelFromIri } from '../renderers/renderer.js';
import { extractLeadingFrontMatter, stripLeadingFrontMatter } from './md-frontmatter.js';
import { parseYamlMap } from './md-yaml.js';
export class MarkdownPreviewRuntime {
    constructor(handlers) {
        this.handlers = handlers;
        this.renderer = new MarkdownIt({
            html: true,
            linkify: true,
            typographer: false,
        });
        this.renderer.inline.ruler.before('link', 'wiki-link', wikilinkRule);
    }
    prepare(markdown) {
        const frontMatter = extractLeadingFrontMatter(markdown);
        const stripped = stripLeadingFrontMatter(markdown);
        const codeBlocks = this.parseCodeBlocks(stripped);
        const executableBlocks = this.resolveExecutableBlocks(codeBlocks);
        const renderedHtml = this.renderer.render(stripped);
        return {
            markdown: stripped,
            renderedHtml,
            frontMatter,
            contextOntologyIri: frontMatter?.contextOntologyIri,
            codeBlocks,
            executableBlocks
        };
    }
    parseCodeBlocks(markdown) {
        const tokens = this.renderer.parse(markdown, {});
        const blocks = [];
        for (const token of tokens) {
            if (token.type !== 'fence') {
                continue;
            }
            const language = parseLanguage(token.info);
            const meta = parseMeta(token.info);
            const lineStart = token.map?.[0] ?? 0;
            const lineEnd = token.map?.[1] ?? lineStart;
            const parsed = parseBlockOptions(token.content);
            blocks.push({
                id: buildBlockId(lineStart, lineEnd, language, parsed.content),
                language,
                meta,
                content: parsed.content,
                options: parsed.options,
                lineStart,
                lineEnd,
            });
        }
        return blocks;
    }
    resolveExecutableBlocks(codeBlocks) {
        const executableBlocks = [];
        for (const block of codeBlocks) {
            const handler = this.handlers.match(block);
            if (!handler) {
                continue;
            }
            executableBlocks.push({
                block,
                handlerId: handler.id,
            });
        }
        return executableBlocks;
    }
}
const wikilinkRule = (state, silent) => {
    const { src } = state;
    const start = state.pos;
    if (src.charCodeAt(start) !== 0x5b || src.charCodeAt(start + 1) !== 0x5b) {
        return false;
    }
    const close = src.indexOf(']]', start + 2);
    if (close < 0) {
        return false;
    }
    const iri = src.slice(start + 2, close).trim();
    if (!iri) {
        return false;
    }
    if (!silent) {
        const label = displayLabelFromIri(iri);
        const open = state.push('link_open', 'a', 1);
        open.attrSet('class', 'wikilink');
        open.attrSet('iri', iri);
        open.attrSet('href', '#');
        open.attrSet('title', iri);
        const text = state.push('text', '', 0);
        text.content = label;
        state.push('link_close', 'a', -1);
    }
    state.pos = close + 2;
    return true;
};
function parseBlockOptions(content) {
    const normalized = content.replace(/^\uFEFF/, '');
    const match = /^(---\s*\r?\n[\s\S]*?\r?\n---\s*\r?\n?)([\s\S]*)$/.exec(normalized);
    if (!match) {
        return { content };
    }
    const rawOptions = match[1]
        .replace(/^---\s*\r?\n/, '')
        .replace(/\r?\n---\s*\r?\n?$/, '');
    const options = parseYamlMap(rawOptions) ?? {};
    return {
        content: match[2],
        options: Object.keys(options).length > 0 ? options : undefined,
    };
}
function parseLanguage(info) {
    if (!info.trim()) {
        return '';
    }
    const first = info.trim().split(/\s+/)[0];
    return first.toLowerCase();
}
function parseMeta(info) {
    const trimmed = info.trim();
    if (!trimmed) {
        return undefined;
    }
    const index = trimmed.indexOf(' ');
    if (index === -1) {
        return undefined;
    }
    return trimmed.slice(index + 1).trim() || undefined;
}
function buildBlockId(lineStart, lineEnd, language, content) {
    return `block-${lineStart}-${lineEnd}-${hash32(`${language}|${content}`)}`;
}
function hash32(input) {
    let hash = 2166136261;
    for (let i = 0; i < input.length; i++) {
        hash ^= input.charCodeAt(i);
        hash = Math.imul(hash, 16777619);
    }
    return (hash >>> 0).toString(16);
}
//# sourceMappingURL=md-runtime.js.map