import { randomBytes } from 'node:crypto';
import { assertOboCacheEncryptionKey } from './check-obo-cache-encryption-key';

const STRONG_KEY = randomBytes(32).toString('base64url');
const OTHER_KEY = randomBytes(32).toString('base64url');

describe('assertOboCacheEncryptionKey', () => {
  const originalObo = process.env['OBO_CACHE_ENCRYPTION_KEY'];
  const originalSession = process.env['SESSION_ENCRYPTION_KEY'];

  afterEach(() => {
    restore('OBO_CACHE_ENCRYPTION_KEY', originalObo);
    restore('SESSION_ENCRYPTION_KEY', originalSession);
  });

  it('returns the decoded 32-byte buffer for a well-formed key', () => {
    process.env['OBO_CACHE_ENCRYPTION_KEY'] = STRONG_KEY;
    const key = assertOboCacheEncryptionKey();
    expect(key).toBeInstanceOf(Buffer);
    expect(key.length).toBe(32);
    expect(key.equals(Buffer.from(STRONG_KEY, 'base64url'))).toBe(true);
  });

  it('throws when OBO_CACHE_ENCRYPTION_KEY is unset', () => {
    delete process.env['OBO_CACHE_ENCRYPTION_KEY'];
    expect(() => assertOboCacheEncryptionKey()).toThrow(/OBO_CACHE_ENCRYPTION_KEY is not set/);
  });

  it('throws when OBO_CACHE_ENCRYPTION_KEY is the .env.example placeholder', () => {
    process.env['OBO_CACHE_ENCRYPTION_KEY'] = 'replace_with_32_random_bytes_base64url';
    expect(() => assertOboCacheEncryptionKey()).toThrow(/placeholder/);
  });

  it('throws when OBO_CACHE_ENCRYPTION_KEY decodes to fewer than 32 bytes', () => {
    process.env['OBO_CACHE_ENCRYPTION_KEY'] = randomBytes(16).toString('base64url');
    expect(() => assertOboCacheEncryptionKey()).toThrow(/decodes to 16 bytes/);
  });

  it('throws when OBO_CACHE_ENCRYPTION_KEY decodes to more than 32 bytes', () => {
    process.env['OBO_CACHE_ENCRYPTION_KEY'] = randomBytes(64).toString('base64url');
    expect(() => assertOboCacheEncryptionKey()).toThrow(/decodes to 64 bytes/);
  });

  it('throws when OBO_CACHE_ENCRYPTION_KEY is identical to SESSION_ENCRYPTION_KEY', () => {
    // ADR-0014 mandates dedicated keys. Catching this at boot
    // prevents a copy-paste regression from silently downgrading
    // the trust boundary.
    process.env['SESSION_ENCRYPTION_KEY'] = STRONG_KEY;
    process.env['OBO_CACHE_ENCRYPTION_KEY'] = STRONG_KEY;
    expect(() => assertOboCacheEncryptionKey()).toThrow(/must differ from SESSION_ENCRYPTION_KEY/);
  });

  it('accepts a value identical only when SESSION_ENCRYPTION_KEY is unset (boot order tolerant)', () => {
    delete process.env['SESSION_ENCRYPTION_KEY'];
    process.env['OBO_CACHE_ENCRYPTION_KEY'] = STRONG_KEY;
    expect(() => assertOboCacheEncryptionKey()).not.toThrow();
  });

  it('accepts distinct keys', () => {
    process.env['SESSION_ENCRYPTION_KEY'] = STRONG_KEY;
    process.env['OBO_CACHE_ENCRYPTION_KEY'] = OTHER_KEY;
    expect(() => assertOboCacheEncryptionKey()).not.toThrow();
  });
});

function restore(name: string, original: string | undefined): void {
  if (original === undefined) {
    delete process.env[name];
  } else {
    process.env[name] = original;
  }
}
