/**
 * msgId derivation: deterministic SHA-256 of the ciphertext blob.
 *
 * `msgId = lowercase-hex( sha256(ciphertext) )`
 *
 * Both client and server compute it independently and check that the
 * client's claimed msgId equals the recomputed hash — that gives us
 * idempotency on PUT (same ciphertext → same row) and replay-protection
 * for free (a tampered re-upload changes the hash and lands in a new
 * slot the recipient simply ignores when decrypt fails).
 */

export async function computeMsgId(ciphertext: Uint8Array): Promise<string> {
  const buf = await globalThis.crypto.subtle.digest(
    'SHA-256',
    ciphertext as unknown as ArrayBuffer,
  );
  const arr = new Uint8Array(buf);
  return Array.from(arr, (b) => b.toString(16).padStart(2, '0')).join('');
}

/** Constant-time string equality on the hex form, both lowercased first. */
export function constantTimeStringEqual(a: string, b: string): boolean {
  const aa = a.toLowerCase();
  const bb = b.toLowerCase();
  if (aa.length !== bb.length) return false;
  let diff = 0;
  for (let i = 0; i < aa.length; i++) {
    diff |= aa.charCodeAt(i) ^ bb.charCodeAt(i);
  }
  return diff === 0;
}

/** Validate hex form: 64 lowercase hex chars (32-byte digest). */
export function isValidMsgId(s: unknown): s is string {
  return typeof s === 'string' && /^[0-9a-f]{64}$/.test(s);
}
