"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parsePlanningArtifactDocument = parsePlanningArtifactDocument;
const node_crypto_1 = require("node:crypto");
const planning_document_utils_1 = require("./planning-document-utils");
function scanLines(text) {
    const lines = [];
    const pattern = /.*?(?:\r\n|\n|$)/g;
    let match;
    while ((match = pattern.exec(text)) !== null) {
        if (match[0] === "" && pattern.lastIndex === text.length) {
            break;
        }
        const start = match.index;
        const endingLength = match[0].endsWith("\r\n") ? 2 : match[0].endsWith("\n") ? 1 : 0;
        const contentEnd = start + match[0].length - endingLength;
        lines.push({ text: text.slice(start, contentEnd), start, contentEnd, end: start + match[0].length });
    }
    return lines;
}
function positionAt(lines, offset) {
    let lineIndex = lines.findIndex((line) => offset < line.end || (line.end === line.start && offset === line.start));
    if (lineIndex < 0) {
        lineIndex = Math.max(0, lines.length - 1);
    }
    const line = lines[lineIndex];
    return {
        offset,
        line: lineIndex + 1,
        column: line === undefined ? 1 : Math.max(1, offset - line.start + 1)
    };
}
function range(lines, start, end) {
    return { start: positionAt(lines, start), end: positionAt(lines, end) };
}
function parseTopLevelFields(frontMatterLines) {
    const fields = {};
    for (const line of frontMatterLines) {
        if (/^\s/.test(line))
            continue;
        const match = line.match(/^([A-Za-z0-9_]+):\s*(.*)$/);
        if (match !== null)
            fields[match[1]] = (0, planning_document_utils_1.parseScalar)(match[2]);
    }
    return fields;
}
function parsePlanningArtifactDocument(rawText) {
    const lines = scanLines(rawText);
    const wholeRange = range(lines, 0, rawText.length);
    const diagnostics = [];
    const startsWithFrontMatter = lines[0]?.text === "---";
    const closingLineIndex = startsWithFrontMatter
        ? lines.findIndex((line, index) => index > 0 && line.text === "---")
        : -1;
    const hasFrontMatter = startsWithFrontMatter && closingLineIndex >= 0;
    if (!startsWithFrontMatter) {
        diagnostics.push({
            code: "missing-front-matter",
            severity: "warning",
            message: "Planning artifact does not begin with YAML front matter.",
            range: range(lines, 0, Math.min(rawText.length, lines[0]?.contentEnd ?? 0))
        });
    }
    else if (closingLineIndex < 0) {
        diagnostics.push({
            code: "unterminated-front-matter",
            severity: "error",
            message: "Planning artifact front matter has no closing delimiter.",
            range: wholeRange
        });
    }
    const bodyStart = hasFrontMatter ? lines[closingLineIndex].end : 0;
    const frontMatterStart = hasFrontMatter ? lines[0].start : 0;
    const frontMatterEnd = hasFrontMatter ? lines[closingLineIndex].end : 0;
    const frontMatterLines = hasFrontMatter
        ? lines.slice(1, closingLineIndex).map((line) => line.text)
        : [];
    const sections = [];
    const headings = lines
        .map((line, index) => ({ line, index, match: line.text.match(/^##\s+(.+)$/) }))
        .filter((entry) => entry.line.start >= bodyStart && entry.match !== null);
    for (let index = 0; index < headings.length; index += 1) {
        const entry = headings[index];
        const sectionEnd = headings[index + 1]?.line.start ?? rawText.length;
        const contentStart = entry.line.end;
        sections.push({
            heading: entry.match[1].trim(),
            headingText: entry.line.text,
            text: rawText.slice(entry.line.start, sectionEnd),
            contentText: rawText.slice(contentStart, sectionEnd),
            range: range(lines, entry.line.start, sectionEnd),
            headingRange: range(lines, entry.line.start, entry.line.contentEnd),
            contentRange: range(lines, contentStart, sectionEnd)
        });
    }
    return {
        rawText,
        contentHash: (0, node_crypto_1.createHash)("sha256").update(rawText).digest("hex"),
        hasFrontMatter,
        frontMatterText: hasFrontMatter ? rawText.slice(frontMatterStart, frontMatterEnd) : "",
        frontMatterLines,
        frontMatterFields: parseTopLevelFields(frontMatterLines),
        frontMatterRange: hasFrontMatter ? range(lines, frontMatterStart, frontMatterEnd) : null,
        bodyText: rawText.slice(bodyStart),
        bodyRange: range(lines, bodyStart, rawText.length),
        sections,
        diagnostics
    };
}
