/**
 * SET 100a — heal-then-reject must emit the rejecting predicate.
 */
import { describe, expect, it } from 'vitest';
import type { FeatureSpec, LlmClient } from '../src/core/types';
import { runTier1Job } from '../src/tier1/runner';
import { makeEvalSuiteRunner } from '../src/tier1/testExec';

function makeSpec(): FeatureSpec {
  return {
    title: 'Wiki',
    prompt: 'wiki',
    features: [{
      id: 'F1', name: 'Article', description: 'wiki article', parentId: null,
      deps: [], files: ['src/wiki.ts'], testFile: 'tests/article.test.ts',
      edgeCases: ['edge width'], envVars: [],
      exports: [{ file: 'src/wiki.ts', symbol: 'render' }],
      stubs: [{
        path: 'src/wiki.ts',
        content: 'export function render(s: string): string { throw new Error("stub"); }\n',
      }],
    }],
  };
}

/** Class-based LlmClient: syntax-healable body that keeps an unallowed symbol. */
class HealRejectLlm implements Partial<LlmClient> {
  async encodeFeatures() {
    return makeSpec();
  }
  async generateTests() {
    return { content: '', parts: { imports: [], cases: [] } };
  }
  async generateImplementation() {
    return [{ path: 'src/wiki.ts', content: 'export function render(s: string): string { return s; }\n' }];
  }
  async repair() {
    return [];
  }
  async fixTestCases(
    _spec: unknown,
    _feature: unknown,
    req: { name: string; kind: 'behavior' | 'edge' | 'integration' }[],
  ) {
    const target = req[0]!;
    return {
      cases: [{
        name: target.name,
        kind: target.kind,
        body: 'expect(render("x")).toBe("x"; fetch();',
      }],
    };
  }
}

describe('SET 100a — heal-then-reject emit', () => {
  it('emits heal then rejected-after-fill with surface-conformance reason', async () => {
    const events: string[] = [];
    const res = await runTier1Job({
      prompt: 'wiki article',
      llm: new HealRejectLlm() as unknown as LlmClient,
      runSuite: makeEvalSuiteRunner(),
      gates: { maxPasses: 1, stallLimit: 1, progressStall: 1, irAttempts: 1 },
      onEvent: e => events.push(e.message),
    });

    const healed = events.filter(m => /case "render" healed via/.test(m));
    const rejected = events.filter(m =>
      /case "render" candidate \d+\/3 rejected after fill — surface-conformance:/.test(m),
    );
    expect(healed.length).toBeGreaterThan(0);
    expect(rejected.length).toBeGreaterThan(0);
    expect(res.metrics.healedButRejected).toBeGreaterThan(0);
  });
});
