/**
 * E2EE crypto utilities — AES-GCM encryption + PBKDF2 key derivation.
 * Uses the Web Crypto API (available in Bun and browsers).
 */

import { toHex, fromHex } from './encoding';

const ALGORITHM = 'AES-GCM';
const KEY_LENGTH = 256;
const IV_LENGTH = 12; // 96 bits, recommended for GCM
const PBKDF2_ITERATIONS = 100_000;
const PBKDF2_HASH = 'SHA-256';

/**
 * Generate a random AES-GCM 256-bit key.
 */
export async function generateKey(): Promise<CryptoKey> {
  return crypto.subtle.generateKey(
    {
      name: ALGORITHM,
      length: KEY_LENGTH,
    },
    true, // extractable
    ['encrypt', 'decrypt'],
  );
}

/**
 * Derive an AES-GCM key from a passphrase and salt using PBKDF2.
 */
export async function deriveKey(passphrase: string, salt: string): Promise<CryptoKey> {
  const enc = new TextEncoder();
  const keyMaterial = await crypto.subtle.importKey(
    'raw',
    enc.encode(passphrase),
    'PBKDF2',
    false,
    ['deriveKey'],
  );

  return crypto.subtle.deriveKey(
    {
      name: 'PBKDF2',
      salt: enc.encode(salt),
      iterations: PBKDF2_ITERATIONS,
      hash: PBKDF2_HASH,
    },
    keyMaterial,
    {
      name: ALGORITHM,
      length: KEY_LENGTH,
    },
    true, // extractable
    ['encrypt', 'decrypt'],
  );
}

/**
 * Encrypt plaintext using AES-GCM.
 * Returns hex-encoded ciphertext and IV.
 */
export async function encrypt(
  key: CryptoKey,
  plaintext: string,
): Promise<{ ciphertext: string; iv: string }> {
  const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
  const enc = new TextEncoder();
  const ciphertextBuffer = await crypto.subtle.encrypt(
    {
      name: ALGORITHM,
      iv,
    },
    key,
    enc.encode(plaintext),
  );

  return {
    ciphertext: toHex(new Uint8Array(ciphertextBuffer)),
    iv: toHex(iv),
  };
}

/**
 * Decrypt hex-encoded ciphertext using AES-GCM.
 * Returns the original plaintext string.
 */
export async function decrypt(
  key: CryptoKey,
  ciphertextHex: string,
  ivHex: string,
): Promise<string> {
  const ciphertext = fromHex(ciphertextHex);
  const iv = fromHex(ivHex);

  const plaintextBuffer = await crypto.subtle.decrypt(
    {
      name: ALGORITHM,
      iv,
    },
    key,
    ciphertext,
  );

  return new TextDecoder().decode(plaintextBuffer);
}
