use std::sync::Mutex;

use bip39::{Language, Mnemonic};
use once_cell::sync::Lazy;

use crate::error::SeedError;

static MNEMONIC_CACHE: Lazy<Mutex<Option<String>>> = Lazy::new(|| Mutex::new(None));

pub fn init_seed(use_bip39: bool) -> Result<Vec<u8>, SeedError> {
    let mut entropy = [0u8; 32];
    getrandom::getrandom(&mut entropy)?;

    if !use_bip39 {
        *MNEMONIC_CACHE.lock().expect("mnemonic cache lock") = None;
        return Ok(entropy.to_vec());
    }

    let mnemonic = Mnemonic::from_entropy_in(Language::English, &entropy)?;
    *MNEMONIC_CACHE.lock().expect("mnemonic cache lock") = Some(mnemonic.to_string());
    let bytes = mnemonic.to_seed("");
    if bytes.is_empty() {
        return Err(SeedError::EmptySeed);
    }
    Ok(bytes.to_vec())
}

pub fn last_mnemonic() -> Option<String> {
    MNEMONIC_CACHE.lock().expect("mnemonic cache lock").take()
}

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

    static TEST_GUARD: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));

    #[test]
    fn init_seed_without_bip39() {
        let _guard = TEST_GUARD.lock().unwrap();
        *MNEMONIC_CACHE.lock().unwrap() = None;
        let seed = init_seed(false).unwrap();
        assert_eq!(seed.len(), 32);
        let mnemonic = last_mnemonic();
        assert!(mnemonic.is_none(), "{mnemonic:?}");
    }

    #[test]
    fn init_seed_with_bip39_caches_mnemonic() {
        let _guard = TEST_GUARD.lock().unwrap();
        *MNEMONIC_CACHE.lock().unwrap() = None;
        let seed = init_seed(true).unwrap();
        assert!(!seed.is_empty());
        let mnemonic = last_mnemonic();
        assert!(mnemonic.is_some(), "{mnemonic:?}");
        assert!(last_mnemonic().is_none());
    }
}
