// engine/spec-format/replace-section.ts — SPEC-1010 PR-C
//
// Shared helper to replace (or append) a top-level `## SectionName` block
// inside a spec.md file while preserving all other sections.
//
// Uses the same fence-masked, CRLF-normalized scanning approach as
// `extractSectionBody` in `read-technical-section.ts` to avoid false
// positive heading matches inside fenced code blocks.
import { readFile } from 'node:fs/promises';
import { atomicWriteFile } from '../safety/atomic-write-file.js';
/**
 * Replace the body of a top-level `## ${sectionName}` section in the given
 * spec.md file with `newBody`. If the section does not yet exist it is
 * appended at the end of the file.
 *
 * Atomic: uses `atomicWriteFile` (tmp + fsync + rename) so the original
 * file is never left in a partial state.
 *
 * @param specPath - Absolute path to the spec.md file.
 * @param sectionName - Exact section name (e.g. "Technical", "Progress").
 * @param newBody - The replacement body (without the heading line).
 * @returns Whether the section was replaced vs appended.
 */
export async function replaceSectionInSpec(specPath, sectionName, newBody) {
    // Treat ENOENT as empty-content + go through the append branch so callers
    // (heal_spec_docs, scan_project) can self-heal a missing spec.md.
    let raw;
    try {
        raw = await readFile(specPath, 'utf-8');
    }
    catch (err) {
        if (err.code === 'ENOENT') {
            raw = '';
        }
        else {
            throw err;
        }
    }
    const normalized = raw.replace(/\r\n/g, '\n');
    const masked = maskFencedBlocks(normalized);
    // Anchor end-of-name to the line terminator so `## Technical` does not
    // match `## Technical Notes` / `## Technical Debt` etc. The original
    // `\\b.*$` form silently clobbered prefix-collision sections when the
    // user authored them in their spec.md. We use `[ \\t]*` (not `\\s*`)
    // to permit trailing whitespace on the heading line WITHOUT consuming
    // the line terminator (which `\\s*$` with the `m` flag would do via
    // greedy backtrack on `\\n`). (SPEC-1010 PR-C dual-Opus review)
    const headingRe = new RegExp(`^##\\s+${escapeRegex(sectionName)}[ \\t]*$`, 'm');
    const match = headingRe.exec(masked);
    if (match) {
        // Section found — slice out its body (from after the heading line to the
        // next top-level heading or EOF) and replace it with newBody.
        const afterHeading = match.index + match[0].length;
        const nextRe = /^##\s+\S/gm;
        nextRe.lastIndex = afterHeading;
        const next = nextRe.exec(masked);
        const headingLine = normalized.slice(match.index, afterHeading);
        const afterSection = next ? normalized.slice(next.index) : '';
        const trimmedBody = newBody.endsWith('\n') ? newBody : `${newBody}\n`;
        const updated = `${normalized.slice(0, match.index)}${headingLine}\n\n${trimmedBody}\n${afterSection}`;
        await atomicWriteFile(specPath, updated);
        return { replaced: true, appended: false };
    }
    // Section not found — append it.
    const trimmedBody = newBody.endsWith('\n') ? newBody : `${newBody}\n`;
    const trailer = normalized.endsWith('\n') ? '' : '\n';
    const updated = `${normalized}${trailer}\n## ${sectionName}\n\n${trimmedBody}`;
    await atomicWriteFile(specPath, updated);
    return { replaced: false, appended: true };
}
// ---------------------------------------------------------------------------
// Internal helpers (mirrors read-technical-section.ts — kept private to avoid
// creating a circular dependency on that module)
// ---------------------------------------------------------------------------
function maskFencedBlocks(body) {
    return body.replace(/(```|~~~)[\s\S]*?\1/g, (block) => block.replace(/[^\n]/g, ' '));
}
function escapeRegex(input) {
    return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
//# sourceMappingURL=replace-section.js.map