"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.renderStructuralPressureSyncResult = exports.renderStructuralPressureReport = void 0;
exports.readStructuralPressureReport = readStructuralPressureReport;
exports.resolveStructuralPressureScanExclusions = resolveStructuralPressureScanExclusions;
exports.syncStructuralPressureState = syncStructuralPressureState;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const errors_1 = require("../errors");
const structural_pressure_scan_defaults_1 = require("../config/structural-pressure-scan-defaults");
const workspace_discovery_1 = require("../workspace-discovery");
const paths_1 = require("../paths");
const planning_document_utils_1 = require("./planning-document-utils");
const implementation_summary_reconciliation_1 = require("./implementation-summary-reconciliation");
const structural_pressure_thresholds_1 = require("./structural-pressure-thresholds");
var structural_pressure_report_rendering_1 = require("./structural-pressure-report-rendering");
Object.defineProperty(exports, "renderStructuralPressureReport", { enumerable: true, get: function () { return structural_pressure_report_rendering_1.renderStructuralPressureReport; } });
Object.defineProperty(exports, "renderStructuralPressureSyncResult", { enumerable: true, get: function () { return structural_pressure_report_rendering_1.renderStructuralPressureSyncResult; } });
const STRUCTURAL_PRESSURE_STATE_PATH = ".day-shift/state/structural-pressure.json";
const STRUCTURAL_PRESSURE_SYNC_FENCE = "structural-pressure-sync";
const STRUCTURAL_PRESSURE_STATUSES = [
    "resolved",
    "acceptable-for-now",
    "follow-up-candidate"
];
const STRUCTURAL_PRESSURE_SEVERITIES = [
    "low",
    "medium",
    "high"
];
function readStructuralPressureReport(repositoryRoot, options) {
    const exclusions = options.exclusions ?? resolveStructuralPressureScanExclusions();
    const stateAbsolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, STRUCTURAL_PRESSURE_STATE_PATH);
    if (!node_fs_1.default.existsSync(stateAbsolutePath)) {
        const scan = scanStructuralPressureSources(repositoryRoot, {
            scanPath: options.scanPath,
            thresholds: options.thresholds,
            entries: [],
            exclusions
        });
        return {
            statePath: STRUCTURAL_PRESSURE_STATE_PATH,
            statePresent: false,
            sourceMilestoneReconciliationPath: null,
            sourceMilestoneOverviewPath: null,
            syncedAt: null,
            thresholds: options.thresholds,
            entries: [],
            driftFindings: [],
            scan
        };
    }
    const state = parseStructuralPressureState(node_fs_1.default.readFileSync(stateAbsolutePath, "utf8"), STRUCTURAL_PRESSURE_STATE_PATH);
    const scan = scanStructuralPressureSources(repositoryRoot, {
        scanPath: options.scanPath,
        thresholds: options.thresholds,
        entries: state.entries,
        exclusions
    });
    return {
        statePath: STRUCTURAL_PRESSURE_STATE_PATH,
        statePresent: true,
        sourceMilestoneReconciliationPath: state.source.milestone_reconciliation,
        sourceMilestoneOverviewPath: state.source.milestone_overview,
        syncedAt: state.synced_at,
        thresholds: options.thresholds,
        entries: state.entries,
        driftFindings: collectStructuralPressureDriftFindings(repositoryRoot, state),
        scan
    };
}
function resolveStructuralPressureScanExclusions(input) {
    return {
        excludedExtensions: normalizeStructuralPressureExcludedExtensions(input?.excluded_extensions, structural_pressure_scan_defaults_1.STRUCTURAL_PRESSURE_DEFAULT_EXCLUDED_EXTENSIONS),
        blockedSegments: normalizeStructuralPressureBlockedSegments(input?.blocked_segments, structural_pressure_scan_defaults_1.STRUCTURAL_PRESSURE_DEFAULT_BLOCKED_SCAN_SEGMENTS),
        excludedBasenames: normalizeStructuralPressureExcludedBasenames(input?.excluded_basenames, structural_pressure_scan_defaults_1.STRUCTURAL_PRESSURE_DEFAULT_EXCLUDED_BASENAMES)
    };
}
function syncStructuralPressureState(repositoryRoot, options) {
    const reconciliationPath = (0, paths_1.parseRepositoryRelativePath)(options.milestoneReconciliationPath).normalizedPath;
    const reconciliationAbsolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, reconciliationPath);
    if (!node_fs_1.default.existsSync(reconciliationAbsolutePath)) {
        throw (0, errors_1.createUsageError)(`Milestone reconciliation not found at '${reconciliationPath}'.`, ["Pass an existing repository-relative --milestone-reconciliation path."]);
    }
    const reconciliationText = node_fs_1.default.readFileSync(reconciliationAbsolutePath, "utf8");
    const frontMatterLines = parseFrontMatterLines(reconciliationPath, reconciliationText);
    const artifactType = (0, planning_document_utils_1.readTopLevelField)(frontMatterLines, "artifact_type")?.trim() ?? null;
    if (artifactType !== "reconciliation") {
        throw (0, errors_1.createUsageError)(`Artifact '${reconciliationPath}' must use artifact_type 'reconciliation'.`, ["Pass a repository-relative milestone reconciliation artifact."]);
    }
    const milestoneOverviewPath = (0, planning_document_utils_1.readUpstreamField)(frontMatterLines, "milestone_overview")?.trim() ?? null;
    if (milestoneOverviewPath === null || milestoneOverviewPath === "") {
        throw new errors_1.RuntimeError({
            message: "Structural-pressure sync rejected because the reconciliation artifact is missing upstream milestone context.",
            code: "STRUCTURAL_PRESSURE_SYNC_REJECTED",
            exitCode: errors_1.RuntimeExitCode.ERROR,
            details: [
                `Artifact '${reconciliationPath}' must declare upstream.milestone_overview before sync can mirror derived state.`
            ]
        });
    }
    const reconciliationReview = (0, implementation_summary_reconciliation_1.reviewReconciliation)(repositoryRoot, {
        milestoneOverviewPath
    });
    if (reconciliationReview.closureEvidenceStatus !== "sufficient") {
        throw new errors_1.RuntimeError({
            message: "Structural-pressure sync rejected because milestone reconciliation evidence is incomplete or ambiguous.",
            code: "STRUCTURAL_PRESSURE_SYNC_REJECTED",
            exitCode: errors_1.RuntimeExitCode.ERROR,
            details: [
                ...reconciliationReview.evidenceGaps,
                ...reconciliationReview.placeholderResidue,
                ...reconciliationReview.alignmentIssues,
                ...reconciliationReview.reviewConcerns
            ]
        });
    }
    const payload = extractSyncPayload(reconciliationText, reconciliationPath);
    const entries = validateSyncPayloadEntries(repositoryRoot, payload.entries, reconciliationPath, milestoneOverviewPath);
    const nextState = {
        schema_version: 1,
        synced_at: new Date().toISOString(),
        source: {
            milestone_reconciliation: reconciliationPath,
            milestone_overview: milestoneOverviewPath
        },
        entries
    };
    const rendered = `${JSON.stringify(nextState, null, 2)}\n`;
    const stateAbsolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, STRUCTURAL_PRESSURE_STATE_PATH);
    const previous = node_fs_1.default.existsSync(stateAbsolutePath)
        ? parseStructuralPressureState(node_fs_1.default.readFileSync(stateAbsolutePath, "utf8"), STRUCTURAL_PRESSURE_STATE_PATH)
        : null;
    const changed = previous === null || structuralPressureMaterialStateChanged(previous, nextState);
    if (changed) {
        node_fs_1.default.mkdirSync(node_path_1.default.dirname(stateAbsolutePath), { recursive: true });
        node_fs_1.default.writeFileSync(stateAbsolutePath, rendered, "utf8");
    }
    return {
        reconciliationPath,
        milestoneOverviewPath,
        statePath: STRUCTURAL_PRESSURE_STATE_PATH,
        entryCount: entries.length,
        changed,
        writeApplied: changed
    };
}
function structuralPressureMaterialStateChanged(previous, next) {
    return JSON.stringify({
        source: previous.source,
        entries: previous.entries
    }) !== JSON.stringify({
        source: next.source,
        entries: next.entries
    });
}
function scanStructuralPressureSources(repositoryRoot, options) {
    const requestedPath = options.scanPath?.trim() ?? null;
    const roots = requestedPath === null || requestedPath === ""
        ? ["src", "tests"]
        : [normalizeScanInputPath(requestedPath)];
    const defaultRootsUsed = requestedPath === null || requestedPath === "";
    const stateByPath = new Map(options.entries.map((entry) => [entry.path, entry]));
    const files = [];
    const discovery = (0, workspace_discovery_1.discoverWorkspacePaths)({
        workspaceRoot: repositoryRoot,
        budgets: { files: 25_000, directories: 10_000, evidenceEntries: 50_000 },
        include: (entry) => entry.kind === "file" && roots.some((root) => entry.path === root || entry.path.startsWith(`${root}/`))
    });
    for (const root of roots) {
        const absolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, root);
        if (!node_fs_1.default.existsSync(absolutePath)) {
            if (defaultRootsUsed) {
                continue;
            }
            throw (0, errors_1.createUsageError)(`Structural-pressure scan path not found: '${root}'.`, ["Pass an existing repository-relative file or directory."]);
        }
        const stat = node_fs_1.default.lstatSync(absolutePath);
        validateScanPath(repositoryRoot, root, stat.isDirectory(), options.exclusions);
        if (stat.isFile()) {
            if (discovery.paths.includes(root))
                files.push(root);
            continue;
        }
        if (stat.isDirectory()) {
            files.push(...discovery.paths.filter((filePath) => filePath.startsWith(`${root}/`) && !filePath.split("/").some((segment) => isBlockedStructuralPressureScanSegment(segment, options.exclusions.blockedSegments)) && !isExcludedStructuralPressureScanFile(node_path_1.default.basename(filePath), options.exclusions.excludedExtensions, options.exclusions.excludedBasenames) && !isBinaryFilePath(repositoryRoot, filePath)));
            continue;
        }
        throw (0, errors_1.createUsageError)(`Structural-pressure scan path '${root}' is not a regular file or directory.`, ["Pass a repository-relative file or directory."]);
    }
    const candidates = files
        .map((filePath) => buildStructuralPressureScanCandidate(repositoryRoot, filePath, roots, stateByPath, options.thresholds))
        .filter((candidate) => candidate !== null)
        .sort(compareStructuralPressureScanCandidates);
    const observedCandidatePaths = new Set(candidates.map((candidate) => candidate.path));
    const stateOnlyEntries = options.entries
        .filter((entry) => !observedCandidatePaths.has(entry.path))
        .map(buildStructuralPressureStateOnlyEntry)
        .sort((left, right) => left.path.localeCompare(right.path));
    return {
        summary: {
            requestedPath: defaultRootsUsed ? null : roots[0],
            defaultRootsUsed,
            roots,
            filesScanned: files.length,
            candidates: candidates.length,
            stateEntries: options.entries.length,
            trackedCandidateOverlays: candidates.filter((candidate) => candidate.reasonCodes.includes("tracked-state-entry")).length,
            observedOnlyCandidates: candidates.filter((candidate) => candidate.mergeStatus === "observed-only").length,
            trackedAndObservedCandidates: candidates.filter((candidate) => candidate.mergeStatus === "tracked-and-observed").length,
            trackedStateOnlyEntries: stateOnlyEntries.length
        },
        candidates,
        stateOnlyEntries,
        discovery: discovery.evidence
    };
}
function collectStructuralPressureDriftFindings(repositoryRoot, state) {
    const findings = [];
    const reviewedReconciliations = new Map();
    const collectForReconciliation = (reconciliationPath, milestoneOverviewPath, entryPath) => {
        const reconciliationAbsolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, reconciliationPath);
        if (!node_fs_1.default.existsSync(reconciliationAbsolutePath)) {
            findings.push({
                derivedStatePath: STRUCTURAL_PRESSURE_STATE_PATH,
                canonicalMarkdownPath: reconciliationPath,
                entryPath,
                reason: "Derived JSON points at a reconciliation artifact that is missing; canonical Markdown evidence is authoritative.",
                severity: "warning"
            });
            return;
        }
        if (!reviewedReconciliations.has(reconciliationPath)) {
            const review = (0, implementation_summary_reconciliation_1.reviewReconciliation)(repositoryRoot, {
                milestoneOverviewPath
            });
            reviewedReconciliations.set(reconciliationPath, [
                ...review.evidenceGaps,
                ...review.placeholderResidue,
                ...review.alignmentIssues
            ]);
        }
        const reviewIssues = reviewedReconciliations.get(reconciliationPath) ?? [];
        for (const reviewIssue of reviewIssues) {
            findings.push({
                derivedStatePath: STRUCTURAL_PRESSURE_STATE_PATH,
                canonicalMarkdownPath: reconciliationPath,
                entryPath,
                reason: `Derived JSON points at reconciliation evidence that is not completed: ${reviewIssue}`,
                severity: "warning"
            });
        }
    };
    collectForReconciliation(state.source.milestone_reconciliation, state.source.milestone_overview, null);
    for (const entry of state.entries) {
        collectForReconciliation(entry.source_milestone_reconciliation, entry.source_milestone_overview, entry.path);
    }
    return findings;
}
function normalizeScanInputPath(input) {
    try {
        return (0, paths_1.parseRepositoryRelativePath)(input).normalizedPath;
    }
    catch (error) {
        throw (0, errors_1.createUsageError)(`Invalid structural-pressure scan path '${input}'.`, [
            error instanceof Error ? error.message : "Pass a repository-relative file or directory path.",
            "Pass a repository-relative file or directory."
        ]);
    }
}
function validateScanPath(repositoryRoot, filePath, isDirectory, exclusions) {
    const parsed = (0, paths_1.parseRepositoryRelativePath)(filePath);
    const blockedSegment = parsed.segments.find((segment) => isBlockedStructuralPressureScanSegment(segment, exclusions.blockedSegments));
    if (blockedSegment !== undefined) {
        throw (0, errors_1.createUsageError)(`Structural-pressure scan path '${filePath}' includes unsupported segment '${blockedSegment}'.`, ["Do not scan hidden repository internals, dependency directories, generated output, derived state, or planning Markdown."]);
    }
    if (!isDirectory && isExcludedStructuralPressureScanFile(filePath, exclusions.excludedExtensions, exclusions.excludedBasenames)) {
        throw (0, errors_1.createUsageError)(`Structural-pressure scan path '${filePath}' uses an unsupported file extension.`, [`Excluded extensions: ${[...new Set(exclusions.excludedExtensions)].sort().join(", ")}.`]);
    }
    if (!isDirectory && isBinaryFilePath(repositoryRoot, filePath)) {
        throw (0, errors_1.createUsageError)(`Structural-pressure scan path '${filePath}' appears to be a binary file.`, ["Use --path with a text source file or directory."]);
    }
}
function collectStructuralPressureScanFiles(repositoryRoot, directoryPath, files, exclusions) {
    const absoluteDirectoryPath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, directoryPath);
    const entries = node_fs_1.default.readdirSync(absoluteDirectoryPath, { withFileTypes: true });
    for (const entry of entries) {
        const childPath = `${directoryPath}/${entry.name}`;
        if (isBlockedStructuralPressureScanSegment(entry.name, exclusions.blockedSegments)) {
            continue;
        }
        if (entry.isDirectory()) {
            collectStructuralPressureScanFiles(repositoryRoot, childPath, files, exclusions);
            continue;
        }
        if (entry.isFile()
            && !isExcludedStructuralPressureScanFile(entry.name, exclusions.excludedExtensions, exclusions.excludedBasenames)
            && !isBinaryFilePath(repositoryRoot, childPath)) {
            files.push(childPath);
        }
    }
}
function isExcludedStructuralPressureScanFile(filePath, excludedExtensions, excludedBasenames) {
    const basename = node_path_1.default.posix.basename(filePath).toLowerCase();
    const extension = node_path_1.default.extname(basename);
    const excludedSet = new Set(excludedExtensions.map((value) => value.toLowerCase()));
    const excludedBasenameSet = new Set(excludedBasenames.map((value) => value.toLowerCase()));
    if (excludedBasenameSet.has(basename)) {
        return true;
    }
    if (excludedSet.has(basename)) {
        return true;
    }
    if (excludedSet.has(extension)) {
        return true;
    }
    return basename.endsWith(".min.js") || basename.endsWith(".min.css");
}
function isBinaryFilePath(repositoryRoot, filePath) {
    const absolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, filePath);
    const maxBytesToInspect = 4096;
    const buffer = Buffer.allocUnsafe(maxBytesToInspect);
    const fileDescriptor = node_fs_1.default.openSync(absolutePath, "r");
    try {
        const bytesRead = node_fs_1.default.readSync(fileDescriptor, buffer, 0, maxBytesToInspect, 0);
        if (bytesRead === 0) {
            return false;
        }
        let nonTextBytes = 0;
        for (let index = 0; index < bytesRead; index += 1) {
            const value = buffer[index];
            if (value === 0) {
                return true;
            }
            // Allow common printable ASCII and whitespace control bytes.
            const isWhitespaceControl = value === 9 || value === 10 || value === 13;
            const isPrintableAscii = value >= 32 && value <= 126;
            if (!isWhitespaceControl && !isPrintableAscii) {
                nonTextBytes += 1;
            }
        }
        return (nonTextBytes / bytesRead) > 0.3;
    }
    finally {
        node_fs_1.default.closeSync(fileDescriptor);
    }
}
function buildStructuralPressureScanCandidate(repositoryRoot, filePath, roots, stateByPath, thresholds) {
    const absolutePath = (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, filePath);
    const fileKind = classifyStructuralPressureFileKind(filePath);
    const threshold = fileKind === "test"
        ? (0, structural_pressure_thresholds_1.structuralPressureThresholdValues)(thresholds.test)
        : (0, structural_pressure_thresholds_1.structuralPressureThresholdValues)(thresholds.source);
    const lineCount = countFileLines(node_fs_1.default.readFileSync(absolutePath, "utf8"));
    const classification = classifyStructuralPressureLineCount(lineCount, threshold);
    const trackedEntry = stateByPath.get(filePath);
    const trackedState = trackedEntry === undefined
        ? undefined
        : buildStructuralPressureTrackedStateMetadata(trackedEntry);
    if (classification === null && trackedEntry === undefined) {
        return null;
    }
    const reasonCodes = [
        fileKind === "test" ? "large-test-file" : "large-source-file"
    ];
    if (trackedEntry !== undefined) {
        reasonCodes.push("tracked-state-entry");
    }
    return {
        path: filePath,
        mergeStatus: trackedEntry === undefined ? "observed-only" : "tracked-and-observed",
        requestedPathContext: findRequestedPathContext(filePath, roots),
        fileKind,
        lineCount,
        threshold,
        classification: classification ?? "watch",
        sourceType: "source-scan",
        reasonCodes,
        ...(trackedEntry === undefined
            ? {}
            : {
                trackedStatus: trackedEntry.status,
                trackedSeverity: trackedEntry.severity,
                trackedSummary: trackedEntry.summary,
                trackedEvidencePaths: trackedEntry.evidence_paths,
                trackedState
            })
    };
}
function buildStructuralPressureStateOnlyEntry(entry) {
    return {
        path: entry.path,
        mergeStatus: "tracked-state-only",
        sourceType: "derived-state",
        reasonCodes: ["tracked-state-entry"],
        trackedState: buildStructuralPressureTrackedStateMetadata(entry)
    };
}
function buildStructuralPressureTrackedStateMetadata(entry) {
    return {
        status: entry.status,
        severity: entry.severity,
        summary: entry.summary,
        evidencePaths: entry.evidence_paths,
        sourceMilestoneReconciliationPath: entry.source_milestone_reconciliation,
        sourceMilestoneOverviewPath: entry.source_milestone_overview
    };
}
function classifyStructuralPressureFileKind(filePath) {
    const segments = (0, paths_1.parseRepositoryRelativePath)(filePath).segments;
    const basename = node_path_1.default.posix.basename(filePath).toLowerCase();
    if (segments[0] === "tests" ||
        segments.includes("__tests__") ||
        /\.test\.[cm]?[jt]sx?$/.test(basename) ||
        /\.spec\.[cm]?[jt]sx?$/.test(basename)) {
        return "test";
    }
    return "source";
}
function classifyStructuralPressureLineCount(lineCount, threshold) {
    if (lineCount >= threshold.hotspot) {
        return "hotspot";
    }
    if (lineCount >= threshold.pressure) {
        return "pressure";
    }
    if (lineCount >= threshold.watch) {
        return "watch";
    }
    return null;
}
function countFileLines(text) {
    if (text.length === 0) {
        return 0;
    }
    return text.endsWith("\n")
        ? text.split(/\r?\n/).length - 1
        : text.split(/\r?\n/).length;
}
function findRequestedPathContext(filePath, roots) {
    return roots.find((root) => filePath === root || filePath.startsWith(`${root}/`)) ?? filePath;
}
function isBlockedStructuralPressureScanSegment(segment, blockedSegments) {
    const blockedSet = new Set(blockedSegments.map((value) => value.toLowerCase()));
    return segment.startsWith(".") || blockedSet.has(segment.toLowerCase());
}
function normalizeStructuralPressureExcludedExtensions(configured, fallback) {
    const source = configured ?? fallback;
    return [...new Set(source.map((entry) => entry.trim().toLowerCase()))]
        .filter((entry) => entry.length > 0)
        .filter((entry) => entry.startsWith("."))
        .sort();
}
function normalizeStructuralPressureBlockedSegments(configured, fallback) {
    const source = configured ?? fallback;
    return [...new Set(source.map((entry) => entry.trim().toLowerCase()))]
        .filter((entry) => entry.length > 0)
        .filter((entry) => !entry.includes("/") && !entry.includes("\\"))
        .sort();
}
function normalizeStructuralPressureExcludedBasenames(configured, fallback) {
    const source = configured ?? fallback;
    return [...new Set(source.map((entry) => entry.trim().toLowerCase()))]
        .filter((entry) => entry.length > 0)
        .filter((entry) => !entry.includes("/") && !entry.includes("\\"))
        .sort();
}
function compareStructuralPressureScanCandidates(left, right) {
    return classificationSortRank(right.classification) - classificationSortRank(left.classification) ||
        left.path.localeCompare(right.path);
}
function classificationSortRank(classification) {
    switch (classification) {
        case "hotspot":
            return 3;
        case "pressure":
            return 2;
        case "watch":
            return 1;
    }
}
function parseFrontMatterLines(filePath, text) {
    if (!text.startsWith("---\n")) {
        throw (0, errors_1.createUsageError)(`Artifact '${filePath}' is missing required YAML front matter.`, ["Repair the reconciliation artifact before running sync."]);
    }
    const endIndex = text.indexOf("\n---\n");
    if (endIndex < 0) {
        throw (0, errors_1.createUsageError)(`Artifact '${filePath}' has malformed YAML front matter.`, ["Repair the reconciliation artifact before running sync."]);
    }
    return text
        .slice(4, endIndex)
        .split(/\r?\n/);
}
function parseStructuralPressureState(text, filePath) {
    let parsed;
    try {
        parsed = JSON.parse(text);
    }
    catch (error) {
        throw new errors_1.RuntimeError({
            message: "Structural-pressure report failed because the derived state file is not valid JSON.",
            code: "STRUCTURAL_PRESSURE_STATE_INVALID",
            exitCode: errors_1.RuntimeExitCode.ERROR,
            details: [
                `${filePath}: ${error instanceof Error ? error.message : "Could not parse JSON."}`
            ]
        });
    }
    if (!isRecord(parsed)) {
        throw invalidStateError(filePath, "Top-level JSON value must be an object.");
    }
    if (parsed.schema_version !== 1) {
        throw invalidStateError(filePath, "schema_version must equal 1.");
    }
    if (!isRecord(parsed.source)) {
        throw invalidStateError(filePath, "source must be an object.");
    }
    if (!Array.isArray(parsed.entries)) {
        throw invalidStateError(filePath, "entries must be an array.");
    }
    const milestoneReconciliation = requireStringField(parsed.source, "milestone_reconciliation", filePath);
    const milestoneOverview = requireStringField(parsed.source, "milestone_overview", filePath);
    const syncedAt = requireStringField(parsed, "synced_at", filePath);
    const entries = parsed.entries.map((entry, index) => {
        if (!isRecord(entry)) {
            throw invalidStateError(filePath, `entries[${index}] must be an object.`);
        }
        const normalizedPath = (0, paths_1.parseRepositoryRelativePath)(requireStringField(entry, "path", filePath, `entries[${index}]`)).normalizedPath;
        const status = requireEnumField(entry, "status", STRUCTURAL_PRESSURE_STATUSES, filePath, `entries[${index}]`);
        const severity = requireEnumField(entry, "severity", STRUCTURAL_PRESSURE_SEVERITIES, filePath, `entries[${index}]`);
        const summary = requireStringField(entry, "summary", filePath, `entries[${index}]`).trim();
        const evidencePaths = normalizeEvidencePaths(entry.evidence_paths, filePath, `entries[${index}]`);
        const sourceMilestoneReconciliation = requireStringField(entry, "source_milestone_reconciliation", filePath, `entries[${index}]`);
        const sourceMilestoneOverview = requireStringField(entry, "source_milestone_overview", filePath, `entries[${index}]`);
        return {
            path: normalizedPath,
            status,
            severity,
            summary,
            evidence_paths: evidencePaths,
            source_milestone_reconciliation: sourceMilestoneReconciliation,
            source_milestone_overview: sourceMilestoneOverview
        };
    });
    return {
        schema_version: 1,
        synced_at: syncedAt,
        source: {
            milestone_reconciliation: milestoneReconciliation,
            milestone_overview: milestoneOverview
        },
        entries
    };
}
function extractSyncPayload(reconciliationText, reconciliationPath) {
    const fencePattern = new RegExp(`\`\`\`${STRUCTURAL_PRESSURE_SYNC_FENCE}\\s*\\r?\\n([\\s\\S]*?)\\r?\\n\`\`\``, "g");
    const matches = [...reconciliationText.matchAll(fencePattern)];
    if (matches.length === 0) {
        throw new errors_1.RuntimeError({
            message: "Structural-pressure sync rejected because reconciliation evidence does not include an explicit sync payload.",
            code: "STRUCTURAL_PRESSURE_SYNC_REJECTED",
            exitCode: errors_1.RuntimeExitCode.ERROR,
            details: [
                `Add one \`\`\`${STRUCTURAL_PRESSURE_SYNC_FENCE}\`\`\` fenced JSON block to '${reconciliationPath}' with the deduped current-state verdict.`
            ]
        });
    }
    if (matches.length > 1) {
        throw new errors_1.RuntimeError({
            message: "Structural-pressure sync rejected because reconciliation evidence contains multiple sync payloads.",
            code: "STRUCTURAL_PRESSURE_SYNC_REJECTED",
            exitCode: errors_1.RuntimeExitCode.ERROR,
            details: [
                `Keep exactly one \`\`\`${STRUCTURAL_PRESSURE_SYNC_FENCE}\`\`\` fenced JSON block in '${reconciliationPath}'.`
            ]
        });
    }
    try {
        return JSON.parse(matches[0][1]);
    }
    catch (error) {
        throw new errors_1.RuntimeError({
            message: "Structural-pressure sync rejected because the explicit sync payload is not valid JSON.",
            code: "STRUCTURAL_PRESSURE_SYNC_REJECTED",
            exitCode: errors_1.RuntimeExitCode.ERROR,
            details: [
                `${reconciliationPath}: ${error instanceof Error ? error.message : "Could not parse sync payload JSON."}`
            ]
        });
    }
}
function validateSyncPayloadEntries(repositoryRoot, entries, reconciliationPath, milestoneOverviewPath) {
    if (!Array.isArray(entries)) {
        throw new errors_1.RuntimeError({
            message: "Structural-pressure sync rejected because the explicit sync payload is missing an entries array.",
            code: "STRUCTURAL_PRESSURE_SYNC_REJECTED",
            exitCode: errors_1.RuntimeExitCode.ERROR,
            details: ["The sync payload must be a JSON object with an 'entries' array."]
        });
    }
    const seenPaths = new Set();
    const normalizedEntries = entries.map((entry, index) => {
        if (!isRecord(entry)) {
            throw syncPayloadError(`entries[${index}] must be an object.`);
        }
        const normalizedPath = (0, paths_1.parseRepositoryRelativePath)(requireStringField(entry, "path", reconciliationPath, `entries[${index}]`)).normalizedPath;
        if (seenPaths.has(normalizedPath)) {
            throw syncPayloadError(`Duplicate structural-pressure entry for '${normalizedPath}' is not allowed.`);
        }
        seenPaths.add(normalizedPath);
        const status = requireEnumField(entry, "status", STRUCTURAL_PRESSURE_STATUSES, reconciliationPath, `entries[${index}]`);
        const severity = requireEnumField(entry, "severity", STRUCTURAL_PRESSURE_SEVERITIES, reconciliationPath, `entries[${index}]`);
        const summary = requireStringField(entry, "summary", reconciliationPath, `entries[${index}]`).trim();
        if (summary.length === 0) {
            throw syncPayloadError(`entries[${index}].summary must not be empty.`);
        }
        const evidencePaths = normalizeEvidencePaths(entry.evidence_paths, reconciliationPath, `entries[${index}]`);
        for (const evidencePath of evidencePaths) {
            (0, paths_1.resolveRepositoryRelativePath)(repositoryRoot, evidencePath);
        }
        return {
            path: normalizedPath,
            status,
            severity,
            summary,
            evidence_paths: evidencePaths,
            source_milestone_reconciliation: reconciliationPath,
            source_milestone_overview: milestoneOverviewPath
        };
    });
    return normalizedEntries.sort((left, right) => left.path.localeCompare(right.path));
}
function requireStringField(input, key, filePath, context = "state") {
    const value = input[key];
    if (typeof value !== "string" || value.trim().length === 0) {
        throw invalidStateError(filePath, `${context}.${key} must be a non-empty string.`);
    }
    return value;
}
function requireEnumField(input, key, values, filePath, context = "state") {
    const value = requireStringField(input, key, filePath, context);
    if (!values.includes(value)) {
        throw invalidStateError(filePath, `${context}.${key} must be one of: ${values.join(", ")}.`);
    }
    return value;
}
function normalizeEvidencePaths(value, filePath, context) {
    if (value === undefined) {
        return [];
    }
    if (!Array.isArray(value)) {
        throw invalidStateError(filePath, `${context}.evidence_paths must be an array when present.`);
    }
    return value.map((entry, index) => {
        if (typeof entry !== "string" || entry.trim().length === 0) {
            throw invalidStateError(filePath, `${context}.evidence_paths[${index}] must be a non-empty string.`);
        }
        return (0, planning_document_utils_1.toRepositoryLikePath)((0, paths_1.parseRepositoryRelativePath)(entry).normalizedPath);
    });
}
function invalidStateError(filePath, detail) {
    return new errors_1.RuntimeError({
        message: "Structural-pressure report failed because the derived state file is malformed.",
        code: "STRUCTURAL_PRESSURE_STATE_INVALID",
        exitCode: errors_1.RuntimeExitCode.ERROR,
        details: [`${filePath}: ${detail}`]
    });
}
function syncPayloadError(detail) {
    return new errors_1.RuntimeError({
        message: "Structural-pressure sync rejected because the explicit sync payload is invalid.",
        code: "STRUCTURAL_PRESSURE_SYNC_REJECTED",
        exitCode: errors_1.RuntimeExitCode.ERROR,
        details: [detail]
    });
}
function isRecord(value) {
    return typeof value === "object" && value !== null;
}
