// Auto-generated by scripts/gen_parity_tests.py from docs/runtime-parity.md.
// Module: node:crypto
// This file is a byte-for-byte parity test against `node --experimental-strip-types`.
// Edit the TODO lines below to exercise each API; rerun
// `./run_parity_tests.sh --filter test_parity_crypto` to verify.
//
// @covers
// crates/perry-stdlib/src/crypto.rs:
//   - js_crypto_create_hash
//   - js_crypto_md5
//   - js_crypto_pbkdf2_bytes
//   - js_crypto_sha256
//   - js_crypto_sha256_bytes
// crates/perry-stdlib/src/crypto_e2e.rs:
//   - js_crypto_hkdf_sha256
// crates/perry-stdlib/src/webcrypto.rs:
//   - js_webcrypto_digest
//   - js_webcrypto_import_key
//   - js_webcrypto_sign
//   - js_webcrypto_verify

import * as crypto from "node:crypto";

// ── Functions ──

// PERRY-SKIP: crypto.checkPrime / checkPrimeSync — BigInt arg path; not exercised
// PERRY-SKIP: crypto.createDiffieHellman / createDiffieHellmanGroup / createECDH — DH/ECDH instance APIs not implemented in Perry
// PERRY-SKIP: crypto.createPrivateKey / createPublicKey / createSign / createVerify — need static valid PEM
// PERRY-SKIP: crypto.diffieHellman — needs key objects
// PERRY-SKIP: crypto.generateKey* / generateKeyPair* / randomBytes / randomFill* / randomInt / randomUUID* — non-deterministic
// PERRY-SKIP: crypto.generatePrime / generatePrimeSync — non-deterministic
// PERRY-SKIP: crypto.getRandomValues — non-deterministic
// PERRY-SKIP: crypto.getDiffieHellman — DH instance API
// PERRY-SKIP: crypto.privateDecrypt / privateEncrypt / publicDecrypt / publicEncrypt — need static keys
// PERRY-SKIP: crypto.setEngine / crypto.setFips — global state mutators
// PERRY-SKIP: crypto.sign / crypto.verify — need static keypair
// PERRY-SKIP: crypto.argon2 / argon2Sync — Node v22+ only
// PERRY-SKIP: crypto.encapsulate / decapsulate — PQC, Node v22+

// Hash family
try {
  console.log("sha1 hex:", crypto.createHash("sha1").update("abc").digest("hex"));
} catch (e: any) { console.log("sha1 error:", e.message); }
try {
  console.log("sha256 hex:", crypto.createHash("sha256").update("abc").digest("hex"));
} catch (e: any) { console.log("sha256 error:", e.message); }
try {
  console.log("sha384 hex:", crypto.createHash("sha384").update("abc").digest("hex"));
} catch (e: any) { console.log("sha384 error:", e.message); }
try {
  console.log("sha512 hex:", crypto.createHash("sha512").update("abc").digest("hex"));
} catch (e: any) { console.log("sha512 error:", e.message); }
try {
  console.log("md5 hex:", crypto.createHash("md5").update("abc").digest("hex"));
} catch (e: any) { console.log("md5 error:", e.message); }
try {
  console.log("sha256 base64:", crypto.createHash("sha256").update("hello world").digest("base64"));
} catch (e: any) { console.log("sha256 base64 error:", e.message); }
// PERRY-SKIP: hash.copy — Perry hash chain doesn't support .copy() yet
// PERRY-SKIP: hash.update chain — multi-update chain not supported on all backends
try {
  console.log("hash.update chain:", crypto.createHash("sha256").update("a").update("b").update("c").digest("hex"));
} catch (e: any) { console.log("hash.update chain error:", e.message); }

