"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SPEC_UNLINK_RUNTIME_SEMANTICS = void 0;
exports.createSpecUnlinkCommand = createSpecUnlinkCommand;
const errors_1 = require("../../../lib/errors");
const output_1 = require("../../../lib/output");
const spec_registry_1 = require("../../../lib/spec-registry");
const transaction_1 = require("../../../lib/spec-registry/transaction");
const path_resolution_1 = require("../../../lib/spec-registry/path-resolution");
const runtime_contract_1 = require("../../../lib/runtime/runtime-contract");
const global_format_1 = require("../global-format");
exports.SPEC_UNLINK_RUNTIME_SEMANTICS = Object.freeze({
    conditionalInputs: Object.freeze([]),
    dependentInputs: Object.freeze([]),
    conflictingInputs: Object.freeze([]),
    mutableFields: Object.freeze(['linked_slice', 'updated_at']),
    genericGuards: Object.freeze([
        Object.freeze({
            id: 'optional-link-only',
            description: 'Removes only optional linked_slice metadata while preserving spec identity, source history, lifecycle status, and planning artifacts.',
        }),
    ]),
    failureTaxonomy: Object.freeze({
        missingRequiredInput: 'invalid-input',
        invalidField: 'invalid-input',
        invalidRepositoryState: 'invalid-config',
    }),
    recoveries: Object.freeze([]),
    exactExamples: Object.freeze([Object.freeze(['day-shift', 'spec', 'unlink', 'emergent-example'])]),
});
async function createSpecUnlinkCommand() {
    const { Command } = await import('commander');
    const command = new Command('unlink');
    command
        .description('Remove stale optional linked_slice metadata without deleting planning history.')
        .argument('<id>', 'Registered spec id whose linked_slice metadata should be removed.')
        .action((entryId, _, commandInstance) => {
        const format = (0, output_1.resolveOutputFormat)((0, global_format_1.getGlobalFormat)(commandInstance));
        if (format === null) {
            throw (0, errors_1.createUsageError)('Output format must be one of: text, json.', [
                `Received: ${String((0, global_format_1.getGlobalFormat)(commandInstance))}`,
            ]);
        }
        const repositoryContext = (0, runtime_contract_1.createRepositoryContext)((0, runtime_contract_1.detectRepositoryRoot)(), false);
        const specIndexPaths = (0, path_resolution_1.resolveSpecIndexPaths)(repositoryContext);
        const currentSnapshot = (0, transaction_1.readSpecRegistrySnapshotSync)(repositoryContext.root);
        const currentIndex = currentSnapshot.document;
        const unlinkResult = (0, spec_registry_1.applySpecRegistryUnlink)({
            entryId,
            currentDocument: currentIndex,
        });
        if (!unlinkResult.ok) {
            throw new errors_1.RuntimeError({
                message: 'Spec unlink validation failed.',
                code: 'SPEC_UNLINK_VALIDATION_FAILED',
                exitCode: errors_1.RuntimeExitCode.ERROR,
                details: unlinkResult.findings.map((finding) => finding.message),
                findings: [...unlinkResult.findings],
            });
        }
        requireCommittedRegistryMutation('unlink', (0, transaction_1.commitSpecRegistryDocumentSync)(repositoryContext.root, {
            expectedRevision: currentSnapshot.revision,
            document: unlinkResult.document,
        }));
        (0, output_1.emitSuccess)({
            command: 'spec unlink',
            format,
            text: [
                'Removed linked slice metadata.',
                `Entry id: ${unlinkResult.entry.id}`,
                `Previous linked slice: ${unlinkResult.previousLinkedSlice.slice}`,
                `Previous linked slice path: ${unlinkResult.previousLinkedSlice.path}`,
            ].join('\n'),
            data: {
                repositoryRoot: repositoryContext.root,
                detectionSource: repositoryContext.detectionSource,
                specIndexPath: specIndexPaths.relativePath,
                entry: unlinkResult.entry,
                previousLinkedSlice: unlinkResult.previousLinkedSlice,
            },
        });
    });
    (0, output_1.attachCommandRuntimeSemantics)(command, exports.SPEC_UNLINK_RUNTIME_SEMANTICS);
    return command;
}
function requireCommittedRegistryMutation(operation, mutation) {
    if (mutation.outcome === 'committed')
        return;
    if (mutation.outcome === 'conflict') {
        throw new errors_1.RuntimeError({
            message: `Spec ${operation} could not commit because the registry changed concurrently.`,
            code: 'SPEC_REGISTRY_CONCURRENCY_CONFLICT',
            exitCode: errors_1.RuntimeExitCode.CONCURRENCY_CONFLICT,
            details: [mutation.conflict?.message ?? 'Refresh the registry and retry the command.'],
        });
    }
    if (mutation.outcome === 'incomplete') {
        throw new errors_1.RuntimeError({
            message: `Spec ${operation} registry transaction is incomplete.`,
            code: 'SPEC_REGISTRY_TRANSACTION_INCOMPLETE',
            exitCode: errors_1.RuntimeExitCode.PARTIAL_MUTATION,
            details: [mutation.failure ?? 'Inspect retained registry transaction evidence before recovery.'],
            recoveryActions: [(0, errors_1.createNonCallableRecoveryAction)({
                    id: 'recovery.spec-registry.transaction.inspect',
                    command: null,
                    explanation: 'Inspect the retained registry transaction before retrying the mutation.',
                    reason: mutation.recoveryPath === null
                        ? 'No callable registry recovery command is currently available.'
                        : `Retained transaction evidence: ${mutation.recoveryPath}.`,
                })],
        });
    }
    throw new errors_1.RuntimeError({
        message: `Spec ${operation} registry transaction did not write.`,
        code: 'SPEC_REGISTRY_WRITE_FAILED',
        exitCode: errors_1.RuntimeExitCode.ERROR,
        details: [mutation.failure ?? 'Refresh the registry and retry the command.'],
    });
}
