/**
 * ScenarioCase → API-level test file translator.
 *
 * Renders cases into a single vitest test file that drives the realized
 * backend's CURVED REST endpoints via `@specverse/engines/scenario-harness`.
 * The translator is intentionally dumb: each step maps to a harness call.
 * No HTTP/route knowledge lives here — that's all in the harness, which
 * derives routes from `@specverse/types/spec-rules` so they match realize.
 */
/** Render a data object literal; `$expr:`-prefixed strings emit as raw JS. */
function renderDataLit(data) {
    const parts = Object.entries(data).map(([k, v]) => {
        if (typeof v === 'string' && v.startsWith('$expr:')) {
            return `${JSON.stringify(k)}: ${v.slice(6)}`;
        }
        return `${JSON.stringify(k)}: ${JSON.stringify(v)}`;
    });
    return `{ ${parts.join(', ')} }`;
}
/**
 * Render one step from its harness call expression + expectation.
 *
 * - `rejected`        → `await expectRejected(<call>);`
 * - has `bindAs`      → `const <bind> = await <call>;` (+ assertField if state).
 *                       Bind only when a later step references the result id.
 * - `state`, no bind  → `assertField(await <call>, field, value);` (inline — no
 *                       binding, so repeated unnamed steps can't collide).
 * - `ok`, no bind     → `await <call>;`
 */
function renderCall(call, step) {
    if (step.expect.kind === 'rejected')
        return `  await expectRejected(${call});`;
    if (step.bindAs) {
        const decl = `  const ${step.bindAs} = await ${call};`;
        if (step.expect.kind === 'state') {
            return `${decl}\n  assertField(${step.bindAs}, ${JSON.stringify(step.expect.field)}, ${JSON.stringify(step.expect.value)});`;
        }
        return decl;
    }
    if (step.expect.kind === 'state') {
        return `  assertField(await ${call}, ${JSON.stringify(step.expect.field)}, ${JSON.stringify(step.expect.value)});`;
    }
    return `  await ${call};`;
}
function renderStep(step) {
    const m = JSON.stringify(step.model);
    const id = step.idExpr || 'undefined';
    switch (step.op) {
        case 'create':
            return renderCall(`create(client, ${m}, ${renderDataLit(step.payload || {})})`, step);
        case 'evolve':
            return renderCall(`evolve(client, ${m}, ${id}, ${JSON.stringify(String(step.payload?.toState ?? ''))})`, step);
        case 'retrieve':
            return renderCall(`retrieve(client, ${m}, ${id})`, step);
        case 'delete':
            return renderCall(`remove(client, ${m}, ${id})`, step);
        case 'update':
        case 'validate':
        default:
            return renderCall(`update(client, ${m}, ${id}, ${renderDataLit(step.payload || {})})`, step);
    }
}
const HELPERS_BY_OP = {
    create: ['create'],
    evolve: ['evolve'],
    retrieve: ['retrieve'],
    delete: ['remove'],
    update: ['update'],
    validate: ['update'],
};
export function renderScenarioFile(ruleId, cases) {
    const helpers = new Set(['scenarioClient']);
    for (const c of cases) {
        for (const step of c.steps) {
            for (const h of HELPERS_BY_OP[step.op] || [])
                helpers.add(h);
            if (step.expect.kind === 'state')
                helpers.add('assertField');
            if (step.expect.kind === 'rejected')
                helpers.add('expectRejected');
        }
    }
    const importList = [...helpers].sort().join(', ');
    const header = `/**
 * Auto-generated behavioral scenario test file.
 * Rule: ${ruleId}
 * DO NOT EDIT — regenerated every \`spv realize\` run. Hand edits are lost.
 * Change the rule or the spec instead.
 */

import { test } from 'vitest';
import { ${importList} } from './_harness.js';

const client = scenarioClient();
`;
    const body = cases
        .map(c => {
        const stepLines = c.steps.map(renderStep).join('\n');
        return `
test(${JSON.stringify(c.name)}, async () => {
${stepLines}
});`;
    })
        .join('\n');
    return header + body + '\n';
}
//# sourceMappingURL=api.js.map