//! WASM bindings for quicproquo crypto primitives.
//!
//! Exposes Ed25519 identity keys, hybrid X25519+ML-KEM-768, safety numbers,
//! sealed sender envelopes, and message padding to JavaScript via wasm-bindgen.

use wasm_bindgen::prelude::*;

// ── Identity (Ed25519) ──────────────────────────────────────────────────────

/// Generate a fresh Ed25519 identity keypair. Returns the 32-byte seed.
#[wasm_bindgen]
pub fn generate_identity() -> Vec<u8> {
    let kp = quicproquo_core::IdentityKeypair::generate();
    kp.seed_bytes().to_vec()
}

/// Derive the 32-byte Ed25519 public key from a 32-byte seed.
#[wasm_bindgen]
pub fn identity_public_key(seed: &[u8]) -> Result<Vec<u8>, JsError> {
    let seed: [u8; 32] = seed
        .try_into()
        .map_err(|_| JsError::new("seed must be exactly 32 bytes"))?;
    let kp = quicproquo_core::IdentityKeypair::from_seed(seed);
    Ok(kp.public_key_bytes().to_vec())
}

/// Sign a message with an Ed25519 seed. Returns the 64-byte signature.
#[wasm_bindgen]
pub fn sign(seed: &[u8], message: &[u8]) -> Result<Vec<u8>, JsError> {
    let seed: [u8; 32] = seed
        .try_into()
        .map_err(|_| JsError::new("seed must be exactly 32 bytes"))?;
    let kp = quicproquo_core::IdentityKeypair::from_seed(seed);
    Ok(kp.sign_raw(message).to_vec())
}