// HMAC family
try {
  console.log("hmac sha256:", crypto.createHmac("sha256", "key").update("abc").digest("hex"));
} catch (e: any) { console.log("hmac sha256 error:", e.message); }
try {
  console.log("hmac sha1:", crypto.createHmac("sha1", "key").update("abc").digest("hex"));
} catch (e: any) { console.log("hmac sha1 error:", e.message); }
try {
  console.log("hmac sha512:", crypto.createHmac("sha512", "key").update("hello world").digest("hex"));
} catch (e: any) { console.log("hmac sha512 error:", e.message); }
try {
  console.log("hmac base64:", crypto.createHmac("sha256", "key").update("abc").digest("base64"));
} catch (e: any) { console.log("hmac base64 error:", e.message); }
try {
  console.log("hmac update chain:", crypto.createHmac("sha256", "key").update("a").update("b").update("c").digest("hex"));
} catch (e: any) { console.log("hmac update chain error:", e.message); }

// crypto.hash (one-shot)
try {
  console.log("crypto.hash sha256:", crypto.hash("sha256", "abc", "hex"));
} catch (e: any) { console.log("crypto.hash sha256 error:", e.message); }
try {
  console.log("crypto.hash md5:", crypto.hash("md5", "abc", "hex"));
} catch (e: any) { console.log("crypto.hash md5 error:", e.message); }

