import { describe, expect, it } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { syncDeployable } from '../src/core/deploySync';
import { checkScaffoldingIntegrity } from '../src/core/scaffoldGuard';
import { getConfig, writeRoleKnobs, resetConfigOverrides, writeConfigOverrides } from '../src/core/config';
import { sha256 } from '../src/core/sandbox';

describe('Part V deploy sync', () => {
  it('copies new files incrementally and skips unchanged', () => {
    const src = fs.mkdtempSync(path.join(os.tmpdir(), 'rha-src-'));
    const dst = fs.mkdtempSync(path.join(os.tmpdir(), 'rha-dst-'));
    fs.mkdirSync(path.join(src, 'src'), { recursive: true });
    fs.mkdirSync(path.join(src, '.supergit'), { recursive: true });
    fs.writeFileSync(path.join(src, 'src', 'a.ts'), 'export const a = 1;\n');
    fs.writeFileSync(path.join(src, '.supergit', 'ledger.csv'), 'x\n');

    const r1 = syncDeployable(src, dst, { includeLedger: false });
    expect(r1.ok).toBe(true);
    expect(r1.copied).toContain('src/a.ts');
    expect(r1.copied.some(p => p.includes('.supergit'))).toBe(false);

    const r2 = syncDeployable(src, dst, { includeLedger: false });
    expect(r2.copied).toEqual([]);
    expect(r2.skipped).toBeGreaterThan(0);

    fs.writeFileSync(path.join(src, 'src', 'a.ts'), 'export const a = 2;\n');
    const r3 = syncDeployable(src, dst, { includeLedger: true });
    expect(r3.copied).toContain('src/a.ts');
    expect(r3.copied.some(p => p.includes('ledger.csv'))).toBe(true);

    fs.rmSync(src, { recursive: true, force: true });
    fs.rmSync(dst, { recursive: true, force: true });
  });
});

describe('Part U3 scaffolding guard', () => {
  it('flags freeze-hash mismatches as external edits', () => {
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rha-guard-'));
    fs.mkdirSync(path.join(dir, 'tests'), { recursive: true });
    fs.mkdirSync(path.join(dir, '.supergit'), { recursive: true });
    const testFile = 'tests/f1.test.ts';
    const content = 'import { describe, it } from "vitest";\ndescribe("x", () => { it("y", () => {}); });\n';
    fs.writeFileSync(path.join(dir, testFile), content);
    fs.writeFileSync(path.join(dir, '.supergit', 'ledger.csv'), 'step,id\n');
    const hash = sha256(content);
    const ok = checkScaffoldingIntegrity(dir, {
      version: 1, title: 't', prompt: 'p', features: [], cases: [],
      freezeHashes: { [testFile]: hash }, frozenAt: null,
    });
    expect(ok.ok).toBe(true);

    fs.writeFileSync(path.join(dir, testFile), content + '// edited\n');
    const bad = checkScaffoldingIntegrity(dir, {
      version: 1, title: 't', prompt: 'p', features: [], cases: [],
      freezeHashes: { [testFile]: hash }, frozenAt: null,
    });
    expect(bad.externalEdits).toBe(true);
    expect(bad.message).toMatch(/scaffolding interrupted/);

    fs.rmSync(dir, { recursive: true, force: true });
  });

  it('attach refuses when freeze hashes diverge (scaffoldGuard)', async () => {
    const { execFileSync } = await import('node:child_process');
    const { JobManager } = await import('../src/server/jobs');
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rha-attach-guard-'));
    fs.mkdirSync(path.join(dir, 'tests'), { recursive: true });
    fs.mkdirSync(path.join(dir, 'src'), { recursive: true });
    fs.mkdirSync(path.join(dir, '.supergit'), { recursive: true });
    execFileSync('git', ['init'], { cwd: dir });
    const testFile = 'tests/f1.test.ts';
    const content = 'import { describe, it } from "vitest";\ndescribe("x", () => { it("y", () => {}); });\n';
    fs.writeFileSync(path.join(dir, testFile), content);
    fs.writeFileSync(path.join(dir, 'src', 'f1.ts'), 'export {}\n');
    fs.writeFileSync(
      path.join(dir, '.supergit', 'ledger.csv'),
      'step,id,type,feature_id,parent_id,cause_id,edge_from,edge_to,artifact_path,bug_class,status,summary,ts\n'
      + '1,N0001,genesis,,,,,,,info,genesis,2020-01-01T00:00:00.000Z\n',
    );
    const hash = sha256(content);
    const manifest = {
      version: 1 as const,
      title: 't',
      prompt: 'p',
      features: [],
      cases: [],
      freezeHashes: { [testFile]: hash },
      frozenAt: null,
    };
    fs.writeFileSync(path.join(dir, '.supergit', 'ir-manifest.json'), JSON.stringify(manifest));
    fs.writeFileSync(path.join(dir, '.supergit', 'spec.json'), JSON.stringify({
      title: 't', prompt: 'p', features: [],
    }));
    // Tamper after recording the freeze hash
    fs.writeFileSync(path.join(dir, testFile), content + '// external\n');

    const mgr = new JobManager();
    const out = mgr.attach(dir);
    expect(out.ok).toBe(false);
    expect(out.error).toMatch(/scaffolding interrupted|external edits/i);

    fs.rmSync(dir, { recursive: true, force: true });
  });
});

describe('Part S1 per-role config', () => {
  it('merges role knobs over global', () => {
    resetConfigOverrides();
    writeConfigOverrides({ swarmWidth: 3 });
    writeRoleKnobs('supervisor', { swarmWidth: 1, effort: 'low' });
    expect(getConfig().swarmWidth).toBe(3);
    expect(getConfig('supervisor').swarmWidth).toBe(1);
    expect(getConfig('supervisor').effort).toBe('low');
    expect(getConfig('operator').swarmWidth).toBe(3);
    resetConfigOverrides();
  });
});