/// Verify an Ed25519 signature. Returns true if valid.
#[wasm_bindgen]
pub fn verify(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<bool, JsError> {
    let pk: [u8; 32] = public_key
        .try_into()
        .map_err(|_| JsError::new("public key must be exactly 32 bytes"))?;
    let sig: [u8; 64] = signature
        .try_into()
        .map_err(|_| JsError::new("signature must be exactly 64 bytes"))?;
    match quicproquo_core::IdentityKeypair::verify_raw(&pk, message, &sig) {
        Ok(()) => Ok(true),
        Err(_) => Ok(false),
    }
}

// ── Hybrid KEM (X25519 + ML-KEM-768) ───────────────────────────────────────

/// Generate a hybrid keypair. Returns serialized bytes:
/// private_key(2432) || public_key(1216).
///
/// Private key layout: x25519_sk(32) || mlkem_dk(2400)
/// Public key layout: x25519_pk(32) || mlkem_ek(1184)
#[wasm_bindgen]
pub fn hybrid_generate_keypair() -> Vec<u8> {
    let kp = quicproquo_core::HybridKeypair::generate();
    let private_bytes = kp.private_to_bytes(); // 2432 bytes
    let public_bytes = kp.public_key().to_bytes(); // 1216 bytes
    let mut out = Vec::with_capacity(private_bytes.len() + public_bytes.len());
    out.extend_from_slice(&private_bytes);
    out.extend_from_slice(&public_bytes);
    out
}

/// Extract the public key (1216 bytes) from a hybrid keypair blob.
#[wasm_bindgen]
pub fn hybrid_public_key(keypair_blob: &[u8]) -> Result<Vec<u8>, JsError> {
    // Private key is 2432 bytes, public key starts at offset 2432
    if keypair_blob.len() != 2432 + 1216 {
        return Err(JsError::new("keypair blob must be exactly 3648 bytes"));
    }
    Ok(keypair_blob[2432..].to_vec())
}

/// Encrypt plaintext to a hybrid public key (1216 bytes).
/// Returns the encrypted envelope.
#[wasm_bindgen]
pub fn hybrid_encrypt(public_key: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, JsError> {
    let pk = quicproquo_core::HybridPublicKey::from_bytes(public_key)
        .map_err(|e| JsError::new(&format!("invalid hybrid public key: {e}")))?;
    quicproquo_core::hybrid_encrypt(&pk, plaintext, b"", b"")
        .map_err(|e| JsError::new(&format!("encryption failed: {e}")))
}

/// Decrypt a hybrid envelope using the private key portion (first 2432 bytes of keypair blob).
#[wasm_bindgen]
pub fn hybrid_decrypt(keypair_blob: &[u8], envelope: &[u8]) -> Result<Vec<u8>, JsError> {
    // Accept either full blob (3648) or just private key (2432)
    let private_bytes = if keypair_blob.len() == 2432 + 1216 {
        &keypair_blob[..2432]
    } else if keypair_blob.len() == 2432 {
        keypair_blob
    } else {
        return Err(JsError::new(
            "keypair must be 2432 (private only) or 3648 (full blob) bytes",
        ));
    };
    let kp = quicproquo_core::HybridKeypair::from_private_bytes(private_bytes)
        .map_err(|e| JsError::new(&format!("invalid hybrid private key: {e}")))?;
    quicproquo_core::hybrid_decrypt(&kp, envelope, b"", b"")
        .map_err(|e| JsError::new(&format!("decryption failed: {e}")))
}

// ── Safety numbers ──────────────────────────────────────────────────────────

/// Compute a 60-digit safety number from two 32-byte Ed25519 public keys.
/// Returns a string of 12 space-separated 5-digit groups.
/// Result is symmetric: compute_safety_number(a, b) === compute_safety_number(b, a).
#[wasm_bindgen]
pub fn compute_safety_number(key_a: &[u8], key_b: &[u8]) -> Result<String, JsError> {
    let a: [u8; 32] = key_a
        .try_into()
        .map_err(|_| JsError::new("key_a must be exactly 32 bytes"))?;
    let b: [u8; 32] = key_b
        .try_into()
        .map_err(|_| JsError::new("key_b must be exactly 32 bytes"))?;
    Ok(quicproquo_core::safety_numbers::compute_safety_number(&a, &b))
}

// ── Sealed sender ───────────────────────────────────────────────────────────

/// Wrap a payload in a sealed sender envelope (magic + sender key + Ed25519 signature + payload).
/// `seed` is the 32-byte Ed25519 identity seed.
#[wasm_bindgen]
pub fn seal(seed: &[u8], payload: &[u8]) -> Result<Vec<u8>, JsError> {
    let seed: [u8; 32] = seed
        .try_into()
        .map_err(|_| JsError::new("seed must be exactly 32 bytes"))?;
    let kp = quicproquo_core::IdentityKeypair::from_seed(seed);
    Ok(quicproquo_core::sealed_sender::seal(&kp, payload))
}

/// Unseal a sealed sender envelope. Returns sender_public_key(32) || inner_payload.
#[wasm_bindgen]
pub fn unseal(envelope: &[u8]) -> Result<Vec<u8>, JsError> {
    let (sender_key, inner) = quicproquo_core::sealed_sender::unseal(envelope)
        .map_err(|e| JsError::new(&format!("unseal failed: {e}")))?;
    let mut out = Vec::with_capacity(32 + inner.len());
    out.extend_from_slice(&sender_key);
    out.extend_from_slice(&inner);
    Ok(out)
}

// ── Padding ─────────────────────────────────────────────────────────────────

/// Pad a message to a fixed bucket size (256, 1024, 4096, or 16384 bytes).
#[wasm_bindgen]
pub fn pad_message(data: &[u8]) -> Vec<u8> {
    quicproquo_core::padding::pad(data)
}

/// Remove padding and recover the original message.
#[wasm_bindgen]
pub fn unpad_message(data: &[u8]) -> Result<Vec<u8>, JsError> {
    quicproquo_core::padding::unpad(data)
        .map_err(|e| JsError::new(&format!("unpad failed: {e}")))
}
