// tools/event-contracts/handlers.ts — Subcommand handler implementations
import { join } from 'node:path';
import { readFile } from 'node:fs/promises';
import { writeJsonSafe } from '../../engine/safety/atomic-writer.js';
import { projectDataDir } from '../../storage/index.js';
import { validatePayload, classifyChange, generateDualVersionStrategy, bumpVersion, buildJsonSchema, } from '../../engine/event-schema-validator.js';
import { detectPrimaryBroker, getBrokerConfig } from '../../engine/event-detector.js';
import { evolveSchema, buildSagaMermaid, errorResult } from './schema-helpers.js';
// ---------------------------------------------------------------------------
// generate-contract
// ---------------------------------------------------------------------------
export async function handleGenerateContract(args) {
    const input = args.generateContract;
    if (!input) {
        return errorResult('generate-contract requires generateContract input');
    }
    const { projectId, eventName, producer, consumers, payloadFields, specId } = input;
    const broker = input.broker ?? detectPrimaryBroker([]);
    const brokerConfig = getBrokerConfig(broker);
    const now = new Date().toISOString();
    const initialVersion = '1.0.0';
    const jsonSchema = buildJsonSchema(payloadFields);
    const schema = {
        jsonSchema,
        version: initialVersion,
        introducedAt: now,
    };
    const contract = {
        name: eventName,
        currentVersion: initialVersion,
        versions: { [initialVersion]: schema },
        producer,
        consumers,
        deliveryGuarantee: brokerConfig?.deliveryGuarantee ?? 'at-least-once',
        broker,
        updatedAt: now,
        referencedBySpecs: specId ? [specId] : [],
    };
    const contractPath = join(projectDataDir(projectId), 'event-contracts', eventName, 
    /* v8 ignore next */
    `v${initialVersion.split('.')[0] ?? '1'}.json`);
    await writeJsonSafe(contractPath, contract);
    return {
        content: [
            {
                type: 'text',
                text: JSON.stringify({
                    message: `Event contract generated for '${eventName}' v${initialVersion}`,
                    contractPath,
                    contract,
                    idempotenceRequired: brokerConfig
                        ? brokerConfig.deliveryGuarantee === 'at-least-once' ||
                            brokerConfig.deliveryGuarantee === 'exactly-once'
                        : false,
                    dlqSupported: brokerConfig ? brokerConfig.dlqSupport !== '' : false,
                }, null, 2),
            },
        ],
    };
}
// ---------------------------------------------------------------------------
// validate-contract
// ---------------------------------------------------------------------------
export async function handleValidateContract(args) {
    const input = args.validateContract;
    if (!input) {
        return errorResult('validate-contract requires validateContract input');
    }
    const { projectId, eventName, payload } = input;
    const contract = await loadLatestContract(projectId, eventName);
    if (!contract) {
        return errorResult(`No contract found for event '${eventName}'. Run generate-contract first.`);
    }
    const result = validatePayload(payload, contract);
    return {
        content: [
            {
                type: 'text',
                text: JSON.stringify({
                    eventName,
                    contractVersion: contract.currentVersion,
                    valid: result.valid,
                    violations: result.violations,
                    message: result.valid
                        ? `Payload conforms to contract v${contract.currentVersion}`
                        : `Payload has ${result.violations.length} violation(s) against contract v${contract.currentVersion}`,
                }, null, 2),
            },
        ],
        isError: !result.valid,
    };
}
// ---------------------------------------------------------------------------
// evolve-contract
// ---------------------------------------------------------------------------
export async function handleEvolveContract(args) {
    const input = args.evolveContract;
    if (!input) {
        return errorResult('evolve-contract requires evolveContract input');
    }
    const { projectId, eventName, changeDescription, addedFields, removedFields, changedFields } = input;
    const contract = await loadLatestContract(projectId, eventName);
    if (!contract) {
        return errorResult(`No contract found for event '${eventName}'. Run generate-contract first.`);
    }
    const currentSchema = contract.versions[contract.currentVersion];
    if (!currentSchema) {
        return errorResult(`Current contract version ${contract.currentVersion} has no schema`);
    }
    const newJsonSchema = evolveSchema(currentSchema.jsonSchema, addedFields, removedFields, changedFields, input.deprecatedFields);
    const changeKind = classifyChange(currentSchema.jsonSchema, newJsonSchema);
    const newVersion = bumpVersion(contract.currentVersion, changeKind);
    const now = new Date().toISOString();
    const newSchema = {
        jsonSchema: newJsonSchema,
        version: newVersion,
        introducedAt: now,
    };
    const updatedContract = {
        ...contract,
        currentVersion: newVersion,
        versions: { ...contract.versions, [newVersion]: newSchema },
        updatedAt: now,
    };
    let dualVersionStrategy = null;
    if (changeKind === 'breaking') {
        dualVersionStrategy = generateDualVersionStrategy(contract.currentVersion, newVersion, changeDescription, removedFields, changedFields);
    }
    const contractPath = join(projectDataDir(projectId), 'event-contracts', eventName, 
    /* v8 ignore next */
    `v${newVersion.split('.')[0] ?? '1'}.json`);
    await writeJsonSafe(contractPath, updatedContract);
    return {
        content: [
            {
                type: 'text',
                text: JSON.stringify({
                    message: `Contract evolved from v${contract.currentVersion} to v${newVersion}`,
                    changeKind,
                    oldVersion: contract.currentVersion,
                    newVersion,
                    contractPath,
                    dualVersionStrategy,
                    warning: changeKind === 'breaking'
                        ? /* v8 ignore next */
                            `BREAKING CHANGE: Dual-version emission required until ${dualVersionStrategy?.dualEmissionEndAt ?? 'TBD'}. Producer must emit both v${contract.currentVersion} and v${newVersion} during transition.`
                        : undefined,
                }, null, 2),
            },
        ],
    };
}
// ---------------------------------------------------------------------------
// saga-spec
// ---------------------------------------------------------------------------
export async function handleSagaSpec(args) {
    const input = args.sagaSpec;
    if (!input) {
        return errorResult('saga-spec requires sagaSpec input');
    }
    const { projectId, sagaId, description, coordinationStyle, steps, orchestratorService, specId } = input;
    const missingCompensation = steps.filter((s) => !s.compensationEvent || s.compensationEvent === '');
    if (missingCompensation.length > 0) {
        return errorResult(`Saga validation failed: steps without compensationEvent: ${missingCompensation.map((s) => s.id).join(', ')}. Every Saga step must have a compensation event.`);
    }
    const saga = {
        id: sagaId,
        description,
        coordinationStyle,
        steps,
        orchestratorService,
        referencedBySpecs: specId ? [specId] : [],
    };
    const sagaPath = join(projectDataDir(projectId), 'event-contracts', 'sagas', `${sagaId}.json`);
    await writeJsonSafe(sagaPath, saga);
    const mermaid = buildSagaMermaid(saga);
    return {
        content: [
            {
                type: 'text',
                text: JSON.stringify({
                    message: `Saga contract generated: ${sagaId}`,
                    sagaPath,
                    saga,
                    stateDiagram: mermaid,
                }, null, 2),
            },
        ],
    };
}
// ---------------------------------------------------------------------------
// Internal I/O helper
// ---------------------------------------------------------------------------
async function loadLatestContract(projectId, eventName) {
    for (let major = 99; major >= 1; major--) {
        const contractPath = join(projectDataDir(projectId), 'event-contracts', eventName, `v${major}.json`);
        try {
            const raw = await readFile(contractPath, 'utf-8');
            return JSON.parse(raw);
        }
        catch {
            continue;
        }
    }
    return null;
}
//# sourceMappingURL=handlers.js.map