// Task 2: crypto.subtle capability probe on Deno
const enc = new TextEncoder();
function hex(b: ArrayBuffer|Uint8Array){const u=b instanceof Uint8Array?b:new Uint8Array(b);return [...u].map(x=>x.toString(16).padStart(2,'0')).join('');}
const results: string[] = [];
function ok(n:string,d:string){results.push(`PASS ${n}: ${d}`);}
function bad(n:string,e:unknown){results.push(`FAIL ${n}: ${(e as Error).name}: ${(e as Error).message}`);}

// 2.1 ECDH P-256 generateKey + deriveBits
let asKp: CryptoKeyPair, uaKp: CryptoKeyPair;
try {
  asKp = await crypto.subtle.generateKey({name:'ECDH',namedCurve:'P-256'}, true, ['deriveBits']);
  uaKp = await crypto.subtle.generateKey({name:'ECDH',namedCurve:'P-256'}, true, ['deriveBits']);
  ok('generateKey ECDH P-256', `asKp.privateKey.algorithm=${JSON.stringify(asKp.privateKey.algorithm)} usages=${asKp.privateKey.usages}`);
} catch(e){ bad('generateKey ECDH P-256', e); throw e; }

// export raw public (65 bytes uncompressed)
let asPubRaw: Uint8Array, uaPubRaw: Uint8Array;
try {
  asPubRaw = new Uint8Array(await crypto.subtle.exportKey('raw', asKp.publicKey));
  uaPubRaw = new Uint8Array(await crypto.subtle.exportKey('raw', uaKp.publicKey));
  ok('exportKey raw pub', `len=${asPubRaw.length} firstByte=0x${asPubRaw[0].toString(16)} (65/0x04 expected)`);
} catch(e){ bad('exportKey raw pub', e); throw e; }

// 2.2 importKey raw 65-byte uncompressed P-256 public
let uaPubImported: CryptoKey;
try {
  uaPubImported = await crypto.subtle.importKey('raw', uaPubRaw, {name:'ECDH',namedCurve:'P-256'}, false, []);
  ok('importKey raw 65B ECDH pub (usages=[])', `algorithm=${JSON.stringify(uaPubImported.algorithm)} type=${uaPubImported.type}`);
} catch(e){ bad('importKey raw 65B ECDH pub (usages=[])', e); throw e; }

// 2.3 deriveBits ECDH -> 256 bits shared secret
let ecdhSecret: Uint8Array;
try {
  ecdhSecret = new Uint8Array(await crypto.subtle.deriveBits({name:'ECDH', public: uaPubImported}, asKp.privateKey, 256));
  // reverse direction must match
  const asPubImported = await crypto.subtle.importKey('raw', asPubRaw, {name:'ECDH',namedCurve:'P-256'}, false, []);
  const rev = new Uint8Array(await crypto.subtle.deriveBits({name:'ECDH', public: asPubImported}, uaKp.privateKey, 256));
  ok('deriveBits ECDH 256', `len=${ecdhSecret.length} sym=${hex(ecdhSecret)===hex(rev)} secret=${hex(ecdhSecret)}`);
} catch(e){ bad('deriveBits ECDH 256', e); throw e; }

// 2.4 HMAC-SHA256 importKey raw + sign
try {
  const k = await crypto.subtle.importKey('raw', enc.encode('auth-secret-16by'), {name:'HMAC',hash:'SHA-256'}, false, ['sign']);
  const sig = new Uint8Array(await crypto.subtle.sign('HMAC', k, ecdhSecret));
  ok('HMAC-SHA256 importKey+sign', `len=${sig.length} out=${hex(sig)}`);
} catch(e){ bad('HMAC-SHA256 importKey+sign', e); }

// 2.5 HKDF importKey + deriveBits
try {
  const ikm = await crypto.subtle.importKey('raw', ecdhSecret, 'HKDF', false, ['deriveBits']);
  const salt = crypto.getRandomValues(new Uint8Array(16));
  const cek = new Uint8Array(await crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-256',salt,info:enc.encode('Content-Encoding: aes128gcm\0')}, ikm, 128));
  const nonce = new Uint8Array(await crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-256',salt,info:enc.encode('Content-Encoding: nonce\0')}, ikm, 96));
  ok('HKDF importKey+deriveBits', `cek(16)=${hex(cek)} nonce(12)=${hex(nonce)}`);
} catch(e){ bad('HKDF importKey+deriveBits', e); }

// 2.6 AES-GCM encrypt 128-bit key, 12-byte IV, 16-byte tag
try {
  const key = await crypto.subtle.importKey('raw', crypto.getRandomValues(new Uint8Array(16)), 'AES-GCM', false, ['encrypt']);
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const pt = enc.encode('hello');
  const ct = new Uint8Array(await crypto.subtle.encrypt({name:'AES-GCM', iv, tagLength:128}, key, pt));
  ok('AES-GCM encrypt', `ptLen=${pt.length} ctLen=${ct.length} (=pt+16 tag appended => ${ct.length===pt.length+16})`);
} catch(e){ bad('AES-GCM encrypt', e); }

// 2.7 HKDF with 0-length salt? (edge) and HKDF-extract-via-HMAC equivalence check
try {
  const salt = crypto.getRandomValues(new Uint8Array(16));
  const ikmBytes = ecdhSecret;
  // manual HKDF: PRK = HMAC(salt, ikm); OKM = HMAC(PRK, info||0x01)
  const sk = await crypto.subtle.importKey('raw', salt, {name:'HMAC',hash:'SHA-256'}, false, ['sign']);
  const prk = new Uint8Array(await crypto.subtle.sign('HMAC', sk, ikmBytes));
  const pk = await crypto.subtle.importKey('raw', prk, {name:'HMAC',hash:'SHA-256'}, false, ['sign']);
  const info = enc.encode('Content-Encoding: aes128gcm\0');
  const buf = new Uint8Array(info.length+1); buf.set(info); buf[info.length]=1;
  const manual = new Uint8Array(await crypto.subtle.sign('HMAC', pk, buf)).slice(0,16);
  const native = new Uint8Array(await crypto.subtle.deriveBits({name:'HKDF',hash:'SHA-256',salt,info}, await crypto.subtle.importKey('raw', ikmBytes, 'HKDF', false, ['deriveBits']), 128));
  ok('HKDF native == manual HMAC construction', `manual=${hex(manual)} native=${hex(native)} equal=${hex(manual)===hex(native)}`);
} catch(e){ bad('HKDF native == manual', e); }

console.log(results.join('\n'));
