/**
 * SET 96b canary: compare the primary IR fill's current standalone-watcher
 * shape with the default-off candidate-zero digest fold.
 *
 * This is a deterministic wiring canary, not evidence of a real-provider
 * quality improvement. The class double deliberately rejects the infused
 * candidate-zero fixture, exercising the exact SKIP disposition.
 */
import { pathToFileURL } from 'node:url';
import type { FeatureSpec, LlmClient, RepairInput } from '../src/core/types';
import { runTier1Job } from '../src/tier1/runner';

const DIGEST_PREAMBLE = 'The pipeline ledger records this activity so far:';
const RIGHT = 'export function add(a: number, b: number): number { return a + b; }';

export interface Candidate0Rate {
  accepted: number;
  total: number;
}

export interface Set96CanaryResult {
  a: Candidate0Rate;
  b: Candidate0Rate;
  verdict: 'SHIP' | 'SKIP';
  reason: string;
}

function adderSpec(): FeatureSpec {
  return {
    title: 'Adder',
    prompt: 'Build an adder.',
    features: [{
      id: 'F1', name: 'add', description: 'Adds two numbers.', parentId: null,
      deps: [], files: ['src/adder.ts'], testFile: 'tests/adder.test.ts',
      edgeCases: ['zero'], envVars: [],
      exports: [{ file: 'src/adder.ts', symbol: 'add' }],
      stubs: [{ path: 'src/adder.ts', content:
        'export function add(a: number, b: number): number { throw new Error("stub"); }' }],
    }],
  };
}

/** Receiver-dependent double: candidate-zero outcomes are recorded per shape. */
class CanaryLlm {
  readonly candidate0: Candidate0Rate = { accepted: 0, total: 0 };
  watcherCalls = 0;

  async encodeFeatures() { return adderSpec(); }
  async generateTests() { return { content: '', parts: { imports: [], cases: [] } }; }
  async generateImplementation() { return [{ path: 'src/adder.ts', content: RIGHT }]; }
  async repair(_input: RepairInput) { return [{ path: 'src/adder.ts', content: RIGHT }]; }
  async watchEpoch() {
    this.watcherCalls++;
    return 'IDIOM: direct assertions\nUNION: retain the narrow add contract.';
  }

  async fixTestCases(
    _spec: unknown,
    _feature: unknown,
    requested: { name: string; kind: 'behavior' | 'edge'; errors?: string[] }[],
    _current?: unknown,
    _imports?: unknown,
    options?: { candidate?: { n: number } },
  ) {
    const request = requested[0]!;
    const isPrimaryFill = request.errors?.some(error => error.startsWith('Fill ONLY this')) ?? false;
    const isCandidateZero = isPrimaryFill && options?.candidate?.n === 1;
    const infused = request.errors?.includes(DIGEST_PREAMBLE)
      || request.errors?.some(error => error.includes(DIGEST_PREAMBLE));

    if (isCandidateZero) {
      this.candidate0.total++;
      // Fixture failure mechanism: the added preamble causes this bounded
      // candidate to omit its body; runner acceptance deterministically rejects it.
      if (infused) {
        return { cases: [{ name: request.name, kind: request.kind, body: '' }] };
      }
      this.candidate0.accepted++;
    }

    return {
      cases: [{
        name: request.name,
        kind: request.kind,
        body: request.kind === 'edge'
          ? 'expect(add(7, 0)).toBe(7);'
          : 'expect(add(2, 3)).toBe(5);',
      }],
    };
  }
}

async function runShape(fold: boolean): Promise<Candidate0Rate> {
  const llm = new CanaryLlm();
  const result = await runTier1Job({
    prompt: 'Build an adder.',
    llm: llm as unknown as LlmClient,
    gates: { set96bFoldWatcherIntoCandidate0: fold },
  });
  if (!fold && result.metrics.watcherInfusedCandidates !== 0) {
    throw new Error('baseline unexpectedly infused a candidate');
  }
  if (fold && result.metrics.watcherInfusedCandidates === 0) {
    throw new Error('fold gate did not infuse candidate zero');
  }
  return llm.candidate0;
}

export async function runSet96Canary(): Promise<Set96CanaryResult> {
  const a = await runShape(false);
  const b = await runShape(true);
  const aRate = a.total === 0 ? 0 : a.accepted / a.total;
  const bRate = b.total === 0 ? 0 : b.accepted / b.total;
  const degraded = bRate < aRate - 0.05;
  return {
    a,
    b,
    verdict: degraded ? 'SKIP' : 'SHIP',
    reason: degraded
      ? 'candidate-zero acceptance fell by more than 5 percentage points'
      : 'candidate-zero acceptance stayed within the 5 percentage-point guard',
  };
}

function formatRate(rate: Candidate0Rate): string {
  const percent = rate.total === 0 ? 0 : (rate.accepted / rate.total) * 100;
  return `${rate.accepted}/${rate.total} = ${percent.toFixed(1)}%`;
}

if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
  const result = await runSet96Canary();
  console.log(`CANARY A candidate0 rate: ${formatRate(result.a)}`);
  console.log(`CANARY B candidate0 rate: ${formatRate(result.b)}`);
  console.log(`CANARY VERDICT: ${result.verdict} (${result.reason})`);
}
