import { readFile, writeFile } from 'node:fs/promises';
import GenesisEquivalenceComparisonBasis from "../../../../src/domain/migrations/GenesisEquivalenceComparisonBasis.js";
import DryRunGraphModelMigrationPlanner from "../../../../src/domain/migrations/DryRunGraphModelMigrationPlanner.js";
import { parseGraphModelMigrationDryRunRequest } from "../../../../src/infrastructure/adapters/GraphModelMigrationDryRunRequestJsonAdapter.js";
import { parseGraphModelMigrationFinalizationRequest } from "../../../../src/infrastructure/adapters/GraphModelMigrationFinalizationRequestJsonAdapter.js";
import { parseV17GoldenGraphFixtureManifestJson } from "./V17GoldenGraphFixtureManifestJsonAdapter.js";
import { runGraphModelMigrationCommand } from "./GraphModelMigrationCommand.js";
import { formatGraphModelMigrationCommandReport } from "./GraphModelMigrationCommandReport.js";
import { createGraphModelMigrationProductionRuntimeConformanceProvider } from "./GraphModelMigrationProductionRuntimeReplayProvider.js";
import { buildV17RestoredPublicReadLegacyReading } from "./V17RestoredPublicReadLegacyReadingBuilder.js";
import { createV17GoldenFixtureScratchReadingProvider } from "./V17GoldenGraphFixtureWetRunHarness.js";
import { GraphModelMigrationCommandCliArgumentError, graphModelMigrationCommandUsage, parseGraphModelMigrationCommandCliArgs, } from "./GraphModelMigrationCommandCliArgs.js";
export { GraphModelMigrationCommandCliArgumentError, GraphModelMigrationCommandCliArgs, graphModelMigrationCommandUsage, parseGraphModelMigrationCommandCliArgs, } from "./GraphModelMigrationCommandCliArgs.js";
export class GraphModelMigrationCommandCliResult {
    exitCode;
    stdout;
    stderr;
    constructor(exitCode, stdout, stderr) {
        this.exitCode = exitCode;
        this.stdout = stdout;
        this.stderr = stderr;
        Object.freeze(this);
    }
}
/** Runs the v18 graph-model migration command wrapper. */
export async function runGraphModelMigrationCommandCli(argv) {
    const args = parseGraphModelMigrationCommandCliArgs(argv);
    if (args.helpRequested) {
        return new GraphModelMigrationCommandCliResult(0, `${graphModelMigrationCommandUsage()}\n`, '');
    }
    requireCommandArgs(args);
    const requestText = await readFile(requireString(args.requestPath, '--request'), 'utf8');
    const legacyManifestText = await readFile(requireString(args.legacyFixtureManifestPath, '--legacy-fixture-manifest'), 'utf8');
    const dryRunRequest = parseGraphModelMigrationDryRunRequest(requestText);
    const legacyManifest = parseV17GoldenGraphFixtureManifestJson(legacyManifestText);
    const finalizationRequest = args.finalizationRequestPath === null
        ? null
        : parseGraphModelMigrationFinalizationRequest(await readFile(args.finalizationRequestPath, 'utf8'));
    const preflightPlan = new DryRunGraphModelMigrationPlanner().plan(dryRunRequest);
    if (preflightPlan.hasFatalErrors() || preflightPlan.manifest === null) {
        return new GraphModelMigrationCommandCliResult(1, preflightFailureReport(preflightPlan), '');
    }
    const repositoryPath = requireString(args.repositoryPath, '--repo');
    const scratchRefName = requireString(args.scratchRefName, '--scratch-ref');
    const result = await runGraphModelMigrationCommand({
        repositoryPath,
        dryRunRequest,
        scratchRefName,
        equivalenceBasis: new GenesisEquivalenceComparisonBasis({
            legacyBasis: preflightPlan.manifest.sourceBasis,
            migratedBasis: preflightPlan.manifest.targetBasis,
        }),
        legacyReading: null,
        scratchReading: null,
        readingProviders: {
            legacyReading: async () => await buildV17RestoredPublicReadLegacyReading({
                repositoryPath,
                manifest: legacyManifest,
            }),
            scratchReading: createV17GoldenFixtureScratchReadingProvider({
                sourceRepositoryPath: repositoryPath,
                manifest: legacyManifest,
                runtimeRepositoryPath: null,
            }),
        },
        finalization: finalizationOptions(finalizationRequest, repositoryPath, legacyManifest.graphId),
    });
    const report = formatGraphModelMigrationCommandReport(result);
    if (args.reportOutPath !== null) {
        await writeFile(args.reportOutPath, report, 'utf8');
    }
    return new GraphModelMigrationCommandCliResult(commandExitCode(result), report, '');
}
function finalizationOptions(request, repositoryPath, graphId) {
    if (request === null) {
        return null;
    }
    return {
        liveRefName: request.liveRefName,
        expectedLiveHead: requireFinalizationString(request.expectedLiveHead, 'expectedLiveHead'),
        archiveRefName: requireFinalizationString(request.archiveRefName, 'archiveRefName'),
        confirmation: request.confirmation,
        runtimeConformance: createGraphModelMigrationProductionRuntimeConformanceProvider({
            sourceRepositoryPath: repositoryPath,
            graphId,
        }),
        reviewedRequest: request,
    };
}
function commandExitCode(result) {
    if (!result.dryRunPlan.hasFatalErrors()
        && !result.loweringResult.hasFatalErrors()
        && result.scratchWriteResult !== null
        && !result.scratchWriteResult.hasFatalErrors()
        && result.gateResult !== null
        && result.gateResult.allowsPromotion()
        && (result.finalizationResult === null || result.finalizationResult.finalized())) {
        return 0;
    }
    return 1;
}
function preflightFailureReport(plan) {
    return [
        'git-warp v18 graph-model migration report',
        'dryRun: blocked',
        `plannedOperations: ${plan.plannedOperations.length}`,
        ...fatalNoticeLines(plan.fatalErrors),
    ].join('\n');
}
function fatalNoticeLines(fatalErrors) {
    const lines = ['fatalErrors:'];
    for (const notice of fatalErrors) {
        lines.push(`- ${notice.code}: ${notice.message}`);
    }
    return Object.freeze(lines);
}
function requireCommandArgs(args) {
    requireString(args.repositoryPath, '--repo');
    requireString(args.requestPath, '--request');
    requireString(args.legacyFixtureManifestPath, '--legacy-fixture-manifest');
    requireString(args.scratchRefName, '--scratch-ref');
}
function requireString(value, flag) {
    if (value === null) {
        throw new GraphModelMigrationCommandCliArgumentError(`${flag} is required`);
    }
    return value;
}
function requireFinalizationString(value, label) {
    if (value === null) {
        throw new GraphModelMigrationCommandCliArgumentError(`${label} is required in finalization request`);
    }
    return value;
}
