/**
 * Broker: AES-GCM app-password encryption
 *
 * Tests the encryptPassword / decryptPassword helpers in src/broker/crypto.ts.
 * Key behaviors verified (T-03-01, T-03-03):
 *   - Lossless roundtrip
 *   - Unique IVs per encryption (never reuse IV)
 *   - GCM auth tag tamper detection
 *   - Stored payload shape
 */

import { describe, it, expect, beforeAll } from 'vitest';

// Set a fixed 32-byte (64-char hex) key before importing the module
const TEST_KEY = 'a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2a3b4c5d6a7b8c9d0e1f2';

beforeAll(() => {
  process.env.APP_PASSWORD_ENCRYPTION_KEY = TEST_KEY;
});

// Dynamic import so env is set before module-level KEY evaluation
async function getCrypto() {
  return import('../../src/broker/crypto.js');
}

describe('encryptPassword / decryptPassword', () => {
  it('roundtrip: decrypt(encrypt(plaintext)) === plaintext', async () => {
    const { encryptPassword, decryptPassword } = await getCrypto();
    const plaintext = 'my-fastmail-app-password-abc123';
    const encrypted = encryptPassword(plaintext);
    expect(decryptPassword(encrypted)).toBe(plaintext);
  });

  it('different IVs produce different ciphertext for the same plaintext', async () => {
    const { encryptPassword } = await getCrypto();
    const plaintext = 'same-password';
    const enc1 = encryptPassword(plaintext);
    const enc2 = encryptPassword(plaintext);
    // The JSON payloads must differ (different IVs → different ciphertext)
    expect(enc1).not.toBe(enc2);
    // And the IVs themselves must differ
    const p1 = JSON.parse(enc1);
    const p2 = JSON.parse(enc2);
    expect(p1.iv).not.toBe(p2.iv);
  });

  it('decrypting with a tampered authTag throws', async () => {
    const { encryptPassword, decryptPassword } = await getCrypto();
    const encrypted = encryptPassword('secret');
    const payload = JSON.parse(encrypted);
    // Flip first byte of authTag
    payload.authTag = 'ff' + payload.authTag.slice(2);
    expect(() => decryptPassword(JSON.stringify(payload))).toThrow();
  });

  it('decrypting with a tampered ciphertext throws', async () => {
    const { encryptPassword, decryptPassword } = await getCrypto();
    const encrypted = encryptPassword('secret');
    const payload = JSON.parse(encrypted);
    // Flip first byte of ciphertext
    payload.ciphertext = 'ff' + payload.ciphertext.slice(2);
    expect(() => decryptPassword(JSON.stringify(payload))).toThrow();
  });

  it('stored payload is valid JSON with iv, authTag, ciphertext fields', async () => {
    const { encryptPassword } = await getCrypto();
    const encrypted = encryptPassword('test-password');
    const payload = JSON.parse(encrypted);
    expect(payload).toHaveProperty('iv');
    expect(payload).toHaveProperty('authTag');
    expect(payload).toHaveProperty('ciphertext');
    // All values are non-empty hex strings
    expect(typeof payload.iv).toBe('string');
    expect(payload.iv.length).toBeGreaterThan(0);
    expect(typeof payload.authTag).toBe('string');
    expect(payload.authTag.length).toBeGreaterThan(0);
    expect(typeof payload.ciphertext).toBe('string');
    expect(payload.ciphertext.length).toBeGreaterThan(0);
  });
});
