"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createLinkDispositionApplyCommand = createLinkDispositionApplyCommand;
const node_fs_1 = __importDefault(require("node:fs"));
const config_contract_1 = require("../../../lib/config/config-contract");
const errors_1 = require("../../../lib/errors");
const output_1 = require("../../../lib/output");
const paths_1 = require("../../../lib/paths");
const trace_link_dispositions_1 = require("../../../lib/trace-link-dispositions");
const global_format_1 = require("../global-format");
async function createLinkDispositionApplyCommand() {
    const { Command } = await import("commander");
    const command = new Command("apply");
    command
        .description("Apply migrated trace-link disposition updates through an explicit write-capable command.")
        .option("--id <id>", "Apply one migrated disposition id.")
        .option("--target <path>", "Apply one migrated disposition target path.")
        .option("--artifact <path>", "Focus apply discovery on one repository-relative planning artifact.")
        .option("--dry-run", "Preview apply evidence without writing files.")
        .option("--format <format>", "Output format. Accepted values: text, json.")
        .addHelpText("after", [
        "",
        "Boundaries:",
        "  Write-capable: applies migrated disposition updates only when write execution is supported by the selected workflow.",
        "  Dry-run: reads disposition storage and planning trace fields, but writes no files, timestamps, or apply evidence.",
        "  Write execution mutates only mechanically safe repository-local trace fields and records apply evidence after successful writes.",
        "  Retired and deleted dispositions, external targets, body-reference rewrites, and inferred replacement targets are outside apply scope.",
        "  Rollback: revert the reported changed artifact paths and .day-shift/state/trace-link-dispositions.json from version control."
    ].join("\n"))
        .action((_, commandInstance) => {
        const options = commandInstance.opts();
        const requestedFormat = options.format ?? commandInstance.optsWithGlobals().format ?? (0, global_format_1.getGlobalFormat)(commandInstance);
        const format = (0, output_1.resolveOutputFormat)(requestedFormat);
        if (format === null) {
            throw (0, errors_1.createUnsupportedOutputFormatError)({
                received: requestedFormat,
                commandBoundary: "link disposition apply command output format"
            });
        }
        const repositoryRoot = getRootCommand(commandInstance).configRepositoryContext?.root ?? process.cwd();
        const resolvedConfig = (0, config_contract_1.resolveDayShiftConfig)({
            repositoryContext: {
                root: repositoryRoot,
                detectionSource: "explicit",
                warnedAboutFallback: false
            }
        });
        const storageAbsolutePath = (0, trace_link_dispositions_1.resolveTraceLinkDispositionStoragePath)(repositoryRoot);
        if (!node_fs_1.default.existsSync(storageAbsolutePath)) {
            throw (0, errors_1.createUsageError)("Trace-link disposition storage does not exist.", [
                `Storage path: ${trace_link_dispositions_1.TRACE_LINK_DISPOSITION_STORAGE_PATH}`
            ]);
        }
        const document = (0, trace_link_dispositions_1.parseTraceLinkDispositionStorageDocument)(node_fs_1.default.readFileSync(storageAbsolutePath, "utf8"));
        const artifactPath = normalizeOptionalRepositoryPath(options.artifact, "artifact");
        const selectedDispositions = selectApplyDispositions({
            dispositions: document.dispositions,
            id: normalizeOptionalString(options.id, "id"),
            target: normalizeOptionalRepositoryPath(options.target, "target")
        });
        assertMigratedDispositions(selectedDispositions);
        const preview = (0, trace_link_dispositions_1.previewTraceLinkDispositionUpdates)({
            repositoryRoot,
            dispositionStoragePath: trace_link_dispositions_1.TRACE_LINK_DISPOSITION_STORAGE_PATH,
            dispositions: selectedDispositions,
            artifactPath,
            planningRootPath: (0, paths_1.toRepositoryRelativePath)(repositoryRoot, resolvedConfig.derived.planningRootPath),
            specIndexPath: (0, paths_1.toRepositoryRelativePath)(repositoryRoot, resolvedConfig.derived.specIndexPath)
        });
        if (options.dryRun === true) {
            const dryRun = createDryRunResult(preview);
            const rollbackGuidance = createRollbackGuidance({
                dryRun: true,
                changedArtifactPaths: [],
                storagePath: trace_link_dispositions_1.TRACE_LINK_DISPOSITION_STORAGE_PATH,
                applyEvidencePersisted: false
            });
            (0, output_1.emitSuccess)({
                command: "link disposition apply",
                format,
                text: renderApplyDryRunText(dryRun, rollbackGuidance),
                data: {
                    repositoryRoot,
                    readOnly: true,
                    writeCapable: true,
                    dryRun: true,
                    localOnly: true,
                    storagePath: trace_link_dispositions_1.TRACE_LINK_DISPOSITION_STORAGE_PATH,
                    artifact: artifactPath,
                    applyEvidencePersisted: false,
                    rollbackGuidance,
                    ...dryRun
                }
            });
            return;
        }
        const appliedAt = new Date().toISOString();
        let applyResult;
        try {
            applyResult = (0, trace_link_dispositions_1.applyTraceLinkDispositionPreviewMutations)({
                repositoryRoot,
                storageDocument: document,
                preview,
                appliedAt
            });
        }
        catch (error) {
            throw (0, errors_1.createUsageError)("Trace-link disposition apply was blocked before writing files.", [
                error instanceof Error ? error.message : String(error),
                "No files were written."
            ]);
        }
        node_fs_1.default.writeFileSync(storageAbsolutePath, (0, trace_link_dispositions_1.serializeTraceLinkDispositionStorageDocument)({
            schemaVersion: document.schemaVersion,
            dispositions: applyResult.dispositions
        }), "utf8");
        const changedPaths = [...applyResult.changedArtifactPaths, trace_link_dispositions_1.TRACE_LINK_DISPOSITION_STORAGE_PATH];
        const rollbackGuidance = createRollbackGuidance({
            dryRun: false,
            changedArtifactPaths: applyResult.changedArtifactPaths,
            storagePath: trace_link_dispositions_1.TRACE_LINK_DISPOSITION_STORAGE_PATH,
            applyEvidencePersisted: true
        });
        (0, output_1.emitSuccess)({
            command: "link disposition apply",
            format,
            text: renderApplyText({
                changedPaths,
                skippedPaths: applyResult.skippedPaths,
                conflictPaths: applyResult.conflictPaths,
                unresolvedPaths: applyResult.unresolvedPaths,
                preview,
                rollbackGuidance
            }),
            data: {
                repositoryRoot,
                readOnly: false,
                writeCapable: true,
                dryRun: false,
                localOnly: true,
                storagePath: trace_link_dispositions_1.TRACE_LINK_DISPOSITION_STORAGE_PATH,
                artifact: artifactPath,
                selectedDispositionCount: preview.selectedDispositionCount,
                proposalCount: preview.proposals.length,
                changedPaths,
                changedArtifactPaths: applyResult.changedArtifactPaths,
                skippedPaths: applyResult.skippedPaths,
                conflictPaths: applyResult.conflictPaths,
                unresolvedPaths: applyResult.unresolvedPaths,
                applyEvidencePersisted: true,
                rollbackGuidance,
                appliedAt,
                preview
            }
        });
    });
    return command;
}
function createDryRunResult(preview) {
    return {
        selectedDispositionCount: preview.selectedDispositionCount,
        proposalCount: preview.proposals.length,
        changedPaths: [],
        skippedPaths: uniqueEvidencePaths(preview.skipped),
        conflictPaths: uniqueEvidencePaths(preview.conflicts),
        unresolvedPaths: uniqueEvidencePaths(preview.unresolved),
        preview
    };
}
function selectApplyDispositions(options) {
    if (options.id !== undefined && options.target !== null && options.target !== undefined) {
        throw (0, errors_1.createUsageError)("Use either --id or --target for link disposition apply, not both.");
    }
    if (options.id !== undefined) {
        const disposition = options.dispositions.find((record) => record.id === options.id);
        if (disposition === undefined) {
            throw (0, errors_1.createUsageError)(`Trace-link disposition '${options.id}' was not found.`);
        }
        return [disposition];
    }
    if (options.target !== null && options.target !== undefined) {
        const disposition = options.dispositions.find((record) => record.target.normalizedPath === options.target);
        if (disposition === undefined) {
            throw (0, errors_1.createUsageError)(`Trace-link disposition target '${options.target}' was not found.`);
        }
        return [disposition];
    }
    throw (0, errors_1.createUsageError)("Provide --id or --target for link disposition apply.");
}
function assertMigratedDispositions(dispositions) {
    const unsupported = dispositions.filter((disposition) => disposition.status !== "migrated");
    if (unsupported.length > 0) {
        throw (0, errors_1.createUsageError)("link disposition apply only supports migrated dispositions as rewrite sources.", [
            `Unsupported disposition ids: ${unsupported.map((disposition) => disposition.id).join(", ")}`
        ]);
    }
}
function createRollbackGuidance(options) {
    if (options.dryRun) {
        return {
            summary: "Dry-run only; no rollback is required because no files were written.",
            changedArtifactPaths: [],
            storagePath: options.storagePath,
            applyEvidencePersisted: false,
            commands: []
        };
    }
    return {
        summary: "To roll back this apply, revert the reported changed paths from version control; artifact changes and storage apply evidence are separate changed paths.",
        changedArtifactPaths: options.changedArtifactPaths,
        storagePath: options.storagePath,
        applyEvidencePersisted: options.applyEvidencePersisted,
        commands: []
    };
}
function renderApplyDryRunText(result, rollbackGuidance) {
    const lines = [
        "Trace-link disposition apply dry-run",
        "Write-capable command: true",
        "Dry-run: true",
        `Selected dispositions: ${result.selectedDispositionCount}`,
        `Proposed updates: ${result.proposalCount}`,
        ""
    ];
    if (result.preview.proposals.length === 0) {
        lines.push("Proposed migrated-target updates: none.");
    }
    else {
        lines.push("Proposed migrated-target updates:");
        for (const proposal of result.preview.proposals) {
            lines.push(`- ${proposal.sourceArtifactPath} ${proposal.traceField}: ${proposal.oldTargetPath} -> ${proposal.newTargetPath}`);
        }
    }
    if (result.skippedPaths.length > 0) {
        lines.push("", `Skipped paths: ${result.skippedPaths.join(", ")}`);
    }
    if (result.unresolvedPaths.length > 0) {
        lines.push("", `Unresolved paths: ${result.unresolvedPaths.join(", ")}`);
    }
    lines.push("", `Apply evidence persisted: ${String(rollbackGuidance.applyEvidencePersisted)}`, `Rollback guidance: ${rollbackGuidance.summary}`, "No files were written.");
    return `${lines.join("\n")}\n`;
}
function renderApplyText(result) {
    const lines = [
        "Trace-link disposition apply",
        "Write-capable command: true",
        "Dry-run: false",
        `Selected dispositions: ${result.preview.selectedDispositionCount}`,
        `Proposed updates: ${result.preview.proposals.length}`,
        `Changed paths: ${result.changedPaths.length === 0 ? "none" : result.changedPaths.join(", ")}`,
        `Apply evidence persisted: ${String(result.rollbackGuidance.applyEvidencePersisted)}`
    ];
    if (result.skippedPaths.length > 0) {
        lines.push(`Skipped paths: ${result.skippedPaths.join(", ")}`);
    }
    if (result.conflictPaths.length > 0) {
        lines.push(`Conflict paths: ${result.conflictPaths.join(", ")}`);
    }
    if (result.unresolvedPaths.length > 0) {
        lines.push(`Unresolved paths: ${result.unresolvedPaths.join(", ")}`);
    }
    lines.push(`Rollback guidance: ${result.rollbackGuidance.summary}`);
    for (const command of result.rollbackGuidance.commands) {
        lines.push(`Rollback command: ${command}`);
    }
    return `${lines.join("\n")}\n`;
}
function uniqueEvidencePaths(evidence) {
    const paths = evidence.map((item) => item.sourceArtifactPath ?? item.dispositionTargetPath ?? item.dispositionId)
        .filter((value) => value !== undefined);
    return [...new Set(paths)];
}
function normalizeOptionalRepositoryPath(input, fieldName) {
    if (input === undefined || input === null || input === "") {
        return null;
    }
    if (typeof input !== "string") {
        throw (0, errors_1.createUsageError)(`Link disposition apply ${fieldName} must be a repository-relative path.`, []);
    }
    try {
        return (0, paths_1.parseRepositoryRelativePath)(input).normalizedPath;
    }
    catch (error) {
        throw (0, errors_1.createUsageError)(`Link disposition apply ${fieldName} must be a repository-relative path.`, [error instanceof Error ? error.message : String(error)]);
    }
}
function normalizeOptionalString(input, fieldName) {
    if (input === undefined || input === null || input === "") {
        return undefined;
    }
    if (typeof input !== "string") {
        throw (0, errors_1.createUsageError)(`Link disposition apply ${fieldName} must be a string.`, []);
    }
    return input;
}
function getRootCommand(commandInstance) {
    let current = commandInstance;
    while (current.parent !== null && current.parent !== undefined) {
        current = current.parent;
    }
    return current;
}
