//! WASM-compatible cryptography for Synor web wallet.
//!
//! This crate provides cryptographic primitives that can be compiled to WebAssembly
//! for use in the Synor web wallet. It includes:
//!
//! - Ed25519 signature generation and verification
//! - Key derivation from BIP-39 mnemonics
//! - Bech32m address encoding
//! - Dilithium3 (ML-DSA-65) post-quantum signatures
//!
//! # Usage in JavaScript
//!
//! ```javascript
//! import init, { Keypair, Mnemonic, DilithiumSigningKey } from 'synor-crypto-wasm';
//!
//! await init();
//!
//! // Generate mnemonic
//! const mnemonic = Mnemonic.generate(24);
//! console.log(mnemonic.phrase());
//!
//! // Create Ed25519 keypair from mnemonic
//! const keypair = Keypair.fromMnemonic(mnemonic.phrase(), "");
//! console.log(keypair.address("mainnet"));
//!
//! // Sign and verify with Ed25519
//! const message = new TextEncoder().encode("Hello Synor!");
//! const signature = keypair.sign(message);
//! const isValid = keypair.verify(message, signature);
//!
//! // Post-quantum signatures with Dilithium3
//! const pqKey = new DilithiumSigningKey();
//! const pqSignature = pqKey.sign(message);
//! const pqValid = pqKey.verify(message, pqSignature);
//! ```

use ed25519_dalek::{Signature, Signer, SigningKey, VerifyingKey};
use rand::rngs::OsRng;
use sha3::{Digest, Sha3_256};
use wasm_bindgen::prelude::*;
use zeroize::Zeroize;

mod address;
mod dilithium_wasm;
mod mnemonic_wasm;

pub use address::*;
pub use dilithium_wasm::*;
pub use mnemonic_wasm::*;

/// Initialize the WASM module.
#[wasm_bindgen(start)]
pub fn init() {
    // WASM initialization - can be extended for panic hooks, logging, etc.
}

/// Ed25519 keypair for signing transactions.
#[wasm_bindgen]
pub struct Keypair {
    signing_key: SigningKey,
    #[wasm_bindgen(skip)]
    pub seed: [u8; 32],
}

#[wasm_bindgen]
impl Keypair {
    /// Generate a new random keypair.
    #[wasm_bindgen(constructor)]
    pub fn new() -> Result<Keypair, JsValue> {
        let signing_key = SigningKey::generate(&mut OsRng);
        let seed = signing_key.to_bytes();
        Ok(Keypair { signing_key, seed })
    }

    /// Create a keypair from a 32-byte seed.
    #[wasm_bindgen(js_name = fromSeed)]
    pub fn from_seed(seed: &[u8]) -> Result<Keypair, JsValue> {
        if seed.len() != 32 {
            return Err(JsValue::from_str("Seed must be exactly 32 bytes"));
        }
        let mut seed_arr = [0u8; 32];
        seed_arr.copy_from_slice(seed);
        let signing_key = SigningKey::from_bytes(&seed_arr);
        Ok(Keypair {
            signing_key,
            seed: seed_arr,
        })
    }

    /// Create a keypair from a BIP-39 mnemonic phrase.
    #[wasm_bindgen(js_name = fromMnemonic)]
    pub fn from_mnemonic(phrase: &str, passphrase: &str) -> Result<Keypair, JsValue> {
        let mnemonic = bip39::Mnemonic::from_phrase(phrase, bip39::Language::English)
            .map_err(|e| JsValue::from_str(&format!("Invalid mnemonic: {:?}", e)))?;

        // Derive seed from mnemonic
        let seed = bip39::Seed::new(&mnemonic, passphrase);
        let seed_bytes = seed.as_bytes();

        // Use first 32 bytes for Ed25519
        let mut ed_seed = [0u8; 32];
        ed_seed.copy_from_slice(&seed_bytes[..32]);

        let signing_key = SigningKey::from_bytes(&ed_seed);
        Ok(Keypair {
            signing_key,
            seed: ed_seed,
        })
    }

    /// Get the public key as hex string.
    #[wasm_bindgen(js_name = publicKeyHex)]
    pub fn public_key_hex(&self) -> String {
        hex::encode(self.signing_key.verifying_key().to_bytes())
    }

    /// Get the public key as bytes.
    #[wasm_bindgen(js_name = publicKeyBytes)]
    pub fn public_key_bytes(&self) -> Vec<u8> {
        self.signing_key.verifying_key().to_bytes().to_vec()
    }

