import { describe, expect, it } from 'vitest';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';
import { acquireProjectLock, releaseProjectLock, validateProjectFolder } from '../src/core/attach';
import { sha256 } from '../src/core/sandbox';

/** Minimal attachable project: substrate dir + git dir + spec + frozen suite. */
function scaffoldProject(): string {
  const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rha-attach-'));
  fs.mkdirSync(path.join(dir, '.supergit'), { recursive: true });
  fs.mkdirSync(path.join(dir, '.git'), { recursive: true });
  fs.mkdirSync(path.join(dir, 'tests'), { recursive: true });
  fs.writeFileSync(path.join(dir, '.supergit', 'ledger.csv'), 'step,id,type\n1,N0001,genesis\n');
  const suite = `import { describe, it, expect } from 'vitest';\ndescribe('F1', () => { it('x', () => { expect(1).toBe(1); }); });\n`;
  fs.writeFileSync(path.join(dir, 'tests', 'f1-x.test.ts'), suite);
  fs.writeFileSync(
    path.join(dir, '.supergit', 'spec.json'),
    JSON.stringify({ title: 'proj', features: [{ id: 'F1', name: 'x' }] }),
  );
  fs.writeFileSync(
    path.join(dir, '.supergit', 'ir-manifest.json'),
    JSON.stringify({ features: [], freezeHashes: { 'tests/f1-x.test.ts': sha256(suite) } }),
  );
  return dir;
}

describe('attach validation (Part U/V — re-attachable synced folder)', () => {
  it('accepts a full-sync project and returns its spec', () => {
    const dir = scaffoldProject();
    const v = validateProjectFolder(dir);
    expect(v.ok).toBe(true);
    expect(v.spec?.title).toBe('proj');
    fs.rmSync(dir, { recursive: true, force: true });
  });

  it('rejects a tampered frozen suite (freeze-hash mismatch)', () => {
    const dir = scaffoldProject();
    fs.appendFileSync(path.join(dir, 'tests', 'f1-x.test.ts'), '// external edit\n');
    const v = validateProjectFolder(dir);
    expect(v.ok).toBe(false);
    expect(v.error).toMatch(/scaffolding interrupted|external edits|integrity|tampered/i);
    fs.rmSync(dir, { recursive: true, force: true });
  });

  it('rejects a deployable-only folder (no git history)', () => {
    const dir = scaffoldProject();
    fs.rmSync(path.join(dir, '.git'), { recursive: true, force: true });
    const v = validateProjectFolder(dir);
    expect(v.ok).toBe(false);
    expect(v.error).toMatch(/git history/i);
    fs.rmSync(dir, { recursive: true, force: true });
  });

  it('lock: live-pid lock blocks, own lock re-acquires, stale lock reclaims', () => {
    const dir = scaffoldProject();
    // Foreign live pid (this test process's parent is alive) → blocked.
    fs.writeFileSync(path.join(dir, '.supergit', 'lock'), JSON.stringify({ pid: process.ppid, ts: 'x' }));
    expect(acquireProjectLock(dir).ok).toBe(false);
    // Stale dead pid → reclaimed with a warning.
    fs.writeFileSync(path.join(dir, '.supergit', 'lock'), JSON.stringify({ pid: 999999999, ts: 'x' }));
    const reclaimed = acquireProjectLock(dir);
    expect(reclaimed.ok).toBe(true);
    expect(reclaimed.warning).toMatch(/stale/i);
    // Our own lock → idempotent re-acquire.
    expect(acquireProjectLock(dir).ok).toBe(true);
    releaseProjectLock(dir);
    expect(fs.existsSync(path.join(dir, '.supergit', 'lock'))).toBe(false);
    fs.rmSync(dir, { recursive: true, force: true });
  });
});
