"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.enforceLifecycleMetadataPosture = enforceLifecycleMetadataPosture;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const errors_1 = require("./errors");
const lifecycle_run_context_1 = require("./lifecycle-run-context");
const metadata_sync_and_inventory_1 = require("./planning/metadata-sync-and-inventory");
const LIFECYCLE_STATUS_FIELDS = new Set([
    "status",
    "readiness",
    "implementation_status",
    "reconciliation_status"
]);
function enforceLifecycleMetadataPosture(options) {
    if (options.runDirectory === null) {
        return null;
    }
    const runDirectory = options.runDirectory;
    const inspection = inspectRun({
        repositoryRoot: options.repositoryRoot,
        runDirectory,
        artifactPath: options.artifactPath
    });
    const manifest = inspection.manifest;
    const lifecycleAssignments = options.assignments.filter((assignment) => LIFECYCLE_STATUS_FIELDS.has(assignment.key));
    const evidence = {
        runDirectory: manifest.runDirectory,
        metadataPosture: manifest.metadataPosture,
        activeArtifactPath: manifest.activeArtifactPath,
        activeGate: manifest.activeGate,
        freshness: "fresh",
        lifecycleStatusFields: lifecycleAssignments.map((assignment) => assignment.key),
        reviewId: inspection.terminalReview.reviewId,
        contractRevisionCheckpointId: manifest.contractRevisions.at(-1)?.checkpointId ?? null
    };
    if (manifest.metadataPosture === "read-only") {
        if (lifecycleAssignments.length > 0) {
            throwPostureBlocked({
                artifactPath: options.artifactPath,
                reason: "read-only-lifecycle-field",
                detail: [
                    `Lifecycle run '${manifest.runDirectory}' has read-only metadata posture at gate '${manifest.activeGate}'.`,
                    `The requested lifecycle fields are: ${lifecycleAssignments.map((assignment) => assignment.key).join(", ")}.`,
                    "Read-only posture never permits lifecycle status or readiness mutation."
                ].join(" "),
                recovery: readOnlyReviewRecovery(options.artifactType, options.artifactPath)
            });
        }
        return evidence;
    }
    assertSyncOnCleanReviewWrite({
        ...options,
        runDirectory,
        inspection,
        lifecycleAssignments
    });
    return evidence;
}
function inspectRun(options) {
    let inspection;
    try {
        inspection = (0, lifecycle_run_context_1.inspectLifecycleRunContext)({
            repositoryRoot: options.repositoryRoot,
            runDirectory: options.runDirectory,
            selectedArtifactPath: options.artifactPath
        });
    }
    catch (error) {
        throwPostureBlocked({
            artifactPath: options.artifactPath,
            reason: "run-context-unavailable",
            detail: `Lifecycle run '${options.runDirectory}' could not be inspected: ${errorMessage(error)}`,
            recovery: "Restore or reopen the selected lifecycle run, then rerun its read-only status inspection before retrying this metadata command."
        });
    }
    if (inspection.conflict !== null) {
        throwPostureBlocked({
            artifactPath: options.artifactPath,
            reason: "active-artifact-conflict",
            detail: [
                `Lifecycle run '${inspection.manifest.runDirectory}' selects active artifact '${inspection.conflict.activeArtifactPath}'.`,
                `The metadata command selected '${inspection.conflict.selectedArtifactPath}'.`
            ].join(" "),
            recovery: "Return to the run's active artifact and permitted gate; do not retarget a delegated metadata write."
        });
    }
    if (inspection.state !== "fresh" || !inspection.manifestComplete) {
        throwPostureBlocked({
            artifactPath: options.artifactPath,
            reason: "run-context-not-fresh",
            detail: [
                `Lifecycle run '${inspection.manifest.runDirectory}' is '${inspection.state}' and cannot authorize metadata planning.`,
                `Stale reasons: ${inspection.staleReasons.join(", ") || "none"}.`,
                `Incomplete reasons: ${inspection.incompleteReasons.join(", ") || "none"}.`
            ].join(" "),
            recovery: "Run day-shift resume run status for this run directory and resolve its exact stale, conflict, or incomplete evidence before retrying."
        });
    }
    return inspection;
}
function assertSyncOnCleanReviewWrite(options) {
    const { inspection } = options;
    const manifest = inspection.manifest;
    if (manifest.activeGate !== "phase-review") {
        throwPostureBlocked({
            artifactPath: options.artifactPath,
            reason: "active-gate-conflict",
            detail: `Lifecycle run '${manifest.runDirectory}' is at gate '${manifest.activeGate}'; sync-on-clean-review metadata requires the current 'phase-review' gate.`,
            recovery: "Complete and record the phase review under the phase-review gate, then retry only while that exact run evidence remains current."
        });
    }
    if (options.assignments.length !== 1
        || options.lifecycleAssignments.length !== 1
        || options.lifecycleAssignments[0]?.key !== "status"
        || options.lifecycleAssignments[0]?.value !== "completed") {
        throwPostureBlocked({
            artifactPath: options.artifactPath,
            reason: "review-transition-scope-mismatch",
            detail: "Sync-on-clean-review posture permits only the exact status=completed transition justified by the current clean phase-review receipt; unrelated or additional fields are not permitted.",
            recovery: "Use the revision-bound metadata sync-status-from-review preview for the clean receipt, or issue a standalone explicit update outside lifecycle-run context when independently authorized."
        });
    }
    const nonPostureReasons = inspection.terminalReview.reasons.filter((reason) => reason !== "phase-review-posture-not-read-only");
    if (inspection.terminalReview.candidateEntryId === null
        || inspection.terminalReview.reviewId === null
        || nonPostureReasons.length > 0) {
        throwPostureBlocked({
            artifactPath: options.artifactPath,
            reason: "clean-review-receipt-unavailable",
            detail: [
                "Sync-on-clean-review posture requires one indexed, complete, zero-blocker phase-review receipt with current overview and reconciliation revisions.",
                `Review reasons: ${inspection.terminalReview.reasons.join(", ") || "phase-review-entry-missing"}.`
            ].join(" "),
            recovery: "Rerun phase review with sync-on-clean-review posture, record that exact result under the phase-review gate, and retry without editing the receipt."
        });
    }
    const entry = manifest.entries.find((candidate) => candidate.entryId === inspection.terminalReview.candidateEntryId);
    if (entry === undefined) {
        throwPostureBlocked({
            artifactPath: options.artifactPath,
            reason: "clean-review-entry-missing",
            detail: "The selected clean phase-review receipt is not indexed by the current lifecycle manifest.",
            recovery: "Record the current phase-review result through resume run record, then rerun run status before retrying."
        });
    }
    const phaseReviewResult = readRecordedPhaseReview(options.repositoryRoot, entry.evidencePath);
    const preview = (0, metadata_sync_and_inventory_1.syncPhaseMetadataStatusesFromReview)({
        repositoryRoot: options.repositoryRoot,
        rootPath: manifest.phasePath,
        activeArtifactPath: manifest.phasePath,
        phaseReviewResult,
        metadataPosture: "sync-on-clean-review",
        dryRun: true
    });
    const transition = preview.transitions.find((candidate) => candidate.artifactPath === options.artifactPath);
    if (transition === undefined
        || transition.artifactType !== options.artifactType
        || !transition.eligible
        || transition.newStatus !== "completed") {
        throwPostureBlocked({
            artifactPath: options.artifactPath,
            reason: "review-transition-not-authorized",
            detail: `The current clean phase-review receipt does not justify status=completed for '${options.artifactPath}'.`,
            recovery: "Use the exact artifact set and transition returned by metadata sync-status-from-review --dry-run; do not narrow or rewrite the receipt."
        });
    }
}
function readRecordedPhaseReview(repositoryRoot, evidencePath) {
    try {
        const document = JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.join(repositoryRoot, evidencePath), "utf8"));
        if (typeof document !== "object" || document === null) {
            throw new TypeError("recorded evidence does not contain a result envelope");
        }
        if ("result" in document)
            return document.result;
        const envelope = document;
        if (envelope.status === "ok" || envelope.status === "error")
            return document;
        throw new TypeError("recorded evidence does not contain a result envelope");
    }
    catch (error) {
        throwPostureBlocked({
            artifactPath: evidencePath,
            reason: "clean-review-evidence-invalid",
            detail: `Recorded phase-review evidence could not be read: ${errorMessage(error)}`,
            recovery: "Restore the indexed evidence bytes or record a fresh unmodified phase-review result before retrying."
        });
    }
}
function throwPostureBlocked(options) {
    throw (0, errors_1.createBlockingInvalidConfigError)({
        message: "Lifecycle metadata posture rejected the write.",
        detail: `${options.detail} Recovery: ${options.recovery} No metadata write was applied.`,
        findingCode: "LIFECYCLE_METADATA_POSTURE_BLOCKED",
        findingMessage: options.detail,
        path: options.artifactPath,
        blockingReason: options.reason.includes("conflict")
            ? "unsafe-scope-mismatch"
            : "invalid-command-input",
        recovery: options.recovery,
        evidenceDetails: {
            postureReason: options.reason,
            writeOutcome: "no-writes"
        }
    });
}
function readOnlyReviewRecovery(artifactType, artifactPath) {
    if (artifactType === "phase_overview") {
        return `Use the permitted read-only review path: day-shift phase review --phase-overview ${artifactPath} --metadata-posture read-only. Do not run phase update, milestone update, or metadata update for lifecycle status. `;
    }
    if (artifactType === "milestone_overview") {
        return `Use the permitted read-only review path: day-shift milestone review --milestone-overview ${artifactPath}. Do not run milestone update or metadata update for lifecycle status. `;
    }
    return `Use the permitted read-only review or validation path for '${artifactPath}' and keep lifecycle status unchanged. `;
}
function errorMessage(error) {
    return error instanceof Error ? error.message : String(error);
}
