// engine/spec-format/read-technical-section.ts — SPEC-1010 PR-B
//
// SSR back-migration (SPEC-752, v2.4.0) folded `technical.md` into the
// unified `spec.md` as a `## Technical` section. This helper is the
// single source of truth for "give me the technical body for this spec":
// it extracts only the canonical inline `## Technical` section.
import { readFile } from 'node:fs/promises';
/**
 * Read the `## Technical` section body from a spec's unified `spec.md`.
 * Returns "" when the spec file is unreadable or the section is missing.
 */
export async function readSpecTechnicalSection(spec) {
    return extractFromUnified(spec.specPath);
}
async function extractFromUnified(specPath) {
    const content = await readFile(specPath, 'utf-8').catch(() => '');
    if (content.length === 0) {
        return '';
    }
    return extractSectionBody(content, 'Technical');
}
/**
 * Extract the body of a top-level (`## `) section from a markdown document.
 * Returns the content AFTER the heading line, up to the next top-level
 * heading or EOF, with surrounding whitespace trimmed. Returns "" when
 * the section is not present.
 *
 * Hardening (SPEC-1010 PR-B dual-Opus review):
 * - Fenced code blocks (``` and ~~~) are masked before scanning so a quoted
 *   markdown sample inside the spec body cannot match the heading regex
 *   nor the next-section terminator. Offsets are preserved (mask uses
 *   same-length placeholders) so we can slice from the original `body`.
 * - CRLF / mixed line endings are normalized to LF before scanning.
 * - The `sectionName` is regex-escaped to make this safe for future
 *   callers passing arbitrary names.
 */
export function extractSectionBody(body, sectionName) {
    const normalized = body.replace(/\r\n/g, '\n');
    const masked = maskFencedBlocks(normalized);
    // Anchor end-of-name to the line terminator so `## Technical` does not
    // match `## Technical Notes` etc. Use `[ \\t]*` (not `\\s*`) to avoid
    // consuming the line terminator via greedy backtrack. (SPEC-1010 PR-C
    // dual-Opus review)
    const headingRe = new RegExp(`^##\\s+${escapeRegex(sectionName)}[ \\t]*$`, 'm');
    const match = headingRe.exec(masked);
    if (!match) {
        return '';
    }
    const afterHeading = match.index + match[0].length;
    const nextRe = /^##\s+\S/gm;
    nextRe.lastIndex = afterHeading;
    const next = nextRe.exec(masked);
    const end = next ? next.index : normalized.length;
    return normalized.slice(afterHeading, end).trim();
}
/**
 * Replace fenced code-block contents with same-length filler so heading
 * regexes do not see the inner markdown. We preserve the exact byte
 * offsets so callers can slice the ORIGINAL string at indices computed
 * against the masked variant.
 */
function maskFencedBlocks(body) {
    return body.replace(/(```|~~~)[\s\S]*?\1/g, (block) => {
        // Replace every non-newline char with a space so offsets (and line
        // numbers) are preserved exactly. Keep newlines intact.
        return block.replace(/[^\n]/g, ' ');
    });
}
function escapeRegex(input) {
    return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
//# sourceMappingURL=read-technical-section.js.map