import { createHash } from 'node:crypto';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { afterAll, describe, expect, it } from 'vitest';
import { buildCorpus, corpusPrompts } from '../scripts/build-corpus';
import { VERSION } from '../src/version';

const outDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rha-corpus-'));

afterAll(() => {
  fs.rmSync(outDir, { recursive: true, force: true });
});

describe('corpus build', () => {
  const manifest = buildCorpus(outDir);

  it('writes every manifest file with a matching sha256 and byte count', () => {
    expect(manifest.files.length).toBeGreaterThan(0);
    for (const f of manifest.files) {
      const content = fs.readFileSync(path.join(outDir, f.name), 'utf8');
      expect(createHash('sha256').update(content).digest('hex')).toBe(f.sha256);
      expect(Buffer.byteLength(content)).toBe(f.bytes);
    }
  });

  it('carries the app version and corpus version', () => {
    expect(manifest.corpusVersion).toBe(1);
    expect(manifest.appVersion).toBe(VERSION);
    const onDisk = JSON.parse(fs.readFileSync(path.join(outDir, 'manifest.json'), 'utf8'));
    expect(onDisk).toEqual(manifest);
  });

  it('publishes every pipeline stage prompt non-empty', () => {
    const prompts = JSON.parse(fs.readFileSync(path.join(outDir, 'prompts.json'), 'utf8'));
    const stages = Object.keys(corpusPrompts());
    expect(stages).toEqual([
      'featureEnum', 'featureProbe', 'encode', 'featureRepair',
      'stubFix', 'ir', 'irCaseFix', 'build', 'rha', 'harnessContract',
    ]);
    for (const s of stages) {
      expect(prompts.stages[s], `stage ${s}`).toBeTypeOf('string');
      expect(prompts.stages[s].length, `stage ${s}`).toBeGreaterThan(50);
    }
  });

  it('publishes gate defaults inside their declared ranges', () => {
    const gates = JSON.parse(fs.readFileSync(path.join(outDir, 'gates.json'), 'utf8'));
    for (const [key, range] of Object.entries<{ min: number; max: number }>(gates.ranges)) {
      const v = gates.defaults[key];
      expect(v, key).toBeGreaterThanOrEqual(range.min);
      expect(v, key).toBeLessThanOrEqual(range.max);
    }
  });

  it('publishes lessons as an array', () => {
    const lessons = JSON.parse(fs.readFileSync(path.join(outDir, 'lessons.json'), 'utf8'));
    expect(Array.isArray(lessons.lessons)).toBe(true);
  });

  it('publishes the repair oracle entries', () => {
    const oracle = JSON.parse(fs.readFileSync(path.join(outDir, 'repair-oracle.json'), 'utf8'));
    expect(Array.isArray(oracle.entries)).toBe(true);
    expect(oracle.entries.length).toBeGreaterThanOrEqual(6);
    expect(oracle.entries.some((e: { id: string }) => e.id === 'undefined-symbol')).toBe(true);
    expect(manifest.files.some(f => f.name === 'repair-oracle.json')).toBe(true);
  });
});
