import { describe, expect, it } from 'vitest';
import { assertSafeRelPath, isSafeRelPath } from '../src/core/deployPath';
import { listDeployableFiles } from '../src/core/deploySync';
import fs from 'node:fs';
import path from 'node:path';
import os from 'node:os';

describe('deploy path safety', () => {
  it('accepts nested relative paths', () => {
    expect(assertSafeRelPath('src/foo.ts')).toEqual(['src', 'foo.ts']);
    expect(assertSafeRelPath('a/b/c.txt')).toEqual(['a', 'b', 'c.txt']);
  });

  it('rejects absolute and traversal paths', () => {
    expect(isSafeRelPath('/etc/passwd')).toBe(false);
    expect(isSafeRelPath('C:\\Windows\\system32')).toBe(false);
    expect(isSafeRelPath('../x')).toBe(false);
    expect(isSafeRelPath('a/../../b')).toBe(false);
    expect(isSafeRelPath('')).toBe(false);
  });
});

describe('deploy-manifest listing', () => {
  it('lists files with hashes and skips node_modules / .supergit by default', () => {
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rha-manifest-'));
    fs.mkdirSync(path.join(dir, 'src'), { recursive: true });
    fs.mkdirSync(path.join(dir, 'node_modules', 'x'), { recursive: true });
    fs.mkdirSync(path.join(dir, '.supergit'), { recursive: true });
    fs.writeFileSync(path.join(dir, 'src', 'a.ts'), 'export const a = 1;\n');
    fs.writeFileSync(path.join(dir, 'node_modules', 'x', 'i.js'), '1');
    fs.writeFileSync(path.join(dir, '.supergit', 'ledger.csv'), 'x\n');

    const files = listDeployableFiles(dir, { includeLedger: false });
    expect(files.map(f => f.path)).toEqual(['src/a.ts']);
    expect(files[0].sha256).toMatch(/^[a-f0-9]{64}$/);
    expect(files[0].size).toBeGreaterThan(0);

    const withLedger = listDeployableFiles(dir, { includeLedger: true });
    expect(withLedger.some(f => f.path === '.supergit/ledger.csv')).toBe(true);

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

describe('sync modes (full vs deployable)', () => {
  const scaffold = () => {
    const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rha-mode-'));
    fs.mkdirSync(path.join(dir, 'src'), { recursive: true });
    fs.mkdirSync(path.join(dir, 'tests'), { recursive: true });
    fs.mkdirSync(path.join(dir, '.supergit'), { recursive: true });
    fs.mkdirSync(path.join(dir, '.git', 'refs'), { recursive: true });
    fs.writeFileSync(path.join(dir, 'src', 'a.ts'), 'export const a = 1;\n');
    fs.writeFileSync(path.join(dir, 'tests', 'a.test.ts'), 'test\n');
    fs.writeFileSync(path.join(dir, '.supergit', 'ledger.csv'), 'x\n');
    fs.writeFileSync(path.join(dir, '.supergit', 'lock'), '{"pid":1}\n');
    fs.writeFileSync(path.join(dir, '.git', 'HEAD'), 'ref: refs/heads/main\n');
    fs.writeFileSync(path.join(dir, '.rha-debug.jsonl'), '{}\n');
    return dir;
  };

  it("'full' (default) carries the portable project: .supergit + .git, never the lockfile", () => {
    const dir = scaffold();
    const files = listDeployableFiles(dir).map(f => f.path);
    expect(files).toContain('src/a.ts');
    expect(files).toContain('tests/a.test.ts');
    expect(files).toContain('.supergit/ledger.csv');
    expect(files).toContain('.git/HEAD');
    expect(files).toContain('.rha-debug.jsonl');
    expect(files).not.toContain('.supergit/lock');
    fs.rmSync(dir, { recursive: true, force: true });
  });

  it("'deployable' strips scaffolding: no .supergit, no .git, no debug trace", () => {
    const dir = scaffold();
    const files = listDeployableFiles(dir, { mode: 'deployable' }).map(f => f.path);
    expect(files).toEqual(expect.arrayContaining(['src/a.ts', 'tests/a.test.ts']));
    expect(files.some(p => p.startsWith('.supergit'))).toBe(false);
    expect(files.some(p => p.startsWith('.git/'))).toBe(false);
    expect(files).not.toContain('.rha-debug.jsonl');
    fs.rmSync(dir, { recursive: true, force: true });
  });
});
