"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RuntimeError = exports.TERMINAL_FAILURE_CATEGORIES = exports.RuntimeExitCode = void 0;
exports.normalizeTerminalFailureContract = normalizeTerminalFailureContract;
exports.getRuntimeErrorEnvelopeFields = getRuntimeErrorEnvelopeFields;
exports.createDescriptorBackedRecoveryAction = createDescriptorBackedRecoveryAction;
exports.createNonCallableRecoveryAction = createNonCallableRecoveryAction;
exports.createUsageError = createUsageError;
exports.createUnsupportedOutputFormatError = createUnsupportedOutputFormatError;
exports.createCommanderUsageError = createCommanderUsageError;
exports.createInvalidConfigError = createInvalidConfigError;
exports.createBlockingInvalidConfigError = createBlockingInvalidConfigError;
exports.createUnexpectedError = createUnexpectedError;
exports.isCommanderLifecycleSignal = isCommanderLifecycleSignal;
exports.isCommanderUsageError = isCommanderUsageError;
const output_1 = require("../output");
const command_capabilities_1 = require("../output/command-capabilities");
var RuntimeExitCode;
(function (RuntimeExitCode) {
    RuntimeExitCode[RuntimeExitCode["SUCCESS"] = 0] = "SUCCESS";
    RuntimeExitCode[RuntimeExitCode["WARNINGS_AS_FAILURE"] = 1] = "WARNINGS_AS_FAILURE";
    RuntimeExitCode[RuntimeExitCode["ERROR"] = 2] = "ERROR";
    RuntimeExitCode[RuntimeExitCode["USAGE"] = 3] = "USAGE";
    RuntimeExitCode[RuntimeExitCode["UNEXPECTED"] = 4] = "UNEXPECTED";
    RuntimeExitCode[RuntimeExitCode["INVALID_CONFIG"] = 5] = "INVALID_CONFIG";
    RuntimeExitCode[RuntimeExitCode["STALE_EVIDENCE"] = 6] = "STALE_EVIDENCE";
    RuntimeExitCode[RuntimeExitCode["CONCURRENCY_CONFLICT"] = 7] = "CONCURRENCY_CONFLICT";
    RuntimeExitCode[RuntimeExitCode["UNAVAILABLE_DEPENDENCY"] = 8] = "UNAVAILABLE_DEPENDENCY";
    RuntimeExitCode[RuntimeExitCode["PERMISSION_FAILURE"] = 9] = "PERMISSION_FAILURE";
    RuntimeExitCode[RuntimeExitCode["PARTIAL_MUTATION"] = 10] = "PARTIAL_MUTATION";
    RuntimeExitCode[RuntimeExitCode["UNSUPPORTED_CAPABILITY"] = 11] = "UNSUPPORTED_CAPABILITY";
})(RuntimeExitCode || (exports.RuntimeExitCode = RuntimeExitCode = {}));
exports.TERMINAL_FAILURE_CATEGORIES = [
    "invalid-input",
    "stale-evidence",
    "concurrency-conflict",
    "unavailable-dependency",
    "permission-failure",
    "partial-mutation",
    "unsupported-capability",
    "unexpected-internal-failure"
];
const EXIT_CONTRACTS = {
    [RuntimeExitCode.SUCCESS]: { category: "unexpected-internal-failure", exitClassification: "error", exitCode: RuntimeExitCode.SUCCESS, retryable: false },
    [RuntimeExitCode.WARNINGS_AS_FAILURE]: { category: "unexpected-internal-failure", exitClassification: "error", exitCode: RuntimeExitCode.WARNINGS_AS_FAILURE, retryable: false },
    [RuntimeExitCode.ERROR]: { category: "unexpected-internal-failure", exitClassification: "error", exitCode: RuntimeExitCode.ERROR, retryable: false },
    [RuntimeExitCode.USAGE]: { category: "invalid-input", exitClassification: "usage", exitCode: RuntimeExitCode.USAGE, retryable: true },
    [RuntimeExitCode.UNEXPECTED]: { category: "unexpected-internal-failure", exitClassification: "unexpected", exitCode: RuntimeExitCode.UNEXPECTED, retryable: false },
    [RuntimeExitCode.INVALID_CONFIG]: { category: "invalid-input", exitClassification: "invalid-config", exitCode: RuntimeExitCode.INVALID_CONFIG, retryable: true },
    [RuntimeExitCode.STALE_EVIDENCE]: { category: "stale-evidence", exitClassification: "stale-evidence", exitCode: RuntimeExitCode.STALE_EVIDENCE, retryable: true },
    [RuntimeExitCode.CONCURRENCY_CONFLICT]: { category: "concurrency-conflict", exitClassification: "concurrency-conflict", exitCode: RuntimeExitCode.CONCURRENCY_CONFLICT, retryable: true },
    [RuntimeExitCode.UNAVAILABLE_DEPENDENCY]: { category: "unavailable-dependency", exitClassification: "unavailable-dependency", exitCode: RuntimeExitCode.UNAVAILABLE_DEPENDENCY, retryable: true },
    [RuntimeExitCode.PERMISSION_FAILURE]: { category: "permission-failure", exitClassification: "permission-failure", exitCode: RuntimeExitCode.PERMISSION_FAILURE, retryable: false },
    [RuntimeExitCode.PARTIAL_MUTATION]: { category: "partial-mutation", exitClassification: "partial-mutation", exitCode: RuntimeExitCode.PARTIAL_MUTATION, retryable: false },
    [RuntimeExitCode.UNSUPPORTED_CAPABILITY]: { category: "unsupported-capability", exitClassification: "unsupported-capability", exitCode: RuntimeExitCode.UNSUPPORTED_CAPABILITY, retryable: false }
};
class RuntimeError extends Error {
    exitCode;
    code;
    details;
    findings;
    category;
    exitClassification;
    retryable;
    writeOutcome;
    recoveryActions;
    constructor(options) {
        super(options.message, { cause: options.cause });
        const failure = normalizeTerminalFailureContract(options);
        this.name = "RuntimeError";
        this.code = options.code;
        this.exitCode = options.exitCode;
        this.details = options.details ?? [];
        this.findings = options.findings ?? [];
        this.category = failure.category;
        this.exitClassification = failure.exitClassification;
        this.retryable = failure.retryable;
        this.writeOutcome = failure.writeOutcome;
        this.recoveryActions = failure.recoveryActions;
    }
}
exports.RuntimeError = RuntimeError;
function normalizeTerminalFailureContract(options) {
    const expected = EXIT_CONTRACTS[options.exitCode];
    if (!expected || options.exitCode === RuntimeExitCode.SUCCESS || options.exitCode === RuntimeExitCode.WARNINGS_AS_FAILURE) {
        throw new TypeError(`Exit code ${options.exitCode} is not a terminal failure classification.`);
    }
    const recoveryActions = [...(options.recoveryActions ?? [])];
    if (recoveryActions.length > 8) {
        throw new TypeError("Terminal failure recovery actions must contain at most 8 entries.");
    }
    if (options.category !== undefined && options.category !== expected.category) {
        throw new TypeError(`Failure category '${options.category}' contradicts exit code ${options.exitCode}.`);
    }
    if (options.exitClassification !== undefined && options.exitClassification !== expected.exitClassification) {
        throw new TypeError(`Exit classification '${options.exitClassification}' contradicts exit code ${options.exitCode}.`);
    }
    if (options.retryable !== undefined && options.retryable !== expected.retryable) {
        throw new TypeError(`Retryability '${options.retryable}' contradicts exit code ${options.exitCode}.`);
    }
    const writeOutcome = options.writeOutcome ?? (expected.category === "partial-mutation" ? "partial" : "no-writes");
    if (writeOutcome === "complete") {
        throw new TypeError("A terminal failure cannot claim a completed write outcome.");
    }
    if (expected.category === "partial-mutation" && writeOutcome !== "partial") {
        throw new TypeError("Partial-mutation failures require writeOutcome 'partial'.");
    }
    if (expected.category !== "partial-mutation" && writeOutcome === "partial") {
        throw new TypeError(`Failure category '${expected.category}' cannot claim a partial write outcome.`);
    }
    if (writeOutcome === "partial" && recoveryActions.length === 0) {
        throw new TypeError("Partial-mutation failures require explicit partial-state recovery evidence.");
    }
    return {
        ...expected,
        writeOutcome,
        recoveryActions
    };
}
function getRuntimeErrorEnvelopeFields(error) {
    return {
        category: error.category,
        exitClassification: error.exitClassification,
        exitCode: error.exitCode,
        retryable: error.retryable,
        writeOutcome: error.writeOutcome,
        recoveryActions: error.recoveryActions
    };
}
function createDescriptorBackedRecoveryAction(options) {
    const descriptor = (0, command_capabilities_1.getRegisteredCommandDescriptor)(options.command);
    if (descriptor === null) {
        throw new TypeError(`Recovery command '${options.command}' has no authoritative descriptor.`);
    }
    if (descriptor.availability !== "callable") {
        throw new TypeError(`Recovery command '${options.command}' is ${descriptor.availability} and cannot be emitted as an executable action.`);
    }
    const expectedPrefix = ["day-shift", ...descriptor.command.split(/\s+/)];
    if (!expectedPrefix.every((segment, index) => options.argv[index] === segment)) {
        throw new TypeError(`Recovery argv must start with '${expectedPrefix.join(" ")}'.`);
    }
    const missingInputs = findMissingRequiredRecoveryInputs(descriptor, options.argv.slice(expectedPrefix.length));
    if (missingInputs.length > 0) {
        return createNonCallableRecoveryAction({
            id: `recovery.${descriptor.command.replace(/\s+/g, ".")}.incomplete`,
            command: descriptor.command,
            explanation: requireRecoveryText(options.explanation, "explanation"),
            reason: `Recovery argv is missing required input${missingInputs.length === 1 ? "" : "s"}: ${missingInputs.join(", ")}.`
        });
    }
    return {
        id: `recovery.${descriptor.command.replace(/\s+/g, ".")}`,
        callable: true,
        explanation: requireRecoveryText(options.explanation, "explanation"),
        command: descriptor.command,
        argv: [...options.argv],
        writeCapable: descriptor.writeCapable,
        effects: { ...descriptor.effects },
        authorization: descriptor.authorization,
        confirmation: descriptor.confirmation,
        selectionRequired: descriptor.selectionRequired,
        idempotency: descriptor.idempotency,
        preconditions: descriptor.preconditions.map((precondition) => ({
            id: precondition.id,
            description: precondition.description,
            status: precondition.kind !== "selection" || Boolean(options.selectedArtifactPath) ? "satisfied" : "blocked",
            expected: precondition.required ? "required evidence present" : "optional evidence",
            observed: precondition.kind === "selection" ? options.selectedArtifactPath ?? null : "evaluated by failure producer"
        })),
        transition: {
            id: descriptor.transition.id,
            kind: descriptor.transition.kind,
            expectedPostconditions: descriptor.transition.expectedPostconditions.map((postcondition) => ({ ...postcondition }))
        }
    };
}
function findMissingRequiredRecoveryInputs(descriptor, argv) {
    const requiredArguments = descriptor.inputs.filter((input) => input.required && input.kind === "argument");
    const positional = argv.filter((value, index) => value !== "--" && !value.startsWith("--")
        && (index === 0 || !argv[index - 1]?.startsWith("--")));
    const missing = requiredArguments.slice(positional.length).map((input) => input.name);
    for (const input of descriptor.inputs) {
        if (!input.required || input.kind !== "option")
            continue;
        const expectedFlag = `--${input.name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`)}`;
        const flagIndex = argv.indexOf(expectedFlag);
        if (flagIndex === -1 || (input.valueType !== "boolean" && (argv[flagIndex + 1] === undefined || argv[flagIndex + 1]?.startsWith("--")))) {
            missing.push(input.name);
        }
    }
    return missing;
}
function createNonCallableRecoveryAction(options) {
    const id = requireRecoveryIdentifier(options.id);
    return {
        id,
        callable: false,
        explanation: requireRecoveryText(options.explanation, "explanation"),
        command: options.command ?? null,
        argv: null,
        writeCapable: null,
        effects: null,
        authorization: null,
        confirmation: null,
        selectionRequired: null,
        idempotency: null,
        preconditions: [{
                id: "authoritative-descriptor",
                description: "The guidance resolves to an authoritative callable command descriptor.",
                status: "blocked",
                expected: "registered command descriptor with complete arguments",
                observed: null
            }],
        transition: {
            id: `${id}:state-observed`,
            kind: "state-observed",
            expectedPostconditions: [{
                    id: "guidance-preserved",
                    description: options.expectedPostcondition ?? "Guidance is reported without claiming that recovery occurred."
                }]
        },
        nonCallableReason: requireRecoveryText(options.reason, "reason")
    };
}
function requireRecoveryText(value, label) {
    const normalized = value.trim();
    if (normalized.length === 0)
        throw new TypeError(`Recovery ${label} must be non-empty.`);
    return normalized;
}
function requireRecoveryIdentifier(value) {
    const normalized = value.trim();
    if (!/^[a-z0-9]+(?:[.:_-][a-z0-9]+)*$/.test(normalized)) {
        throw new TypeError(`Recovery action id '${value}' is invalid.`);
    }
    return normalized;
}
function createUsageError(message, details) {
    return new RuntimeError({
        message,
        code: "USAGE_ERROR",
        exitCode: RuntimeExitCode.USAGE,
        details,
        recoveryActions: [createNonCallableRecoveryAction({
                id: "recovery.correct-input",
                explanation: "Correct the rejected input using the error details, then rerun the command.",
                reason: "The shared boundary cannot infer the caller's intended replacement value or exact argv."
            })]
    });
}
function createUnsupportedOutputFormatError(options) {
    return createUsageError(`Output format must be one of: ${output_1.OUTPUT_FORMATS.join(", ")}.`, [
        `Received: ${String(options.received)}`,
        `Supported formats: ${output_1.OUTPUT_FORMATS.join(", ")}`,
        `Command boundary: ${options.commandBoundary}`,
        `Exit behavior: exits with usage code ${RuntimeExitCode.USAGE} before command work runs.`,
        `Recovery: ${options.recovery ?? "Use --format text or --format json and rerun the command."}`
    ]);
}
function createCommanderUsageError(options) {
    const message = options.error instanceof Error
        ? options.error.message
        : "Usage validation failed.";
    const details = normalizeCommanderUsageDetails(options.capturedStderr);
    return createUsageError(message, details);
}
function normalizeCommanderUsageDetails(capturedStderr) {
    const trimmedStderr = capturedStderr?.trim();
    if (!trimmedStderr) {
        return [];
    }
    // Keep the first logical error block and drop verbose help text for stable automation payloads.
    const [firstBlock] = trimmedStderr.split(/\n\s*\n/);
    return firstBlock ? [firstBlock.trim()] : [];
}
function createInvalidConfigError(message, details, findings) {
    return new RuntimeError({
        message,
        code: "INVALID_CONFIG",
        exitCode: RuntimeExitCode.INVALID_CONFIG,
        details,
        findings,
        recoveryActions: [createNonCallableRecoveryAction({
                id: "recovery.correct-config",
                explanation: "Correct the invalid local configuration using the reported findings, then rerun.",
                reason: "The shared boundary cannot infer a safe configuration edit."
            })]
    });
}
function createBlockingInvalidConfigError(options) {
    return createInvalidConfigError(options.message, [options.detail], [
        (0, output_1.createBlockingValidationFinding)({
            code: options.findingCode,
            message: options.findingMessage ?? options.detail,
            path: options.path,
            blockingReason: options.blockingReason,
            recovery: options.recovery,
            evidence: options.evidenceDetails === undefined
                ? undefined
                : {
                    category: "command-specific",
                    details: options.evidenceDetails
                }
        })
    ]);
}
function createUnexpectedError(error) {
    if (error instanceof RuntimeError) {
        return error;
    }
    if (error instanceof Error) {
        return new RuntimeError({
            message: error.message,
            code: "UNEXPECTED_ERROR",
            exitCode: RuntimeExitCode.UNEXPECTED,
            recoveryActions: [createNonCallableRecoveryAction({
                    id: "recovery.inspect-unexpected-failure",
                    explanation: "Inspect the error details and current repository state before choosing a follow-up.",
                    reason: "Unexpected failures do not have enough authoritative evidence for a callable recovery command."
                })],
            cause: error
        });
    }
    return new RuntimeError({
        message: "An unexpected runtime error occurred.",
        code: "UNEXPECTED_ERROR",
        exitCode: RuntimeExitCode.UNEXPECTED,
        recoveryActions: [createNonCallableRecoveryAction({
                id: "recovery.inspect-unexpected-failure",
                explanation: "Inspect current repository state before choosing a follow-up.",
                reason: "Unexpected failures do not have enough authoritative evidence for a callable recovery command."
            })]
    });
}
function isCommanderLifecycleSignal(error) {
    return getCommanderCode(error) === "commander.helpDisplayed" ||
        getCommanderCode(error) === "commander.version";
}
function isCommanderUsageError(error) {
    const code = getCommanderCode(error);
    return typeof code === "string" &&
        code.startsWith("commander.") &&
        code !== "commander.helpDisplayed" &&
        code !== "commander.version";
}
function getCommanderCode(error) {
    if (typeof error !== "object" || error === null || !("code" in error)) {
        return null;
    }
    const { code } = error;
    return typeof code === "string" ? code : null;
}
