//! Pinned cryptographic test vectors for host-wallet on WASM.
//!
//! Mirror of `pinned_vectors.rs` — same vectors, same assertions, different
//! test harness. Constants live in `vectors.rs`; both files share one source
//! of truth so a vector update here is immediately visible on native too.
//!
//! Run WASM: wasm-pack test --headless --chrome crates/host-wallet

#![cfg(target_arch = "wasm32")]

mod vectors;

use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);

use host_wallet::crypto;
use host_wallet::derive;

fn mnemonic() -> bip39::Mnemonic {
    bip39::Mnemonic::parse(vectors::MNEMONIC).unwrap()
}

// ---------------------------------------------------------------------------
// sr25519 key derivation
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn sr25519_root_pubkey_pinned() {
    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    assert_eq!(root.public.to_bytes(), vectors::ROOT_PUBKEY);
}

#[wasm_bindgen_test]
fn sr25519_app_pubkey_pinned() {
    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    let app = derive::app_keypair(&root, "com.test.app", 0);
    assert_eq!(app.public.to_bytes(), vectors::APP_PUBKEY);
}

#[wasm_bindgen_test]
fn sr25519_sso_pubkey_pinned() {
    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    let sso = derive::sso_keypair(&root);
    assert_eq!(sso.public.to_bytes(), vectors::SSO_PUBKEY);
}

#[wasm_bindgen_test]
fn sr25519_chat_pubkey_pinned() {
    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    let chat = derive::chat_keypair(&root);
    assert_eq!(chat.public.to_bytes(), vectors::CHAT_PUBKEY);
}

#[wasm_bindgen_test]
fn sr25519_chat_sign_and_verify() {
    const PAYLOAD: &[u8] = b"wasm-native-parity-test";
    const SIGNING_CTX: &[u8] = b"substrate";

    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    let chat = derive::chat_keypair(&root);

    let ctx = schnorrkel::signing_context(SIGNING_CTX);
    let sig = chat.sign(ctx.bytes(PAYLOAD));

    assert_eq!(chat.public.to_bytes(), vectors::CHAT_PUBKEY);
    assert!(
        chat.public.verify(ctx.bytes(PAYLOAD), &sig).is_ok(),
        "sr25519 chat signature verification failed"
    );
}

// ---------------------------------------------------------------------------
// sr25519 sign-and-verify
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn sr25519_app_sign_and_verify() {
    // sr25519 uses a Merlin transcript with randomness, so the signature bytes
    // are non-deterministic. We verify correctness by signing then verifying.
    const PAYLOAD: &[u8] = b"wasm-native-parity-test";
    const SIGNING_CTX: &[u8] = b"substrate";

    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    let app = derive::app_keypair(&root, "com.test.app", 0);

    let ctx = schnorrkel::signing_context(SIGNING_CTX);
    let sig = app.sign(ctx.bytes(PAYLOAD));

    // Public key must match the pinned vector — confirms we signed with the right key.
    assert_eq!(app.public.to_bytes(), vectors::APP_PUBKEY);

    // Verify the signature with the same public key.
    assert!(
        app.public.verify(ctx.bytes(PAYLOAD), &sig).is_ok(),
        "sr25519 signature verification failed"
    );
}

// ---------------------------------------------------------------------------
// SS58 addresses
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn ss58_generic_prefix_42() {
    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    assert_eq!(derive::ss58_address(&root.public), vectors::SS58_GENERIC);
}

#[wasm_bindgen_test]
fn ss58_polkadot_prefix_0() {
    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    assert_eq!(
        derive::ss58_address_with_prefix(&root.public.to_bytes(), 0),
        vectors::SS58_POLKADOT,
    );
}

#[wasm_bindgen_test]
fn ss58_phala_prefix_172_pinned() {
    // Prefix 172 exercises the two-byte canary encoding path (prefix >= 64).
    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    assert_eq!(
        derive::ss58_address_with_prefix(&root.public.to_bytes(), 172),
        vectors::SS58_PHALA,
    );
}

#[wasm_bindgen_test]
fn ss58_phala_prefix_172_canary_roundtrip() {
    // Verify the canary bytes decode back to prefix 172 and the pubkey is
    // correctly placed at bytes 2..34 in the decoded payload.
    let root = derive::root_keypair_from_mnemonic(&mnemonic());
    let addr = derive::ss58_address_with_prefix(&root.public.to_bytes(), 172);
    let decoded = bs58::decode(&addr).into_vec().unwrap();

    // Recover the prefix from the two canary bytes.
    let recovered = ((decoded[0] as u16 & 0b0011_1111) << 2)
        | ((decoded[1] as u16 & 0b1100_0000) >> 6)
        | ((decoded[1] as u16 & 0b0011_1111) << 8);
    assert_eq!(recovered, 172, "canary round-trip must yield prefix 172");

    // Public key occupies bytes 2..34 (two-byte prefix, 32-byte key, 2-byte checksum).
    assert_eq!(&decoded[2..34], &root.public.to_bytes());
}

// ---------------------------------------------------------------------------
// secp256k1 / ETH / BTC addresses
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn eth_address_pinned() {
    let key = derive::secp256k1::eth_key(&mnemonic());
    assert_eq!(derive::secp256k1::eth_address(&key), vectors::ETH_ADDRESS);
}

#[wasm_bindgen_test]
fn btc_address_pinned() {
    let key = derive::secp256k1::btc_key(&mnemonic());
    assert_eq!(
        derive::secp256k1::btc_address_p2wpkh(&key),
        vectors::BTC_ADDRESS
    );
}

