/**
 * Constant-time byte array comparison (standalone utility).
 * Same semantics as CryptoProvider.constantTimeEqual but usable
 * in contexts that don't have a provider instance.
 */
export function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {
  if (a.length !== b.length) return false;
  let diff = 0;
  for (let i = 0; i < a.length; i++) {
    diff |= a[i]! ^ b[i]!;
  }
  return diff === 0;
}

/**
 * CryptoProvider — platform-agnostic interface for all cryptographic primitives.
 *
 * Implementations:
 *  - @shade/crypto-web: SubtleCrypto (Bun, Node.js, browser)
 *  - shade-android: Google Tink (Kotlin/Android)
 */
export interface CryptoProvider {
  // ─── X25519 (Diffie-Hellman) ───────────────────────────────

  /** Generate an X25519 keypair (32-byte public + 32-byte private) */
  generateX25519KeyPair(): Promise<{ publicKey: Uint8Array; privateKey: Uint8Array }>;

  /** Perform X25519 Diffie-Hellman: returns 32-byte shared secret */
  x25519(privateKey: Uint8Array, publicKey: Uint8Array): Promise<Uint8Array>;

  // ─── Ed25519 (Signing) ─────────────────────────────────────

  /** Generate an Ed25519 keypair (32-byte public + 32-byte private) */
  generateEd25519KeyPair(): Promise<{ publicKey: Uint8Array; privateKey: Uint8Array }>;

  /** Sign a message with Ed25519. Returns a 64-byte signature. */
  sign(privateKey: Uint8Array, message: Uint8Array): Promise<Uint8Array>;

  /** Verify an Ed25519 signature. Returns true if valid. */
  verify(publicKey: Uint8Array, message: Uint8Array, signature: Uint8Array): Promise<boolean>;

  // ─── AES-256-GCM (Symmetric Encryption) ────────────────────

  /** Encrypt plaintext with AES-256-GCM. Generates a random 12-byte nonce. */
  aesGcmEncrypt(
    key: Uint8Array,
    plaintext: Uint8Array,
    aad?: Uint8Array,
  ): Promise<{ ciphertext: Uint8Array; nonce: Uint8Array }>;

  /** Decrypt ciphertext with AES-256-GCM. Throws on authentication failure. */
  aesGcmDecrypt(
    key: Uint8Array,
    ciphertext: Uint8Array,
    nonce: Uint8Array,
    aad?: Uint8Array,
  ): Promise<Uint8Array>;

  // ─── Key Derivation ────────────────────────────────────────

  /** HKDF-SHA256: derive `length` bytes from input keying material */
  hkdf(
    ikm: Uint8Array,
    salt: Uint8Array,
    info: Uint8Array,
    length: number,
  ): Promise<Uint8Array>;

  /** HMAC-SHA256: returns 32-byte MAC */
  hmacSha256(key: Uint8Array, data: Uint8Array): Promise<Uint8Array>;

  // ─── Random ────────────────────────────────────────────────

  /** Generate cryptographically secure random bytes */
  randomBytes(length: number): Uint8Array;

  // ─── Hardening ─────────────────────────────────────────────

  /**
   * Constant-time byte array comparison. No early exit on mismatch.
   * Use this for ALL comparisons involving secret data (keys, MACs, tokens).
   * Returns false if lengths differ (timing leak on length is acceptable).
   */
  constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean;

  /**
   * Overwrite a buffer with zeros. Use after a key is no longer needed.
   * Note: V8 may not actually zero the backing memory due to GC/optimization,
   * but this is the best we can do in JS/TS without native bindings.
   */
  zeroize(buf: Uint8Array): void;

  /**
   * Cryptographically secure random 32-bit unsigned integer.
   * Use for registrationId, keyId generation, etc. (NEVER Math.random()).
   */
  randomUint32(): number;
}
