//! BIP-39 mnemonic support for WASM.

use bip39::{Language, Mnemonic as Bip39Mnemonic, MnemonicType};
use wasm_bindgen::prelude::*;

/// BIP-39 mnemonic phrase wrapper.
#[wasm_bindgen]
pub struct Mnemonic {
    inner: Bip39Mnemonic,
}

#[wasm_bindgen]
impl Mnemonic {
    /// Generate a new random mnemonic with the specified word count.
    #[wasm_bindgen(constructor)]
    pub fn new(word_count: u8) -> Result<Mnemonic, JsValue> {
        let mnemonic_type = match word_count {
            12 => MnemonicType::Words12,
            15 => MnemonicType::Words15,
            18 => MnemonicType::Words18,
            21 => MnemonicType::Words21,
            24 => MnemonicType::Words24,
            _ => {
                return Err(JsValue::from_str(
                    "Invalid word count. Must be 12, 15, 18, 21, or 24",
                ))
            }
        };

        let mnemonic = Bip39Mnemonic::new(mnemonic_type, Language::English);
        Ok(Mnemonic { inner: mnemonic })
    }

    /// Generate a 24-word mnemonic.
    #[wasm_bindgen]
    pub fn generate(word_count: u8) -> Result<Mnemonic, JsValue> {
        Mnemonic::new(word_count)
    }

    /// Parse a mnemonic from a phrase.
    #[wasm_bindgen(js_name = fromPhrase)]
    pub fn from_phrase(phrase: &str) -> Result<Mnemonic, JsValue> {
        let mnemonic = Bip39Mnemonic::from_phrase(phrase, Language::English)
            .map_err(|e| JsValue::from_str(&format!("Invalid mnemonic: {:?}", e)))?;
        Ok(Mnemonic { inner: mnemonic })
    }

    /// Get the mnemonic phrase as a string.
    #[wasm_bindgen]
    pub fn phrase(&self) -> String {
        self.inner.phrase().to_string()
    }

    /// Get the mnemonic words as an array.
    #[wasm_bindgen]
    pub fn words(&self) -> Vec<String> {
        self.inner
            .phrase()
            .split_whitespace()
            .map(String::from)
            .collect()
    }

    /// Get the word count.
    #[wasm_bindgen(js_name = wordCount)]
    pub fn word_count(&self) -> usize {
        self.inner.phrase().split_whitespace().count()
    }

    /// Derive a 64-byte seed from the mnemonic.
    #[wasm_bindgen(js_name = toSeed)]
    pub fn to_seed(&self, passphrase: &str) -> Vec<u8> {
        let seed = bip39::Seed::new(&self.inner, passphrase);
        seed.as_bytes().to_vec()
    }

    /// Get the entropy bytes.
    #[wasm_bindgen]
    pub fn entropy(&self) -> Vec<u8> {
        self.inner.entropy().to_vec()
    }

    /// Validate a mnemonic phrase.
    #[wasm_bindgen]
    pub fn validate(phrase: &str) -> bool {
        Bip39Mnemonic::validate(phrase, Language::English).is_ok()
    }
}

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

    #[test]
    fn test_mnemonic_generation() {
        let mnemonic = Mnemonic::new(24).unwrap();
        assert_eq!(mnemonic.word_count(), 24);
    }

    #[test]
    fn test_mnemonic_validation() {
        assert!(Mnemonic::validate("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"));
        assert!(!Mnemonic::validate("invalid mnemonic phrase"));
    }

    #[test]
    fn test_mnemonic_to_seed() {
        let mnemonic = Mnemonic::from_phrase(
            "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"
        ).unwrap();
        let seed = mnemonic.to_seed("");
        assert_eq!(seed.len(), 64);
    }
}
