/**
 * SET 97 — exhaust deterministic case-body mechanisms before an LLM fill draw.
 */
import type { FeatureNode, FeatureSpec, GeneratedFile } from '../core/types';
import { assembleTestFile } from '../core/irScaffold';
import type { IRCaseGen, IRImport } from '../core/irScaffold';
import { wireSuiteScaffoldImports } from './suiteScaffoldWire';
import type { PlannedCase } from './planSuite';
import { matchConsumableTemplate } from './reportEvidence';
import {
  classifyCaseClade,
  isMethodAnchorName,
  methodAnchorBody,
  rebindCladeBody,
} from './clades';
import { acceptCaseBody } from './substrate';
import type { SuiteRunner } from './testExec';

export type SubstrateFillMechanism =
  | 'matchConsumableTemplate'
  | 'rebindCladeBody'
  | 'methodAnchorBody';

export type SubstrateFillMechanismResult = 'empty' | 'vacuous' | 'red';

export interface SubstrateFillHit {
  body: string;
  mechanism: SubstrateFillMechanism;
  templateId?: string;
}

export interface SubstrateFillOpts {
  spec: FeatureSpec;
  feature: FeatureNode;
  caseGen: IRCaseGen & { symbol?: string };
  /** The original plan object supplies template matching's signature/license evidence. */
  plannedCase?: PlannedCase;
  stubs: GeneratedFile[];
  runSuite: SuiteRunner;
  imports: IRImport[];
  onMechanismResult?: (
    mechanism: SubstrateFillMechanism,
    result: SubstrateFillMechanismResult,
  ) => void;
}

/**
 * Returns a body only when deterministic substrate produced exactly one
 * non-crashing, red case against the stub artifact. Green candidates are
 * explicitly rejected as vacuous so they cannot suppress the LLM ladder.
 */
export async function substrateFillOne(
  opts: SubstrateFillOpts,
): Promise<SubstrateFillHit | null> {
  const {
    spec, feature, caseGen, plannedCase, stubs, runSuite, imports, onMechanismResult,
  } = opts;

  const tryMechanism = async (
    mechanism: SubstrateFillMechanism,
    body: string | null,
    templateId?: string,
  ): Promise<SubstrateFillHit | null> => {
    if (!body || !acceptCaseBody(body)) {
      onMechanismResult?.(mechanism, 'empty');
      return null;
    }
    const singleCase: IRCaseGen = { ...caseGen, body };
    const content = wireSuiteScaffoldImports(
      assembleTestFile(feature, imports, [singleCase]),
      feature.testFile,
      spec,
    ).content;
    try {
      const run = await runSuite(stubs, { path: feature.testFile, content });
      if (run.crashed || run.total !== 1) {
        onMechanismResult?.(mechanism, 'empty');
        return null;
      }
      if (run.failures.length !== 1) {
        onMechanismResult?.(mechanism, 'vacuous');
        return null;
      }
    } catch {
      onMechanismResult?.(mechanism, 'empty');
      return null;
    }
    onMechanismResult?.(mechanism, 'red');
    return { body, mechanism, ...(templateId ? { templateId } : {}) };
  };

  const template = plannedCase ? matchConsumableTemplate(plannedCase) : null;
  const templateBody = template && caseGen.symbol
    ? template.body.replace(/SYM/g, caseGen.symbol)
    : null;
  const templateHit = await tryMechanism(
    'matchConsumableTemplate',
    templateBody,
    template?.id,
  );
  if (templateHit) return templateHit;

  const clade = classifyCaseClade(
    { ...caseGen, body: '' },
    caseGen.symbol,
  );
  const reboundHit = await tryMechanism(
    'rebindCladeBody',
    rebindCladeBody({ ...caseGen, body: '', clade }),
  );
  if (reboundHit) return reboundHit;

  const methodBody = isMethodAnchorName(caseGen.name)
    ? methodAnchorBody(caseGen.symbol ?? caseGen.name, feature.description)
    : null;
  return tryMechanism('methodAnchorBody', methodBody);
}
