/**
 * Server-side PII encryption for EncryptID
 *
 * AES-256-GCM encryption and HMAC-SHA256 hashing derived from JWT_SECRET via HKDF.
 * Used to encrypt PII at rest in PostgreSQL and provide deterministic hashes for lookups.
 */

const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET) throw new Error('JWT_SECRET environment variable is required');

const encoder = new TextEncoder();
const decoder = new TextDecoder();

// ── Cached keys ──

let _piiKey: CryptoKey | null = null;
let _hmacKey: CryptoKey | null = null;

async function getPIIKey(): Promise<CryptoKey> {
  if (_piiKey) return _piiKey;
  const base = await crypto.subtle.importKey('raw', encoder.encode(JWT_SECRET), { name: 'HKDF' }, false, ['deriveKey']);
  _piiKey = await crypto.subtle.deriveKey(
    { name: 'HKDF', hash: 'SHA-256', salt: encoder.encode('pii-v1'), info: new Uint8Array(0) },
    base,
    { name: 'AES-GCM', length: 256 },
    false,
    ['encrypt', 'decrypt'],
  );
  return _piiKey;
}

async function getHMACKey(): Promise<CryptoKey> {
  if (_hmacKey) return _hmacKey;
  const base = await crypto.subtle.importKey('raw', encoder.encode(JWT_SECRET), { name: 'HKDF' }, false, ['deriveKey']);
  _hmacKey = await crypto.subtle.deriveKey(
    { name: 'HKDF', hash: 'SHA-256', salt: encoder.encode('pii-hash-v1'), info: new Uint8Array(0) },
    base,
    { name: 'HMAC', hash: 'SHA-256', length: 256 },
    false,
    ['sign'],
  );
  return _hmacKey;
}

// ── Public API ──

/**
 * Encrypt a PII field for storage. Returns "iv.ciphertext" (base64url), or null if input is null/undefined.
 */
export async function encryptField(plaintext: string | null | undefined): Promise<string | null> {
  if (plaintext == null || plaintext === '') return null;
  const key = await getPIIKey();
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const ct = new Uint8Array(await crypto.subtle.encrypt(
    { name: 'AES-GCM', iv },
    key,
    encoder.encode(plaintext),
  ));
  return `${Buffer.from(iv).toString('base64url')}.${Buffer.from(ct).toString('base64url')}`;
}

/**
 * Decrypt a stored PII field. Returns plaintext, or null if input is null.
 * Falls back to returning the value as-is if it doesn't contain a "." separator (legacy plaintext).
 */
export async function decryptField(stored: string | null | undefined): Promise<string | null> {
  if (stored == null || stored === '') return null;
  const dotIdx = stored.indexOf('.');
  if (dotIdx === -1) return stored; // legacy plaintext passthrough
  const key = await getPIIKey();
  const iv = Buffer.from(stored.slice(0, dotIdx), 'base64url');
  const ct = Buffer.from(stored.slice(dotIdx + 1), 'base64url');
  const plain = await crypto.subtle.decrypt(
    { name: 'AES-GCM', iv },
    key,
    ct,
  );
  return decoder.decode(plain);
}

/**
 * Compute a deterministic HMAC-SHA256 hash for equality lookups.
 * Input is lowercased and trimmed before hashing.
 */
export async function hashForLookup(value: string): Promise<string> {
  const key = await getHMACKey();
  const sig = await crypto.subtle.sign('HMAC', key, encoder.encode(value.toLowerCase().trim()));
  return Array.from(new Uint8Array(sig)).map(b => b.toString(16).padStart(2, '0')).join('');
}
