/**
 * PII encryption helpers - column-level encryption via pgcrypto.
 *
 * ## How to use at INSERT time:
 *
 *   import { encrypt, blindIndex } from '@/server/db/crypto';
 *
 *   await db.insert(users).values({
 *     phone: encrypt(rawPhone, env.PII_KEY),          // opaque ciphertext stored
 *     phoneIndex: blindIndex(rawPhone, env.PII_KEY),  // deterministic hex for lookup
 *     ...
 *   });
 *
 * ## How to use at QUERY (lookup) time:
 *
 *   const idx = blindIndex(rawPhone, env.PII_KEY);
 *   const [user] = await db
 *     .select()
 *     .from(users)
 *     .where(eq(users.phoneIndex, idx));
 *
 * ## Notes:
 * - `encrypt()` returns a Drizzle sql`` fragment that will be embedded into the
 *   parameterized query. The PII key and plaintext are sent as bind parameters -
 *   never via string interpolation.
 * - `blindIndex()` is a pure JS computation using the Web Crypto API, producing
 *   an HMAC-SHA256 hex digest. It is consistent across runtimes (Node / Workers).
 * - Never log the return value of either function - it contains sensitive data.
 */

import { sql } from 'drizzle-orm';
import { bytesToHex } from '@/lib/encoding.js';
import type { SQL } from 'drizzle-orm';

export type { SQL };

/**
 * Returns a Drizzle `sql` fragment that evaluates to `pgp_sym_encrypt(value, key)`
 * at query time. Safe for use in `.values({})` objects - Drizzle embeds it as a
 * raw SQL expression, not a string literal.
 */
export function encrypt(value: string, key: string): SQL {
  return sql`pgp_sym_encrypt(${value}, ${key})`;
}

/**
 * Returns a Drizzle `sql` fragment that evaluates to `pgp_sym_decrypt(col, key)`
 * at query time. Use in SELECT expressions to decrypt a PII column inline.
 *
 * Example:
 *   const rows = await db.execute(sql`
 *     SELECT ${decryptExpr(sql`vault_token_enc`, env.PII_KEY)} AS vault_token
 *     FROM group_reservations WHERE id = ${id}
 *   `);
 */
export function decryptExpr(colExpr: SQL | string, key: string): SQL {
  const col = typeof colExpr === 'string' ? sql.raw(colExpr) : colExpr;
  // Columns are stored as either Postgres hex bytea (\x...) or base64 depending
  // on the write path. CASE normalises both before passing to pgp_sym_decrypt.
  return sql`pgp_sym_decrypt(CASE WHEN substring(${col}::text, 1, 2) = '\\x' THEN ${col}::bytea ELSE decode(${col}::text, 'base64') END, ${key})::text`;
}

/**
 * Like `decryptExpr` but returns NULL instead of throwing on undecryptable rows
 * (legacy/old-key PII). Use for BULK listings where one bad row must not abort
 * the query.
 */
export function safeDecryptExpr(colExpr: SQL | string, key: string): SQL {
  const col = typeof colExpr === 'string' ? sql.raw(colExpr) : colExpr;
  return sql`safe_pgp_sym_decrypt(CASE WHEN substring(${col}::text, 1, 2) = '\\x' THEN ${col}::bytea ELSE decode(${col}::text, 'base64') END, ${key})`;
}

/**
 * Computes a deterministic HMAC-SHA256 blind index over `value` keyed with `key`.
 * Returns a lowercase hex string suitable for exact-match lookups.
 *
 * Pure JS - no DB round-trip needed. Works identically in Node and Cloudflare Workers.
 */
export async function blindIndex(value: string, key: string): Promise<string> {
  const enc = new TextEncoder();
  const cryptoKey = await crypto.subtle.importKey(
    'raw',
    enc.encode(key),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign'],
  );
  const signature = await crypto.subtle.sign('HMAC', cryptoKey, enc.encode(value));
  return bytesToHex(signature);
}
