//! Merkle Proof verification
use ergo_lib::ergo_merkle_tree;
use serde::{Deserialize, Serialize};
use std::convert::TryInto;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
/// A MerkleProof type. Given leaf data and levels (bottom-upwards), the root hash can be computed and validated
pub struct MerkleProof(pub(crate) ergo_merkle_tree::MerkleProof);

/// A level node in a merkle proof
#[wasm_bindgen]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct LevelNode(ergo_merkle_tree::LevelNode);

#[wasm_bindgen]
impl LevelNode {
    /// Creates a new LevelNode from a 32 byte hash and side that the node belongs on in the tree. Fails if the digest is not 32 bytes
    pub fn new(hash: &[u8], side: u8) -> Result<LevelNode, JsValue> {
        Ok(Self(ergo_merkle_tree::LevelNode::new(
            (*hash)
                .try_into()
                .map_err(|_| "Digest is not 32 bytes in size")?,
            side.try_into()?,
        )))
    }
    /// Returns the associated digest (hash) with this node. Returns an empty array if there's no hash
    #[wasm_bindgen(getter)]
    pub fn digest(&self) -> Vec<u8> {
        match &self.0.hash {
            Some(hash) => hash.0.to_vec(),
            None => vec![],
        }
    }
    /// Returns the associated side with this node (0 = Left, 1 = Right)
    #[wasm_bindgen(getter)]
    pub fn side(&self) -> u8 {
        self.0.side as u8
    }
}

#[wasm_bindgen]
impl MerkleProof {
    /// Creates a new merkle proof with given leaf data and level data (bottom-upwards)
    /// You can verify it against a Blakeb256 root hash by using [`Self::valid()`]
    /// Add a node by using [`Self::add_node()`]
    /// Each digest on the level must be exactly 32 bytes
    pub fn new(leaf_data: &[u8]) -> MerkleProof {
        Self(ergo_merkle_tree::MerkleProof::new(leaf_data, &[])) // There are issues with wasm when trying to pass an array of structs, so it's better to use add_node instead
    }

    /// Adds a new node to the MerkleProof above the current nodes
    pub fn add_node(&mut self, level: &LevelNode) {
        self.0.add_node(level.0.clone());
    }

    /// Validates the Merkle proof against the root hash
    pub fn valid(&self, expected_root: &[u8]) -> bool {
        self.0.valid(expected_root)
    }
}

#[wasm_bindgen]
/// Decodes a base16 string into an array of bytes
pub fn base16_decode(data: &str) -> Result<Vec<u8>, JsValue> {
    base16::decode(&data).map_err(|_| "Failed to decode base16 input".into())
}