// ---------------------------------------------------------------------------
// HKDF-SHA256 key derivation
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn hkdf_aes_key_pinned() {
    let key = crypto::derive_aes_key(&[0xABu8; 32]);
    assert_eq!(key, vectors::HKDF_KEY);
}

// ---------------------------------------------------------------------------
// HMAC-SHA256 topic derivation
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn hmac_topic_pinned() {
    let account_id = [0x01u8; 32];
    let p256_pubkey = [0x02u8; 65];

    let topic = crypto::derive_topic(&account_id, &p256_pubkey, b"topic");
    let request = crypto::derive_topic(&account_id, &p256_pubkey, b"request");
    let response = crypto::derive_topic(&account_id, &p256_pubkey, b"response");

    assert_eq!(hex::encode(topic), vectors::HMAC_TOPIC_HEX);
    assert_eq!(hex::encode(request), vectors::HMAC_REQUEST_HEX);
    assert_eq!(hex::encode(response), vectors::HMAC_RESPONSE_HEX);
}

// ---------------------------------------------------------------------------
// AES-128-GCM cross-platform decrypt
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn aes_gcm_cross_platform_decrypt() {
    let plaintext =
        crypto::decrypt_aes_gcm(&vectors::AES_GCM_KEY, &vectors::AES_GCM_CIPHERTEXT).unwrap();
    assert_eq!(plaintext, vectors::AES_GCM_PLAINTEXT);
}

#[wasm_bindgen_test]
fn aes_gcm_encrypt_deterministic() {
    let ct = crypto::encrypt_aes_gcm_with_nonce(
        &vectors::AES_GCM_KEY,
        &vectors::AES_GCM_NONCE,
        vectors::AES_GCM_PLAINTEXT,
    );
    assert_eq!(ct.as_slice(), &vectors::AES_GCM_CIPHERTEXT[..]);
}

#[wasm_bindgen_test]
fn aes_gcm_roundtrip() {
    let key = [0x42u8; 16];
    let plaintext = b"wasm roundtrip test";
    let encrypted = crypto::encrypt_aes_gcm(&key, plaintext);
    let decrypted = crypto::decrypt_aes_gcm(&key, &encrypted).unwrap();
    assert_eq!(decrypted, plaintext);
}

#[wasm_bindgen_test]
fn aes_gcm_wrong_key_fails() {
    let key1 = [0x42u8; 16];
    let key2 = [0x43u8; 16];
    let encrypted = crypto::encrypt_aes_gcm(&key1, b"test");
    assert!(crypto::decrypt_aes_gcm(&key2, &encrypted).is_err());
}

// ---------------------------------------------------------------------------
// SCALE compact encoding
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn scale_compact_boundaries() {
    assert_eq!(crypto::scale_compact_encode(0), vec![0x00]);
    assert_eq!(crypto::scale_compact_encode(1), vec![0x04]);
    assert_eq!(crypto::scale_compact_encode(63), vec![0xfc]);

    let enc64 = crypto::scale_compact_encode(64);
    assert_eq!(enc64.len(), 2);
    let val = u16::from_le_bytes([enc64[0], enc64[1]]);
    assert_eq!((val >> 2) as usize, 64);
    assert_eq!(val & 0b11, 0b01);
}

// ---------------------------------------------------------------------------
// P-256 ECDH → HKDF pinned vector
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn p256_ecdh_pinned_vector() {
    use p256::SecretKey;

    let sk_a = SecretKey::from_bytes((&vectors::P256_SECRET_A).into()).unwrap();
    let sk_b = SecretKey::from_bytes((&vectors::P256_SECRET_B).into()).unwrap();

    let pk_a = sk_a.public_key();
    let pk_b = sk_b.public_key();

    // ECDH is commutative: A×PK_B and B×PK_A must yield the same shared secret.
    let shared_a = p256::ecdh::diffie_hellman(sk_a.to_nonzero_scalar(), pk_b.as_affine());
    let shared_b = p256::ecdh::diffie_hellman(sk_b.to_nonzero_scalar(), pk_a.as_affine());

    assert_eq!(
        shared_a.raw_secret_bytes().as_slice(),
        shared_b.raw_secret_bytes().as_slice(),
        "ECDH must be commutative"
    );

    let aes_a = crypto::derive_aes_key(shared_a.raw_secret_bytes().as_slice());
    let aes_b = crypto::derive_aes_key(shared_b.raw_secret_bytes().as_slice());

    assert_eq!(aes_a, aes_b, "Both sides must derive the same AES key");
    assert_eq!(
        aes_a,
        vectors::P256_ECDH_AES_KEY,
        "AES key must match pinned vector"
    );
}

// ---------------------------------------------------------------------------
// Handshake encoding
// ---------------------------------------------------------------------------

#[wasm_bindgen_test]
fn encode_handshake_format() {
    let sr_key = [0x01u8; 32];
    let p256_key = [0x04u8; 65];
    let url = "https://example.com/metadata";
    let encoded = crypto::encode_handshake(&sr_key, &p256_key, url);
    assert_eq!(encoded.len(), 32 + 65 + 1 + url.len());
    assert_eq!(&encoded[..32], &sr_key);
    assert_eq!(&encoded[32..97], &p256_key);
    assert_eq!(encoded[97], (url.len() << 2) as u8);
}
