/**
 * Emits a self-contained scenario-test harness into a realized project at
 * `tests/scenario/_harness.ts`. Consumption model B: realized projects
 * import `./_harness.js` rather than depending on @specverse/engines, so no
 * heavy toolchain dep is pulled in just to run tests.
 *
 * The harness inlines the canonical `pluralize` + base-path derivation
 * (kept verbatim in sync with @specverse/types/spec-rules `pluralize`), so
 * its routes match exactly what the realize routes-generator emits.
 *
 * Authored as a template string (the SpecVerse idiom for emitted code —
 * validated by the realize → tsc → run end-to-end path, like Factory B's
 * emitted source and the conformance script). Deliberately contains no
 * backticks / template-expressions / regex-with-backslashes so it embeds in
 * a template literal without escaping.
 */
export const HARNESS_FILENAME = '_harness.ts';
export function renderScenarioHarness() {
    return `/**
 * Auto-generated scenario-test harness. DO NOT EDIT — regenerated every
 * spv realize run. Self-contained: drives this backend's CURVED REST
 * endpoints. Run with the backend up and SPECVERSE_API_URL pointing at it.
 */

export interface ScenarioClient { baseUrl: string; }

function pluralize(word: string): string {
  if (word.endsWith('y') && !word.endsWith('ay') && !word.endsWith('ey') && !word.endsWith('oy') && !word.endsWith('uy')) {
    return word.slice(0, -1) + 'ies';
  }
  if (word.endsWith('s') || word.endsWith('x') || word.endsWith('z') || word.endsWith('ch') || word.endsWith('sh')) {
    return word + 'es';
  }
  return word + 's';
}

function basePath(model: string): string {
  return '/api/' + pluralize(model.toLowerCase());
}

export function scenarioClient(baseUrl?: string): ScenarioClient {
  let resolved = baseUrl || process.env.SPECVERSE_API_URL || 'http://127.0.0.1:3000';
  while (resolved.endsWith('/')) resolved = resolved.slice(0, -1);
  return { baseUrl: resolved };
}

export class ScenarioHttpError extends Error {
  constructor(public op: string, public model: string, public status: number, public bodyText: string) {
    super(op + ' ' + model + ' failed: HTTP ' + status + ' ' + bodyText);
    this.name = 'ScenarioHttpError';
  }
}

function url(client: ScenarioClient, model: string, suffix: string): string {
  return client.baseUrl + basePath(model) + suffix;
}

async function readBody(res: Response): Promise<string> {
  try { return (await res.text()).slice(0, 500); } catch { return ''; }
}

function unwrap(json: any): any {
  if (json && typeof json === 'object' && 'data' in json && json.data && typeof json.data === 'object') return json.data;
  return json;
}

export async function create(client: ScenarioClient, model: string, data: Record<string, unknown>): Promise<any> {
  const res = await fetch(url(client, model, ''), { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(data) });
  if (!res.ok) throw new ScenarioHttpError('create', model, res.status, await readBody(res));
  return unwrap(await res.json());
}

export async function evolve(client: ScenarioClient, model: string, id: string, toState: string): Promise<any> {
  const res = await fetch(url(client, model, '/' + id + '/evolve'), { method: 'PATCH', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ toState }) });
  if (!res.ok) throw new ScenarioHttpError('evolve', model, res.status, await readBody(res));
  return unwrap(await res.json());
}

export async function retrieve(client: ScenarioClient, model: string, id: string): Promise<any> {
  const res = await fetch(url(client, model, '/' + id), { method: 'GET' });
  if (!res.ok) throw new ScenarioHttpError('retrieve', model, res.status, await readBody(res));
  return unwrap(await res.json());
}

export async function update(client: ScenarioClient, model: string, id: string, data: Record<string, unknown>): Promise<any> {
  const res = await fetch(url(client, model, '/' + id), { method: 'PUT', headers: { 'content-type': 'application/json' }, body: JSON.stringify(data) });
  if (!res.ok) throw new ScenarioHttpError('update', model, res.status, await readBody(res));
  return unwrap(await res.json());
}

export async function remove(client: ScenarioClient, model: string, id: string): Promise<void> {
  const res = await fetch(url(client, model, '/' + id), { method: 'DELETE' });
  if (!res.ok) throw new ScenarioHttpError('delete', model, res.status, await readBody(res));
}

export function assertField(entity: any, field: string, value: unknown): void {
  const actual = entity == null ? undefined : (entity[field] !== undefined ? entity[field] : (entity.data ? entity.data[field] : undefined));
  if (actual !== value) {
    throw new Error('expected ' + field + ' === ' + JSON.stringify(value) + ', got ' + JSON.stringify(actual));
  }
}

export async function expectRejected(p: Promise<unknown>): Promise<void> {
  try { await p; } catch { return; }
  throw new Error('expected the operation to be rejected, but it succeeded');
}
`;
}
//# sourceMappingURL=harness-emitter.js.map