import GenesisEquivalenceComparisonBasis from "../../domain/migrations/GenesisEquivalenceComparisonBasis.js";
import GenesisEquivalenceGateResult from "../../domain/migrations/GenesisEquivalenceGateResult.js";
import GenesisEquivalenceProofSuccess from "../../domain/migrations/GenesisEquivalenceProofSuccess.js";
import GenesisEquivalenceProofSummary from "../../domain/migrations/GenesisEquivalenceProofSummary.js";
import GraphModelMigrationBasis from "../../domain/migrations/GraphModelMigrationBasis.js";
import GraphModelMigrationFinalizationConfirmation from "../../domain/migrations/GraphModelMigrationFinalizationConfirmation.js";
import GraphModelMigrationFinalizationRequest from "../../domain/migrations/GraphModelMigrationFinalizationRequest.js";
import GraphModelMigrationNotice from "../../domain/migrations/GraphModelMigrationNotice.js";
import GraphModelMigrationRuntimeConformanceResult from "../../domain/migrations/GraphModelMigrationRuntimeConformanceResult.js";
import GraphModelMigrationScratchRef from "../../domain/migrations/GraphModelMigrationScratchRef.js";
import AdapterValidationError from "../../domain/errors/AdapterValidationError.js";
import WarpError from "../../domain/errors/WarpError.js";
const REQUEST_KEYS = Object.freeze([
    'liveRefName',
    'expectedLiveHead',
    'observedLiveHead',
    'scratchRefName',
    'scratchHead',
    'archiveRefName',
    'confirmationToken',
    'equivalence',
    'runtimeReplay',
]);
const CONFIRMATION_KEYS = Object.freeze(['confirmationToken']);
const EQUIVALENCE_KEYS = Object.freeze([
    'legacyBasis',
    'migratedBasis',
    'legacyFactCount',
    'migratedFactCount',
    'mismatchCount',
]);
const BASIS_KEYS = Object.freeze(['graphId', 'basisId']);
const RUNTIME_REPLAY_KEYS = Object.freeze([
    'scratchRefName',
    'scratchHead',
    'status',
    'witness',
    'fatalErrors',
]);
const NOTICE_KEYS = Object.freeze(['kind', 'code', 'message']);
/** Parses finalization confirmation JSON into a runtime-backed confirmation noun. */
export function parseGraphModelMigrationFinalizationConfirmation(raw) {
    return parseDomainValue('finalization confirmation', () => {
        const envelope = requireJsonObject(parseJson(raw, 'finalization confirmation'), 'finalizationConfirmation');
        rejectUnknownKeys(envelope, CONFIRMATION_KEYS, 'finalizationConfirmation');
        return new GraphModelMigrationFinalizationConfirmation({
            token: readRequiredString(envelope, 'finalizationConfirmation.confirmationToken', 'confirmationToken'),
        });
    });
}
/** Parses finalization request JSON into a runtime-backed safety request. */
export function parseGraphModelMigrationFinalizationRequest(raw) {
    return parseDomainValue('finalization request', () => requestFromJson(parseJson(raw)));
}
function parseDomainValue(label, parser) {
    try {
        return parser();
    }
    catch (error) {
        if (error instanceof AdapterValidationError) {
            throw error;
        }
        if (error instanceof WarpError) {
            throw new AdapterValidationError(`Graph model migration ${label} is invalid: ${error.message}`, { context: { causeCode: error.code, causeMessage: error.message } });
        }
        throw error;
    }
}
function requestFromJson(value) {
    const request = requireJsonObject(value, 'finalizationRequest');
    rejectUnknownKeys(request, REQUEST_KEYS, 'finalizationRequest');
    return new GraphModelMigrationFinalizationRequest({
        liveRefName: readRequiredString(request, 'finalizationRequest.liveRefName', 'liveRefName'),
        expectedLiveHead: readRequiredString(request, 'finalizationRequest.expectedLiveHead', 'expectedLiveHead'),
        observedLiveHead: readRequiredString(request, 'finalizationRequest.observedLiveHead', 'observedLiveHead'),
        scratchRef: new GraphModelMigrationScratchRef({
            refName: readRequiredString(request, 'finalizationRequest.scratchRefName', 'scratchRefName'),
        }),
        scratchHead: readRequiredString(request, 'finalizationRequest.scratchHead', 'scratchHead'),
        archiveRefName: readRequiredString(request, 'finalizationRequest.archiveRefName', 'archiveRefName'),
        confirmation: new GraphModelMigrationFinalizationConfirmation({
            token: readRequiredString(request, 'finalizationRequest.confirmationToken', 'confirmationToken'),
        }),
        gateResult: readPassedGateResult(request),
        runtimeConformance: readRuntimeReplay(request),
    });
}
function readPassedGateResult(source) {
    const equivalence = readRequiredObject(source, 'equivalence');
    rejectUnknownKeys(equivalence, EQUIVALENCE_KEYS, 'equivalence');
    const basis = new GenesisEquivalenceComparisonBasis({
        legacyBasis: readBasis(equivalence, 'legacyBasis'),
        migratedBasis: readBasis(equivalence, 'migratedBasis'),
    });
    const summary = new GenesisEquivalenceProofSummary({
        basis,
        legacyFactCount: readRequiredSafeInteger(equivalence, 'equivalence.legacyFactCount', 'legacyFactCount'),
        migratedFactCount: readRequiredSafeInteger(equivalence, 'equivalence.migratedFactCount', 'migratedFactCount'),
        mismatchCount: readRequiredSafeInteger(equivalence, 'equivalence.mismatchCount', 'mismatchCount'),
    });
    return new GenesisEquivalenceGateResult({
        proofResult: new GenesisEquivalenceProofSuccess({ basis, summary }),
        divergenceReport: null,
        fatalErrors: [],
    });
}
function readBasis(source, key) {
    const basis = readRequiredObject(source, key);
    rejectUnknownKeys(basis, BASIS_KEYS, key);
    return new GraphModelMigrationBasis({
        graphId: readRequiredString(basis, `${key}.graphId`, 'graphId'),
        basisId: readRequiredString(basis, `${key}.basisId`, 'basisId'),
    });
}
function readRuntimeReplay(source) {
    const runtimeReplay = readRequiredObject(source, 'runtimeReplay');
    rejectUnknownKeys(runtimeReplay, RUNTIME_REPLAY_KEYS, 'runtimeReplay');
    return new GraphModelMigrationRuntimeConformanceResult({
        scratchRef: new GraphModelMigrationScratchRef({
            refName: readRequiredString(runtimeReplay, 'runtimeReplay.scratchRefName', 'scratchRefName'),
        }),
        scratchHead: readRequiredString(runtimeReplay, 'runtimeReplay.scratchHead', 'scratchHead'),
        status: readRuntimeReplayStatus(runtimeReplay, 'runtimeReplay.status', 'status'),
        witness: readRequiredString(runtimeReplay, 'runtimeReplay.witness', 'witness'),
        fatalErrors: readFatalNotices(runtimeReplay),
    });
}
function readFatalNotices(source) {
    return readObjectArray(source, 'fatalErrors').map((notice, index) => {
        const label = `fatalErrors[${index}]`;
        rejectUnknownKeys(notice, NOTICE_KEYS, label);
        return new GraphModelMigrationNotice({
            kind: readNoticeKind(notice, `${label}.kind`, 'kind'),
            code: readRequiredString(notice, `${label}.code`, 'code'),
            message: readRequiredString(notice, `${label}.message`, 'message'),
        });
    });
}
function parseJson(raw, label = 'finalization request') {
    try {
        return JSON.parse(raw);
    }
    catch {
        throw new AdapterValidationError(`Graph model migration ${label} JSON must be valid JSON`);
    }
}
function readRequiredObject(source, key) {
    return requireJsonObject(readRequiredValue(source, key), key);
}
function readObjectArray(source, key) {
    const value = readRequiredValue(source, key);
    if (!Array.isArray(value)) {
        throw new AdapterValidationError(`Graph model migration finalization request field "${key}" must be an array`);
    }
    const objects = [];
    value.forEach((entry, index) => {
        objects.push(requireJsonObject(entry, `${key}[${index}]`));
    });
    return Object.freeze(objects);
}
function readRequiredString(source, label, key) {
    const value = readRequiredValue(source, key);
    if (typeof value !== 'string' || value.length === 0) {
        throw new AdapterValidationError(`Graph model migration finalization request field "${label}" must be a non-empty string`);
    }
    return value;
}
function readRequiredSafeInteger(source, label, key) {
    const value = readRequiredValue(source, key);
    if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0) {
        throw new AdapterValidationError(`Graph model migration finalization request field "${label}" must be a non-negative safe integer`);
    }
    return value;
}
function readRuntimeReplayStatus(source, label, key) {
    const value = readRequiredValue(source, key);
    if (value === 'passed' || value === 'failed') {
        return value;
    }
    throw new AdapterValidationError(`Graph model migration finalization request field "${label}" must be passed or failed`);
}
function readNoticeKind(source, label, key) {
    const value = readRequiredValue(source, key);
    if (value === 'warning' || value === 'fatal') {
        return value;
    }
    throw new AdapterValidationError(`Graph model migration finalization request field "${label}" must be warning or fatal`);
}
function readRequiredValue(source, key) {
    const value = source[key];
    if (value === undefined) {
        throw new AdapterValidationError(`Graph model migration finalization request field "${key}" is required`);
    }
    return value;
}
function requireJsonObject(value, label) {
    if (!isJsonObject(value)) {
        throw new AdapterValidationError(`Graph model migration finalization request field "${label}" must be an object`);
    }
    return value;
}
function isJsonObject(value) {
    return value !== null && typeof value === 'object' && !Array.isArray(value);
}
function rejectUnknownKeys(source, allowed, label) {
    for (const key of Object.keys(source)) {
        if (!allowed.includes(key)) {
            throw new AdapterValidationError(`Graph model migration finalization request field "${label}.${key}" is not allowed`);
        }
    }
}