    /// Get the Synor address for this keypair.
    #[wasm_bindgen]
    pub fn address(&self, network: &str) -> Result<String, JsValue> {
        let pubkey = self.signing_key.verifying_key().to_bytes();
        address::encode_address(network, &pubkey)
    }

    /// Sign a message.
    #[wasm_bindgen]
    pub fn sign(&self, message: &[u8]) -> Vec<u8> {
        let signature = self.signing_key.sign(message);
        signature.to_bytes().to_vec()
    }

    /// Verify a signature.
    #[wasm_bindgen]
    pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<bool, JsValue> {
        if signature.len() != 64 {
            return Err(JsValue::from_str("Signature must be 64 bytes"));
        }

        let sig_bytes: [u8; 64] = signature
            .try_into()
            .map_err(|_| JsValue::from_str("Invalid signature length"))?;
        let sig = Signature::from_bytes(&sig_bytes);

        use ed25519_dalek::Verifier;
        Ok(self
            .signing_key
            .verifying_key()
            .verify(message, &sig)
            .is_ok())
    }
}

impl Default for Keypair {
    fn default() -> Self {
        Self::new().expect("Failed to generate keypair")
    }
}

impl Drop for Keypair {
    fn drop(&mut self) {
        self.seed.zeroize();
    }
}

/// Verify a signature with a public key.
#[wasm_bindgen(js_name = verifyWithPublicKey)]
pub fn verify_with_public_key(
    public_key: &[u8],
    message: &[u8],
    signature: &[u8],
) -> Result<bool, JsValue> {
    if public_key.len() != 32 {
        return Err(JsValue::from_str("Public key must be 32 bytes"));
    }
    if signature.len() != 64 {
        return Err(JsValue::from_str("Signature must be 64 bytes"));
    }

    let pk_bytes: [u8; 32] = public_key
        .try_into()
        .map_err(|_| JsValue::from_str("Invalid public key"))?;
    let sig_bytes: [u8; 64] = signature
        .try_into()
        .map_err(|_| JsValue::from_str("Invalid signature"))?;

    let verifying_key = VerifyingKey::from_bytes(&pk_bytes)
        .map_err(|_| JsValue::from_str("Invalid public key format"))?;
    let signature = Signature::from_bytes(&sig_bytes);

    use ed25519_dalek::Verifier;
    Ok(verifying_key.verify(message, &signature).is_ok())
}

/// Compute SHA3-256 hash.
#[wasm_bindgen(js_name = sha3_256)]
pub fn sha3_256_hash(data: &[u8]) -> Vec<u8> {
    let mut hasher = Sha3_256::new();
    hasher.update(data);
    hasher.finalize().to_vec()
}

/// Compute BLAKE3 hash.
#[wasm_bindgen(js_name = blake3)]
pub fn blake3_hash(data: &[u8]) -> Vec<u8> {
    blake3::hash(data).as_bytes().to_vec()
}

/// Derive key using HKDF-SHA256.
#[wasm_bindgen(js_name = deriveKey)]
pub fn derive_key(
    input_key: &[u8],
    salt: &[u8],
    info: &[u8],
    output_len: usize,
) -> Result<Vec<u8>, JsValue> {
    use hkdf::Hkdf;
    use sha3::Sha3_256;

    let hk = Hkdf::<Sha3_256>::new(Some(salt), input_key);
    let mut output = vec![0u8; output_len];
    hk.expand(info, &mut output)
        .map_err(|_| JsValue::from_str("HKDF expansion failed"))?;
    Ok(output)
}

// bip39 crate is used in mnemonic_wasm.rs

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_keypair_generation() {
        let keypair = Keypair::new().unwrap();
        assert_eq!(keypair.public_key_bytes().len(), 32);
    }

    #[test]
    fn test_sign_verify() {
        let keypair = Keypair::new().unwrap();
        let message = b"Hello, Synor!";
        let signature = keypair.sign(message);
        assert!(keypair.verify(message, &signature).unwrap());
    }

    #[test]
    fn test_address_generation() {
        let keypair = Keypair::new().unwrap();
        let address = keypair.address("mainnet").unwrap();
        assert!(address.starts_with("synor1"));
    }

    #[test]
    fn test_mnemonic_keypair() {
        let phrase = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
        let keypair = Keypair::from_mnemonic(phrase, "").unwrap();
        assert_eq!(keypair.public_key_bytes().len(), 32);
    }
}
