import { describe, it, expect, beforeEach } from 'vitest';
import { mkdtempSync, rmSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import {
  initMasterKey, generateUserDek, encryptUserDek, decryptUserDek,
  encryptStateBlob, decryptStateBlob,
} from './sessions.js';

describe('envelope encryption', () => {
  let dir: string;
  let masterKeyPath: string;

  beforeEach(() => {
    dir = mkdtempSync(join(tmpdir(), 'crypto-test-'));
    masterKeyPath = join(dir, 'master.key');
  });

  it('initMasterKey writes a 32-byte file when missing', () => {
    const key = initMasterKey(masterKeyPath);
    expect(key.length).toBe(32);
    const reread = initMasterKey(masterKeyPath);
    expect(reread.equals(key)).toBe(true);
    rmSync(dir, { recursive: true, force: true });
  });

  it('round-trips a user DEK through master key envelope', () => {
    const master = initMasterKey(masterKeyPath);
    const dek = generateUserDek();
    const enc = encryptUserDek(master, dek);
    const dec = decryptUserDek(master, enc);
    expect(dec.equals(dek)).toBe(true);
    rmSync(dir, { recursive: true, force: true });
  });

  it('round-trips a state blob through user DEK', () => {
    const dek = generateUserDek();
    const payload = JSON.stringify({ cookies: [{ name: 'sid', value: 'abc' }] });
    const enc = encryptStateBlob(dek, payload);
    const dec = decryptStateBlob(dek, enc);
    expect(dec).toBe(payload);
    rmSync(dir, { recursive: true, force: true });
  });

  it('decryptStateBlob throws on tampered ciphertext', () => {
    const dek = generateUserDek();
    const enc = encryptStateBlob(dek, 'hello');
    enc[enc.length - 1] ^= 0xff;
    expect(() => decryptStateBlob(dek, enc)).toThrow();
  });
});
