"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTaskReadinessAuthorizeCommand = createTaskReadinessAuthorizeCommand;
exports.createTaskLifecycleWriteCommand = createTaskLifecycleWriteCommand;
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const node_util_1 = require("node:util");
const errors_1 = require("../../../lib/errors");
const output_1 = require("../../../lib/output");
const task_lifecycle_transitions_1 = require("../../../lib/planning/task-lifecycle-transitions");
const markdown_link_materialization_1 = require("../../../lib/planning/markdown-link-materialization");
const cli_constants_1 = require("../../../lib/config/cli-constants");
const runtime_contract_1 = require("../../../lib/runtime/runtime-contract");
const global_format_1 = require("../global-format");
async function createTaskReadinessAuthorizeCommand() {
    return createTaskLifecycleWriteCommand('readiness-authorize');
}
async function createTaskLifecycleWriteCommand(kind) {
    const { Command } = await import('commander');
    const command = new Command(kind);
    command.description(description(kind));
    command.requiredOption('--task-definition <path>', 'Repository-relative canonical task-definition.md path.');
    addKindOptions(command, kind);
    command.option('--dry-run', 'Preview exact lifecycle writes and pair preconditions without mutation; omit to apply.');
    command.action((_, 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.');
        const repository = (0, runtime_contract_1.createRepositoryContext)((0, runtime_contract_1.detectRepositoryRoot)(), false);
        const options = commandInstance.opts();
        let request;
        try {
            request = normalizeRequest(repository.root, kind, options);
        }
        catch (error) {
            throw normalizeTransitionError(error, kind, options.taskDefinition);
        }
        let result;
        try {
            const preview = (0, task_lifecycle_transitions_1.previewTaskLifecycleTransitionSync)(request);
            result = options.dryRun === true ? preview : (0, task_lifecycle_transitions_1.applyTaskLifecycleTransitionSync)(preview);
        }
        catch (error) {
            throw normalizeTransitionError(error, kind, options.taskDefinition);
        }
        if (result.writeOutcome !== 'previewed' &&
            result.writeOutcome !== 'committed' &&
            !(result.idempotent && result.writeOutcome === 'no-write')) {
            throw createOutcomeError(result);
        }
        const implementationReviewReceipt = request.kind === 'disposition' || request.kind === 'close' ? (request.reviewReceipt ?? null) : null;
        const data = {
            ...result,
            ...(implementationReviewReceipt === null
                ? {}
                : {
                    reviewReceipt: implementationReviewReceipt,
                    reviewReceiptId: implementationReviewReceipt.reviewReceiptId,
                }),
            nextAction: nextAction(result),
        };
        (0, output_1.emitSuccess)({
            command: `task ${kind}`,
            format,
            text: renderTransitionText(result),
            data,
        });
    });
    return command;
}
function addKindOptions(command, kind) {
    if (kind === 'readiness-authorize') {
        command.requiredOption('--review-result <path>', 'Explicitly selected UTF-8 JSON file containing the complete task readiness-review result; bounded external files are supported.');
        return;
    }
    if (kind === 'review-repair') {
        command
            .requiredOption('--review-result <path>', 'Explicitly selected UTF-8 JSON file containing the complete fresh readiness-review result; bounded external files are supported.')
            .requiredOption('--attempt-id <identity>', 'Stable successor attempt identity.')
            .requiredOption('--opened-at <timestamp>', 'ISO-8601 successor opening time.')
            .requiredOption('--reason <text>', 'Non-empty post-close repair rationale.');
        return;
    }
    if (kind === 'disposition') {
        command
            .requiredOption('--disposition <value>', 'Review disposition: changes_requested, accepted, or accepted_with_follow_up.')
            .option('--review-result <path>', 'Required for accepted dispositions: bounded UTF-8 JSON implementation-summary review output containing the fresh receipt.')
            .option('--reason <text>', 'Required for changes_requested and accepted_with_follow_up.');
    }
    else if (kind === 'close') {
        command.requiredOption('--review-result <path>', 'Bounded UTF-8 JSON implementation-summary review output containing the fresh close-authorizing receipt.');
    }
    else if (kind === 'block') {
        command
            .requiredOption('--dimension <dimension>', 'Blocked dimension: implementation or review.')
            .requiredOption('--reason <text>', 'Non-empty blocker reason.')
            .requiredOption('--next-decision <text>', 'Required next decision or dependency.');
    }
    else if (kind === 'cancel') {
        command.requiredOption('--reason <text>', 'Non-empty task cancellation reason.');
    }
    if (kind !== 'close') {
        command
            .requiredOption('--recorded-by <actor>', 'Non-empty transition actor.')
            .requiredOption('--recorded-at <timestamp>', 'ISO-8601 transition time.');
    }
}
function normalizeRequest(repositoryRoot, kind, options) {
    const base = { repositoryRoot, taskDefinitionPath: options.taskDefinition };
    if (kind === 'readiness-authorize') {
        assertNoMaterializableTaskLinks(repositoryRoot, options.taskDefinition);
        return {
            ...base,
            kind,
            reviewResult: readReviewResult(repositoryRoot, options.reviewResult, options.taskDefinition),
        };
    }
    if (kind === 'review-repair') {
        assertNoMaterializableTaskLinks(repositoryRoot, options.taskDefinition);
        return {
            ...base,
            kind,
            reviewResult: readReviewResult(repositoryRoot, options.reviewResult, options.taskDefinition),
            attemptId: options.attemptId,
            openedAt: options.openedAt,
            reason: options.reason,
        };
    }
    if (kind === 'disposition') {
        if (!['changes_requested', 'accepted', 'accepted_with_follow_up'].includes(options.disposition ?? '')) {
            throw (0, errors_1.createUsageError)('--disposition must be one of: changes_requested, accepted, accepted_with_follow_up.');
        }
        return {
            ...base,
            kind,
            disposition: options.disposition,
            ...(options.disposition === 'accepted' || options.disposition === 'accepted_with_follow_up'
                ? {
                    reviewReceipt: readImplementationReviewReceipt(repositoryRoot, options.reviewResult, options.taskDefinition),
                }
                : {}),
            recordedBy: options.recordedBy,
            recordedAt: options.recordedAt,
            ...(options.reason === undefined ? {} : { reason: options.reason }),
        };
    }
    if (kind === 'block') {
        if (options.dimension !== 'implementation' && options.dimension !== 'review') {
            throw (0, errors_1.createUsageError)('--dimension must be one of: implementation, review.');
        }
        return {
            ...base,
            kind,
            dimension: options.dimension,
            reason: options.reason,
            nextDecision: options.nextDecision,
            recordedBy: options.recordedBy,
            recordedAt: options.recordedAt,
        };
    }
    if (kind === 'cancel') {
        return {
            ...base,
            kind,
            reason: options.reason,
            recordedBy: options.recordedBy,
            recordedAt: options.recordedAt,
        };
    }
    if (kind === 'close') {
        return {
            ...base,
            kind,
            reviewReceipt: readImplementationReviewReceipt(repositoryRoot, options.reviewResult, options.taskDefinition),
        };
    }
    throw (0, errors_1.createUsageError)(`Unsupported task lifecycle transition '${String(kind)}'.`);
}
function readImplementationReviewReceipt(repositoryRoot, requestedPath, taskDefinitionPath) {
    if (requestedPath === undefined || requestedPath.trim() === '') {
        throw (0, errors_1.createUsageError)('Accepted disposition and task close require --review-result with fresh implementation-summary review output.');
    }
    const selected = readReviewResult(repositoryRoot, requestedPath, taskDefinitionPath);
    if (selected !== null && typeof selected === 'object' && !Array.isArray(selected)) {
        const record = selected;
        const receipt = record.reviewReceipt !== null && typeof record.reviewReceipt === 'object' && !Array.isArray(record.reviewReceipt)
            ? record.reviewReceipt
            : record;
        return receipt;
    }
    throw (0, errors_1.createUsageError)('--review-result must contain implementation-summary review output with a complete reviewReceipt object.');
}
function readReviewResult(repositoryRoot, requestedPath, taskDefinitionPath) {
    const root = node_fs_1.default.realpathSync(repositoryRoot);
    const candidate = node_path_1.default.isAbsolute(requestedPath) ? node_path_1.default.resolve(requestedPath) : node_path_1.default.resolve(root, requestedPath);
    let canonicalPath;
    try {
        const selectedStats = node_fs_1.default.lstatSync(candidate);
        if (selectedStats.isSymbolicLink()) {
            throw createReviewResultInputError({
                code: 'TASK_REVIEW_RESULT_UNSAFE_PATH',
                message: `Review result file '${requestedPath}' is a symbolic link and cannot be selected safely.`,
                taskDefinitionPath,
            });
        }
        canonicalPath = node_fs_1.default.realpathSync(candidate);
    }
    catch (error) {
        if (error instanceof errors_1.RuntimeError)
            throw error;
        throw createReviewResultInputError({
            code: 'TASK_REVIEW_RESULT_UNREADABLE',
            message: `Review result file '${requestedPath}' is not readable.`,
            taskDefinitionPath,
            detail: errorMessage(error),
        });
    }
    let descriptor = null;
    try {
        descriptor = node_fs_1.default.openSync(canonicalPath, node_fs_1.default.constants.O_RDONLY | (node_fs_1.default.constants.O_NOFOLLOW ?? 0));
        const stats = node_fs_1.default.fstatSync(descriptor);
        if (!stats.isFile()) {
            throw createReviewResultInputError({
                code: 'TASK_REVIEW_RESULT_NON_REGULAR',
                message: '--review-result must identify a regular file.',
                taskDefinitionPath,
            });
        }
        if (stats.size > cli_constants_1.CLI_MAX_REVIEW_RESULT_BYTES) {
            throw createReviewResultInputError({
                code: 'TASK_REVIEW_RESULT_OVERSIZED',
                message: '--review-result must be no larger than 2 MiB.',
                taskDefinitionPath,
            });
        }
        const bytes = readBoundedFile(descriptor, cli_constants_1.CLI_MAX_REVIEW_RESULT_BYTES);
        if (node_fs_1.default.realpathSync(candidate) !== canonicalPath) {
            throw createReviewResultInputError({
                code: 'TASK_REVIEW_RESULT_UNSAFE_REPLACEMENT',
                message: '--review-result changed canonical identity while it was being inspected.',
                taskDefinitionPath,
            });
        }
        let contents;
        try {
            contents = new node_util_1.TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes);
        }
        catch (error) {
            throw createReviewResultInputError({
                code: 'TASK_REVIEW_RESULT_INVALID_UTF8',
                message: '--review-result must contain valid UTF-8 JSON.',
                taskDefinitionPath,
                detail: errorMessage(error),
            });
        }
        let parsed;
        try {
            parsed = JSON.parse(contents);
        }
        catch (error) {
            throw createReviewResultInputError({
                code: 'TASK_REVIEW_RESULT_MALFORMED_JSON',
                message: '--review-result must contain valid UTF-8 JSON.',
                taskDefinitionPath,
                detail: errorMessage(error),
            });
        }
        if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
            const record = parsed;
            const data = record.data !== null && typeof record.data === 'object' && !Array.isArray(record.data)
                ? record.data
                : null;
            const value = data?.reviewResult !== null && typeof data?.reviewResult === 'object' && !Array.isArray(data.reviewResult)
                ? data.reviewResult
                : (data ?? record);
            return value;
        }
        throw createReviewResultInputError({
            code: 'TASK_REVIEW_RESULT_INCOMPLETE',
            message: '--review-result must contain a complete readiness-review JSON object.',
            taskDefinitionPath,
        });
    }
    catch (error) {
        if (error instanceof errors_1.RuntimeError)
            throw error;
        const code = error instanceof RangeError ? 'TASK_REVIEW_RESULT_OVERSIZED' : 'TASK_REVIEW_RESULT_UNREADABLE';
        throw createReviewResultInputError({
            code,
            message: error instanceof RangeError
                ? '--review-result must be no larger than 2 MiB.'
                : `Review result file '${requestedPath}' could not be read safely.`,
            taskDefinitionPath,
            detail: errorMessage(error),
        });
    }
    finally {
        if (descriptor !== null)
            node_fs_1.default.closeSync(descriptor);
    }
}
function readBoundedFile(descriptor, maximumBytes) {
    const chunks = [];
    let total = 0;
    while (true) {
        const remaining = maximumBytes + 1 - total;
        if (remaining <= 0) {
            throw new RangeError('--review-result exceeded the 2 MiB read boundary.');
        }
        const buffer = Buffer.allocUnsafe(Math.min(64 * 1024, remaining));
        const bytesRead = node_fs_1.default.readSync(descriptor, buffer, 0, buffer.length, null);
        if (bytesRead === 0)
            break;
        chunks.push(buffer.subarray(0, bytesRead));
        total += bytesRead;
        if (total > maximumBytes) {
            throw new RangeError('--review-result exceeded the 2 MiB read boundary.');
        }
    }
    return Buffer.concat(chunks, total);
}
function assertNoMaterializableTaskLinks(repositoryRoot, taskDefinitionPath) {
    const inspection = (0, markdown_link_materialization_1.inspectMarkdownLinkMaterialization)({
        repositoryRoot,
        artifactPath: taskDefinitionPath,
    });
    if (inspection.candidateCount === 0)
        return;
    const previewCommand = `node dist/cli/index.js link materialize --dry-run --artifact ${taskDefinitionPath} --format json`;
    throw new errors_1.RuntimeError({
        message: 'Task readiness authorization requires link materialization first.',
        code: 'TASK_READINESS_LINK_MATERIALIZATION_REQUIRED',
        exitCode: errors_1.RuntimeExitCode.INVALID_CONFIG,
        details: [
            `Task '${taskDefinitionPath}' has ${inspection.candidateCount} safely materializable planning link candidate${inspection.candidateCount === 1 ? '' : 's'}.`,
            'No task, implementation-summary, reconciliation, operational-state, or parent artifact was written.',
            `Preview the exact rewrite first: ${previewCommand}`,
        ],
        findings: [
            {
                code: 'TASK_READINESS_LINK_MATERIALIZATION_REQUIRED',
                severity: 'error',
                message: 'Complete safe planning-link materialization before readiness authorization.',
                path: taskDefinitionPath,
                recovery: previewCommand,
                recommendedCommand: previewCommand,
                noWriteBoundary: 'Rejected readiness authorization does not write lifecycle metadata.',
            },
        ],
    });
}
function createReviewResultInputError(options) {
    const argv = [
        'day-shift',
        'task',
        'readiness-review',
        '--task-definition',
        options.taskDefinitionPath,
        '--reviewed-by',
        'operator',
        '--reviewed-at',
        new Date().toISOString(),
        '--format',
        'json',
    ];
    const command = argv.join(' ');
    return new errors_1.RuntimeError({
        message: options.message,
        code: options.code,
        exitCode: errors_1.RuntimeExitCode.INVALID_CONFIG,
        details: [
            ...(options.detail === undefined ? [] : [options.detail]),
            'The selected evidence file was opened read-only and no task-pair write was attempted.',
            `Generate fresh bounded readiness evidence with: ${command}`,
        ],
        findings: [
            {
                code: options.code,
                severity: 'error',
                message: options.message,
                path: options.taskDefinitionPath,
                recovery: command,
                recommendedCommand: command,
                noWriteBoundary: 'Invalid external readiness evidence is never mutated and cannot authorize task writes.',
            },
        ],
        recoveryActions: [
            (0, errors_1.createDescriptorBackedRecoveryAction)({
                command: 'task readiness-review',
                argv,
                explanation: 'Generate fresh revision-bound readiness evidence with every mandatory reviewer argument.',
                selectedArtifactPath: options.taskDefinitionPath,
            }),
        ],
    });
}
function normalizeTransitionError(error, kind, taskPath) {
    if (error instanceof errors_1.RuntimeError)
        return error;
    if (error instanceof task_lifecycle_transitions_1.TaskLifecycleTransitionError) {
        const exitCode = error.code === 'review-mismatch' || error.code === 'stale-preview'
            ? errors_1.RuntimeExitCode.STALE_EVIDENCE
            : error.code === 'legacy-mutation-unsupported' || error.code === 'unmaterialized-attempt'
                ? errors_1.RuntimeExitCode.UNSUPPORTED_CAPABILITY
                : errors_1.RuntimeExitCode.USAGE;
        const code = `TASK_${kind.replace(/-/g, '_').toUpperCase()}_${error.code.replace(/-/g, '_').toUpperCase()}`;
        return new errors_1.RuntimeError({
            message: `Task ${kind} was rejected.`,
            code,
            exitCode,
            details: [
                error.message,
                'No task, implementation-summary, reconciliation, operational-state, or parent artifact was written.',
                'Correct or refresh the complete pair evidence, then preview the explicit transition again.',
            ],
            findings: [
                {
                    code,
                    severity: 'error',
                    message: error.message,
                    path: taskPath,
                    noWriteBoundary: 'Rejected task lifecycle transitions do not write canonical evidence.',
                },
            ],
        });
    }
    throw error;
}
function createOutcomeError(result) {
    const details = [
        `transition: ${result.transition}`,
        `writeOutcome: ${result.writeOutcome}`,
        `intendedPaths: ${JSON.stringify(result.intendedPaths)}`,
        `changedPaths: ${JSON.stringify(result.changedPaths)}`,
        `conflicts: ${JSON.stringify(result.conflicts)}`,
        `recoveryPath: ${result.recoveryPath ?? 'null'}`,
        `failure: ${result.failure ?? 'null'}`,
    ];
    if (result.writeOutcome === 'conflict') {
        return new errors_1.RuntimeError({
            message: 'Task lifecycle preview is stale or conflicted.',
            code: 'TASK_LIFECYCLE_CONFLICT',
            exitCode: errors_1.RuntimeExitCode.CONCURRENCY_CONFLICT,
            details,
            recoveryActions: [
                (0, errors_1.createNonCallableRecoveryAction)({
                    id: 'recovery.refresh-task-lifecycle-preview',
                    command: `task ${result.transition}`,
                    explanation: 'Refresh both task-pair revisions and preview this lifecycle transition again.',
                    reason: 'The original review and transition inputs must be supplied again.',
                }),
            ],
        });
    }
    if (result.writeOutcome === 'incomplete') {
        return new errors_1.RuntimeError({
            message: 'Task lifecycle transition stopped with incomplete repository state.',
            code: 'TASK_LIFECYCLE_INCOMPLETE',
            exitCode: errors_1.RuntimeExitCode.PARTIAL_MUTATION,
            writeOutcome: 'partial',
            details,
            recoveryActions: [
                (0, errors_1.createNonCallableRecoveryAction)({
                    id: 'recovery.inspect-task-lifecycle-transaction',
                    explanation: 'Inspect retained transaction evidence before retrying or repairing the task pair.',
                    reason: result.recoveryPath ?? 'The retained transaction path was unavailable.',
                }),
            ],
        });
    }
    return new errors_1.RuntimeError({
        message: 'Task lifecycle transition produced no canonical write.',
        code: 'TASK_LIFECYCLE_NO_WRITE',
        exitCode: errors_1.RuntimeExitCode.ERROR,
        details,
    });
}
function renderTransitionText(result) {
    return [
        (0, output_1.renderTerminalHeading)(`Task ${result.transition}`, 'title').trimEnd(),
        '',
        `Task: ${result.taskId}`,
        `Task definition: ${result.taskDefinitionPath}`,
        `Implementation summary: ${result.implementationSummaryPath}`,
        `Write outcome: ${result.writeOutcome}`,
        `Intended paths: ${JSON.stringify(result.intendedPaths)}`,
        `Changed paths: ${JSON.stringify(result.changedPaths)}`,
        `Expected conditions: ${JSON.stringify(result.expected)}`,
        `Transaction ID: ${result.transactionId ?? '(none)'}`,
        `Recovery path: ${result.recoveryPath ?? '(none)'}`,
        `Next action: ${nextAction(result)}`,
    ].join('\n');
}
function nextAction(result) {
    if (result.writeOutcome === 'previewed')
        return 'Inspect both revisions and intended paths, then rerun without --dry-run to apply.';
    if (result.writeOutcome === 'committed')
        return 'Continue only through the next explicit task lifecycle command.';
    if (result.idempotent && result.writeOutcome === 'no-write') {
        return result.transition === 'readiness-authorize'
            ? 'The exact readiness authorization is already current; continue through task attempt-open.'
            : 'The exact canonical terminal pair is already present; no further close write is required.';
    }
    if (result.writeOutcome === 'conflict')
        return 'Refresh the review and transition preview against both current artifacts.';
    if (result.writeOutcome === 'incomplete')
        return 'Inspect the retained transaction recovery path before retrying.';
    return 'Inspect current pair evidence; no canonical artifact changed.';
}
function description(kind) {
    if (kind === 'readiness-authorize')
        return 'Preview or apply verified revision-bound task readiness authorization.';
    if (kind === 'review-repair')
        return 'Preview or atomically append a revision-authorized repair successor after accepted task closeout.';
    if (kind === 'disposition')
        return 'Preview or apply a current-attempt review disposition without closing the task.';
    if (kind === 'block')
        return 'Preview or apply current-attempt blocker evidence without closing the task.';
    if (kind === 'cancel')
        return 'Preview or atomically apply current-attempt cancellation evidence and task cancellation.';
    return 'Preview or apply explicit task closure after authorized completed-and-accepted evidence exists.';
}
function errorMessage(error) {
    return error instanceof Error && error.message.trim() !== '' ? error.message : 'Unknown file import error.';
}
