use crate::prelude::*;

/// Get N cryptographically strong random bytes.
/// ```ignore
/// let five_bytes = random_bytes(5)?;
/// ```
/// It's not possible to generate random bytes from inside the wasm guest so the data is provided
/// by the wasm host which implies operating system specific details re: randomness.
/// The bytes are cryptographically random in that they are unpredictable, to the quality of what
/// host environment offers and the crypto implementation within holochain.
/// The bytes are not "secure" though:
/// - there's no way to prove that a specific value was the result of random generation or not
/// - the bytes are open in memory and even (de)serialized several times between the host and guest
/// The bytes are not a performant or testable way to do statistical analysis (e.g. monte carlo).
/// Rust provides several seedable PRNG implementations that are fast, repeatable and statistically
/// high quality even if not suitable for crypto applications. If you need to do anything with
/// statistics it is usually recommended to generate or provide a seed and then use an appropriate
/// PRNG from there.
/// See the rand rust crate
pub fn random_bytes(number_of_bytes: u32) -> ExternResult<Bytes> {
    HDK.with(|h| h.borrow().random_bytes(number_of_bytes))

pub trait TryFromRandom {
    fn try_from_random() -> ExternResult<Self>
        Self: Sized;

/// Ideally we wouldn't need to do this with a macro.
/// All we want is to implement this trait with whatever length our random-bytes-new-types need to
/// be, but if we use a const on the trait directly we get 'constant expression depends on a
/// generic parameter'
macro_rules! impl_try_from_random {
    ( $t:ty, $bytes:expr ) => {
        impl TryFromRandom for $t {
            fn try_from_random() -> $crate::prelude::ExternResult<Self> {
                $crate::prelude::random_bytes($bytes as u32).map(|bytes| {
                    // Always a fatal error if our own bytes generation has the wrong length.
                    assert_eq!($bytes, bytes.len());
                    let mut inner = [0; $bytes];