// Cipher / Decipher AES-256-CBC
try {
  const keyCbc = Buffer.from("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "hex");
  const ivCbc = Buffer.from("0123456789abcdef0123456789abcdef", "hex");
  const cipher = crypto.createCipheriv("aes-256-cbc", keyCbc, ivCbc);
  let enc = cipher.update("hello", "utf8", "hex");
  enc += cipher.final("hex");
  console.log("aes-256-cbc enc:", enc);
  const decipher = crypto.createDecipheriv("aes-256-cbc", keyCbc, ivCbc);
  let dec = decipher.update(enc, "hex", "utf8");
  dec += decipher.final("utf8");
  console.log("aes-256-cbc dec:", dec);
} catch (e: any) {
  console.log("aes-256-cbc error:", e.message);
}

// Cipher / Decipher AES-256-GCM
try {
  const keyGcm = Buffer.from("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "hex");
  const ivGcm = Buffer.from("0123456789abcdef0123456789abcdef", "hex");
  const cipher = crypto.createCipheriv("aes-256-gcm", keyGcm, ivGcm);
  cipher.setAAD(Buffer.from("aad"));
  let enc = cipher.update("hello", "utf8", "hex");
  enc += cipher.final("hex");
  const tag = cipher.getAuthTag().toString("hex");
  console.log("aes-256-gcm enc:", enc);
  console.log("aes-256-gcm tag:", tag);
  const decipher = crypto.createDecipheriv("aes-256-gcm", keyGcm, ivGcm);
  decipher.setAAD(Buffer.from("aad"));
  decipher.setAuthTag(Buffer.from(tag, "hex"));
  let dec = decipher.update(enc, "hex", "utf8");
  dec += decipher.final("utf8");
  console.log("aes-256-gcm dec:", dec);
} catch (e: any) {
  console.log("aes-256-gcm error:", e.message);
}

// cipher.setAutoPadding
try {
  const k = Buffer.from("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", "hex");
  const iv = Buffer.from("0123456789abcdef0123456789abcdef", "hex");
  const cipher = crypto.createCipheriv("aes-256-cbc", k, iv);
  cipher.setAutoPadding(true);
  let enc = cipher.update("hello world 1234", "utf8", "hex");
  enc += cipher.final("hex");
  console.log("cipher.setAutoPadding(true) enc:", enc);
} catch (e: any) {
  console.log("cipher.setAutoPadding error:", e.message);
}

// crypto.createSecretKey + KeyObject
try {
  const sk = crypto.createSecretKey(Buffer.from("0123456789abcdef0123456789abcdef"));
  console.log("createSecretKey type:", sk.type);
  console.log("createSecretKey symmetricKeySize:", sk.symmetricKeySize);
  console.log("keyObject.asymmetricKeyType:", sk.asymmetricKeyType);
  const sk2 = crypto.createSecretKey(Buffer.from("0123456789abcdef0123456789abcdef"));
  console.log("keyObject.equals(same):", sk.equals(sk2));
  const sk3 = crypto.createSecretKey(Buffer.from("ffffffffffffffffffffffffffffffff"));
  console.log("keyObject.equals(diff):", sk.equals(sk3));
  console.log("keyObject.export hex:", sk.export().toString("hex"));
} catch (e: any) {
  console.log("createSecretKey/KeyObject error:", e.message);
}

// PERRY-SKIP: KeyObject.from — Node CryptoKey wrapper signature varies by version
// PERRY-SKIP: keyObject.toCryptoKey — Node v22+ only
// PERRY-SKIP: keyObject.asymmetricKeyDetails — needs asymmetric key

// PERRY-SKIP: crypto.getCipherInfo — Node prints `[Object: null prototype] { ... }` which Perry won't match
// try { console.log("getCipherInfo aes-256-cbc:", crypto.getCipherInfo("aes-256-cbc")); } catch (e: any) { console.log("getCipherInfo error:", e.message); }

// getCiphers / getCurves / getHashes — only print length + first 5 joined
try {
  const ciphers = crypto.getCiphers();
  console.log("getCiphers length:", ciphers.length, "first5:", ciphers.slice(0, 5).join(","));
} catch (e: any) { console.log("getCiphers error:", e.message); }
try {
  const curves = crypto.getCurves();
  console.log("getCurves length:", curves.length, "first5:", curves.slice(0, 5).join(","));
} catch (e: any) { console.log("getCurves error:", e.message); }
try {
  const hashes = crypto.getHashes();
  console.log("getHashes length:", hashes.length, "first5:", hashes.slice(0, 5).join(","));
} catch (e: any) { console.log("getHashes error:", e.message); }

// HKDF
try {
  const out = crypto.hkdfSync("sha256", "ikm", "salt", "info", 32);
  console.log("hkdfSync hex:", Buffer.from(out).toString("hex"));
} catch (e: any) { console.log("hkdfSync error:", e.message); }

// PBKDF2
try {
  const out = crypto.pbkdf2Sync("password", "salt", 1000, 32, "sha256");
  console.log("pbkdf2Sync hex:", out.toString("hex"));
} catch (e: any) { console.log("pbkdf2Sync error:", e.message); }
try {
  const out2 = crypto.pbkdf2Sync("password", "salt", 100, 16, "sha512");
  console.log("pbkdf2Sync sha512 16:", out2.toString("hex"));
} catch (e: any) { console.log("pbkdf2Sync sha512 error:", e.message); }

// scrypt (deterministic but slow)
try {
  const out = crypto.scryptSync("password", "salt", 32);
  console.log("scryptSync hex:", out.toString("hex"));
} catch (e: any) { console.log("scryptSync error:", e.message); }

// PERRY-SKIP: crypto.secureHeapUsed — Node returns {total,used,utilization:NaN,min:2}, formatting varies
try {
  const s = crypto.secureHeapUsed();
  console.log("secureHeapUsed total:", s.total, "used:", s.used);
} catch (e: any) { console.log("secureHeapUsed error:", e.message); }

try { console.log("crypto.getFips():", crypto.getFips()); } catch (e: any) { console.log("crypto.getFips error:", e.message); }

// timingSafeEqual
try {
  const a = Buffer.from("0123456789abcdef", "hex");
  const b = Buffer.from("0123456789abcdef", "hex");
  const c = Buffer.from("0123456789abcdee", "hex");
  console.log("timingSafeEqual equal:", crypto.timingSafeEqual(a, b));
  console.log("timingSafeEqual diff:", crypto.timingSafeEqual(a, c));
} catch (e: any) { console.log("timingSafeEqual error:", e.message); }

// ── Module Properties ──
try { console.log("typeof crypto.constants:", typeof crypto.constants); } catch (e: any) { console.log("typeof crypto.constants error:", e.message); }
try {
  console.log("crypto.constants.RSA_PKCS1_PADDING:", crypto.constants.RSA_PKCS1_PADDING);
} catch (e: any) { console.log("crypto.constants.RSA_PKCS1_PADDING error:", e.message); }
try { console.log("typeof crypto.fips:", typeof crypto.fips); } catch (e: any) { console.log("typeof crypto.fips error:", e.message); }
try { console.log("typeof crypto.subtle:", typeof crypto.subtle); } catch (e: any) { console.log("typeof crypto.subtle error:", e.message); }
try { console.log("typeof crypto.webcrypto:", typeof crypto.webcrypto); } catch (e: any) { console.log("typeof crypto.webcrypto error:", e.message); }

// ── Classes ──

// PERRY-SKIP: crypto.Certificate / Certificate.exportChallenge / exportPublicKey / verifySpkac — legacy SPKAC
// PERRY-SKIP: Cipher / Decipher class instance — exercised via createCipheriv/createDecipheriv
// PERRY-SKIP: DiffieHellman class (computeSecret/generateKeys/getGenerator/getPrime/getPrivateKey/getPublicKey/setPrivateKey/setPublicKey/verifyError) — DH not implemented
// PERRY-SKIP: DiffieHellmanGroup
// PERRY-SKIP: ECDH class (convertKey/computeSecret/generateKeys/getPrivateKey/getPublicKey/setPrivateKey/setPublicKey) — ECDH not implemented
// PERRY-SKIP: Hash class (copy/digest/update) — exercised via createHash above
// PERRY-SKIP: Hmac class (digest/update) — exercised via createHmac above
// PERRY-SKIP: Sign class (sign/update) — needs static keypair
// PERRY-SKIP: Verify class (verify/update) — needs static keypair
// PERRY-SKIP: X509Certificate + all x509.* properties/methods — needs static cert blob

// ── WebCrypto ──

async function runWebCrypto() {
  // crypto.subtle.digest
  try {
    const enc = new TextEncoder();
    const data = enc.encode("abc");
    const digest = await crypto.subtle.digest("SHA-256", data);
    console.log("subtle.digest SHA-256:", Buffer.from(new Uint8Array(digest)).toString("hex"));
  } catch (e: any) { console.log("subtle.digest SHA-256 error:", e.message); }
  try {
    const enc = new TextEncoder();
    const data = enc.encode("abc");
    const d1 = await crypto.subtle.digest("SHA-1", data);
    console.log("subtle.digest SHA-1:", Buffer.from(new Uint8Array(d1)).toString("hex"));
  } catch (e: any) { console.log("subtle.digest SHA-1 error:", e.message); }
  try {
    const enc = new TextEncoder();
    const data = enc.encode("abc");
    const d384 = await crypto.subtle.digest("SHA-384", data);
    console.log("subtle.digest SHA-384:", Buffer.from(new Uint8Array(d384)).toString("hex"));
  } catch (e: any) { console.log("subtle.digest SHA-384 error:", e.message); }
  try {
    const enc = new TextEncoder();
    const data = enc.encode("abc");
    const d512 = await crypto.subtle.digest("SHA-512", data);
    console.log("subtle.digest SHA-512:", Buffer.from(new Uint8Array(d512)).toString("hex"));
  } catch (e: any) { console.log("subtle.digest SHA-512 error:", e.message); }

  // crypto.subtle.importKey + sign + verify (HMAC, deterministic)
  try {
    const enc = new TextEncoder();
    const keyBytes = enc.encode("secret-key");
    const key = await crypto.subtle.importKey(
      "raw",
      keyBytes,
      { name: "HMAC", hash: "SHA-256" },
      false,
      ["sign", "verify"],
    );
    const data = enc.encode("abc");
    const sig = await crypto.subtle.sign("HMAC", key, data);
    console.log("subtle.sign HMAC SHA-256:", Buffer.from(new Uint8Array(sig)).toString("hex"));
    const ok = await crypto.subtle.verify("HMAC", key, sig, data);
    console.log("subtle.verify HMAC ok:", ok);
    const bad = await crypto.subtle.verify("HMAC", key, sig, enc.encode("abd"));
    console.log("subtle.verify HMAC bad:", bad);
  } catch (e: any) { console.log("subtle HMAC error:", e.message); }
}

await runWebCrypto();

try { console.log("typeof crypto.webcrypto.subtle:", typeof crypto.webcrypto.subtle); } catch (e: any) { console.log("typeof crypto.webcrypto.subtle error:", e.message); }
try { console.log("typeof crypto.webcrypto.CryptoKey:", typeof crypto.webcrypto.CryptoKey); } catch (e: any) { console.log("typeof crypto.webcrypto.CryptoKey error:", e.message); }

// Coverage: 9 auto-emitted, 129 TODO, 9 skip-listed.
