/**
 * SET 43d — draft clade templates for top recurring empty-draw families.
 *
 * These are DRAFTS only. They are NOT admitted into testgenOracle.generated.ts.
 * Full admission still requires the mutation-fixture oracle gate (same path as
 * model-schema / uses-dep / method-anchor): fixture → mutate → freeze → regenerate.
 * Callers may emit `clade-candidate` events for visibility; never auto-admit.
 */

import type { IRClade } from '../core/irScaffold';

/** Draft family ids (mirror the constants exported from clades.ts). */
export const DRAFT43_RESPONSE_SHAPE = 'response-shape';
export const DRAFT43_FORMAT_STRING = 'format-string';
export const DRAFT43_PAGINATION = 'pagination';

export const DRAFT43_FAMILY_IDS = [
  DRAFT43_RESPONSE_SHAPE,
  DRAFT43_FORMAT_STRING,
  DRAFT43_PAGINATION,
] as const;

export type Draft43FamilyId = (typeof DRAFT43_FAMILY_IDS)[number];

export type CladeDraft43 = {
  id: string;
  family: Draft43FamilyId;
  branch: IRClade['branch'];
  /** Human-readable ledger note from the 37b strata drop families. */
  ledgerNote: string;
  /** Admission status — drafts never skip the mutation-fixture oracle. */
  admission: 'draft-pending-mutation-oracle';
  /** Substrate-templated case body (SYM placeholder). Must contain expect(). */
  body: string;
};

/**
 * Draft template bodies for the three empty-draw ledger families.
 * Bodies stay in this module until mutation-oracle admission regenerates the oracle.
 */
export const CLADE_DRAFTS_43: readonly CladeDraft43[] = [
  {
    id: 'response-shape-status-error',
    family: DRAFT43_RESPONSE_SHAPE,
    branch: 'contract',
    ledgerNote: 'HTTP status codes / error bodies',
    admission: 'draft-pending-mutation-oracle',
    body:
      "const __res = SYM({ status: 404, error: { message: 'not found' } });\n"
      + "expect(__res.status).toBe(404);\n"
      + "expect(__res.error).toBeDefined();\n"
      + "expect(typeof __res.error.message).toBe('string');",
  },
  {
    id: 'format-string-json-log',
    family: DRAFT43_FORMAT_STRING,
    branch: 'representation',
    ledgerNote: 'Structured JSON logging format',
    admission: 'draft-pending-mutation-oracle',
    body:
      "const __line = SYM({ level: 'info', msg: 'ok', ts: 0 });\n"
      + "expect(typeof __line).toBe('string');\n"
      + "const __parsed = JSON.parse(__line);\n"
      + "expect(__parsed).toEqual(expect.objectContaining({ level: 'info', msg: 'ok' }));",
  },
  {
    id: 'pagination-offset-link',
    family: DRAFT43_PAGINATION,
    branch: 'state',
    ledgerNote: 'N per page, offset/Link headers',
    admission: 'draft-pending-mutation-oracle',
    body:
      "const __page = SYM({ limit: 10, offset: 0 });\n"
      + "expect(__page.items.length).toBeLessThanOrEqual(10);\n"
      + "expect(__page.offset).toBe(0);\n"
      + "expect(__page.link || __page.headers?.Link || __page.headers?.link).toBeDefined();",
  },
];

/** Name patterns that map a planned case onto a draft-43 family. */
const NAME_FAMILY_PATTERNS: readonly { family: Draft43FamilyId; re: RegExp }[] = [
  // response-shape — Status / ErrorBody anchors
  { family: DRAFT43_RESPONSE_SHAPE, re: /Status|ErrorBody|error\s*bod/i },
  // format-string — LogFormat / json log
  { family: DRAFT43_FORMAT_STRING, re: /LogFormat|json\s*log|structured\s*json\s*log/i },
  // pagination — Page / Offset / Link
  { family: DRAFT43_PAGINATION, re: /Page|Offset|LinkHeader|\bLink\b/i },
];

/**
 * Match a planned/case name to a SET 43d draft family (if any).
 * Pure string recognition — does not admit the clade into the oracle.
 */
export function matchDraft43Family(name: string): Draft43FamilyId | null {
  if (!name) return null;
  for (const { family, re } of NAME_FAMILY_PATTERNS) {
    if (re.test(name)) return family;
  }
  return null;
}

/** True when a name is recognized as one of the draft-43 planned families. */
export function isDraft43PlannedName(name: string): boolean {
  return matchDraft43Family(name) != null;
}

/** Look up the draft record for a family id (first body). */
export function draftBodyForFamily(family: Draft43FamilyId): string | null {
  return CLADE_DRAFTS_43.find(d => d.family === family)?.body ?? null;
}

/**
 * Register helpers — return the draft catalog + matcher so planners/hosts can
 * wire recognition without importing the generated oracle. No filesystem writes;
 * admission remains mutation-fixture oracle.
 */
export function registerDraft43Clades(): {
  families: readonly Draft43FamilyId[];
  drafts: readonly CladeDraft43[];
  match: typeof matchDraft43Family;
  admissionNote: string;
} {
  return {
    families: DRAFT43_FAMILY_IDS,
    drafts: CLADE_DRAFTS_43,
    match: matchDraft43Family,
    admissionNote:
      'SET 43d drafts only — full admission requires mutation-fixture oracle '
      + '(gen-testgen-oracle path). Emit clade-candidate for visibility; never auto-admit.',
  };
}
