// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! # Nonce
//!
//! This module provides a nonce implementation for use as an identifier in the Rialo system.
//!
//! ## Overview
//!
//! In the context of Rialo, a nonce is a unique value that serves as an identifier,
//! particularly for generating Program Derived Addresses (PDAs). PDAs are deterministic
//! addresses derived from a program ID and one or more seeds (including nonces).
//!
//! This implementation is designed to be:
//!
//! - **Consistent**: Same input always produces the same nonce
//! - **Flexible**: Can be created from various data types (strings, byte arrays, integers)
//! - **Fixed-size**: Always `NONCE_LEN` bytes regardless of input size, making it ideal for use in
//!   Rialo Transactions and Instructions where fixed-length identifiers are important
//! - **Serializable**: Can be converted to/from string representations
//! - **Secure**: Uses Blake3 hashing for inputs exceeding `NONCE_LEN` bytes
//!
//! ## Behavior
//!
//! The module supports creating nonces from different data formats and handles
//! inputs of various sizes by either:
//!
//! - **Padding**: If input is ≤ `NONCE_LEN` bytes, it's padded with zeros to reach `NONCE_LEN` bytes
//! - **Hashing**: If input is > `NONCE_LEN` bytes, it's hashed using Blake3 to produce a `NONCE_LEN`-byte value
//!
//! ## Usage
//!
//! Nonces can be created from various data types, and will always result in a `NONCE_LEN`-byte value:
//!
//! ```
//! # use rialo_types::Nonce;
//! // From a string
//! let nonce_from_str = Nonce::from("my_identifier");
//!
//! // From a byte string literal
//! let nonce_from_byte_str = Nonce::from(b"my_identifier");
//!
//! // From a byte array of any size (small example)
//! let small_bytes = [1, 2, 3, 4];
//! let nonce_from_small_bytes = Nonce::from(&small_bytes);
//! assert_eq!(nonce_from_small_bytes.as_bytes().len(), 32); // Always 32 bytes
//!
//! // From a byte array of any size (large example)
//! let large_bytes = [1u8; 64];
//! let nonce_from_large_bytes = Nonce::from(&large_bytes);
//! assert_eq!(nonce_from_large_bytes.as_bytes().len(), 32); // Always 32 bytes
//!
//! // From a dynamic slice
//! let dynamic_slice: &[u8] = &[5, 6, 7, 8, 9];
//! let nonce_from_slice = Nonce::from(dynamic_slice);
//!
//! // From a u64
//! let nonce_from_int = Nonce::from(12345u64);
//!
//! // Direct creation (if you already have a 32-byte array)
//! let array = [0u8; 32];
//! let nonce_direct = Nonce::new(array);
//! ```
//!
//! ## Use in Rialo Transactions and Instructions
//!
//! The fixed-size nature of `Nonce` (always `NONCE_LEN` bytes) makes it particularly useful in contexts
//! where knowing the exact size of an identifier is important, such as in Rialo Transactions
//! and Instructions. This allows for more efficient serialization and deserialization, as well
//! as predictable memory layouts.
//!
//! ## Security Considerations
//!
//! When using nonces for security-critical operations:
//!
//! - Prefer using cryptographically strong random values when uniqueness is critical
//! - Be aware that predictable nonces may lead to address collisions or vulnerabilities
//! - For deterministic address generation, ensure all inputs are properly validated

use std::fmt;

use blake3::Hasher;
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};

/// Length of a nonce in bytes.
const NONCE_LEN: usize = 32;

#[derive(
    Debug,
    Default,
    Clone,
    Copy,
    PartialEq,
    Eq,
    Hash,
    PartialOrd,
    Ord,
    Serialize,
    Deserialize,
    BorshSerialize,
    BorshDeserialize,
)]
pub struct Nonce([u8; NONCE_LEN]);

impl Nonce {
    /// Creates a new Nonce directly from a `NONCE_LEN`-byte array.
    ///
    /// This is the most direct way to create a Nonce when you already have
    /// a properly sized `NONCE_LEN`-byte array. No padding or hashing is performed.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// let bytes = [1u8; 32];
    /// let nonce = Nonce::new(bytes);
    /// assert_eq!(nonce.as_bytes(), &bytes);
    /// ```
    pub const fn new(nonce: [u8; NONCE_LEN]) -> Self {
        Self(nonce)
    }

    /// Creates a Nonce from a string slice at compile time.
    ///
    /// If the string is shorter than `NONCE_LEN` bytes, it is padded with zeros.
    /// If the string is longer than `NONCE_LEN` bytes, it is truncated to `NONCE_LEN` bytes.
    ///
    /// Note: This behavior matches `From<&str>` for strings up to `NONCE_LEN` bytes.
    /// For longer strings, `From<&str>` would perform a Blake3 hash instead of truncation.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// const CONNECT: Nonce = Nonce::from_short_str("connect");
    /// ```
    pub const fn from_short_str(s: &str) -> Self {
        let bytes = s.as_bytes();
        let mut data = [0u8; NONCE_LEN];
        let mut i = 0;
        while i < bytes.len() && i < NONCE_LEN {
            data[i] = bytes[i];
            i += 1;
        }
        Self(data)
    }

    /// Returns a reference to the inner `NONCE_LEN`-byte array.
    ///
    /// This method is useful when you need to access the raw bytes
    /// of the nonce, for example when using it as a seed for PDA derivation.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// let nonce = Nonce::from("example");
    /// let bytes = nonce.as_bytes();
    /// // Use bytes for PDA derivation or other operations
    /// ```
    pub fn as_bytes(&self) -> &[u8; NONCE_LEN] {
        &self.0
    }
}

impl<const N: usize> From<[u8; N]> for Nonce {
    /// Creates a Nonce from an owned fixed-size byte array.
    ///
    /// This implementation delegates to the reference implementation to avoid code duplication.
    /// The same padding or hashing rules apply as in the reference implementation.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// let array = [5u8; 16];
    /// let nonce = Nonce::from(array);
    /// ```
    fn from(nonce: [u8; N]) -> Self {
        Self::from(&nonce)
    }
}

impl<const N: usize> From<&[u8; N]> for Nonce {
    /// Creates a Nonce from a reference to a fixed-size byte array of any length.
    ///
    /// # Behavior
    ///
    /// - If `N > NONCE_LEN`: The input is hashed using Blake3 to produce a `NONCE_LEN`-byte nonce
    /// - If `N <= NONCE_LEN`: The input is padded with zeros to reach `NONCE_LEN` bytes
    ///
    /// The padding is applied to the right side, preserving the original bytes at the beginning.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// // Small array (padded)
    /// let small = [1, 2, 3, 4];
    /// let nonce_small = Nonce::from(&small);
    ///
    /// // Large array (hashed)
    /// let large = [1u8; 64];
    /// let nonce_large = Nonce::from(&large);
    /// ```
    fn from(nonce: &[u8; N]) -> Self {
        // If the slice is longer than `NONCE_LEN` bytes, it will be hashed
        if N > NONCE_LEN {
            let mut hasher = Hasher::new();
            hasher.update(nonce);
            let result = hasher.finalize();
            Self(result.into())
        } else {
            // If the slice is equal to or shorter than `NONCE_LEN` bytes, it will be padded with zeros
            let mut padded = [0u8; NONCE_LEN];
            padded[..N].copy_from_slice(nonce);
            Self(padded)
        }
    }
}

impl From<&[u8]> for Nonce {
    /// Creates a Nonce from a byte slice of any length.
    ///
    /// This implementation handles dynamic-length byte slices, applying the same
    /// logic as the fixed-size array implementation:
    ///
    /// # Behavior
    ///
    /// - If `nonce.len() > NONCE_LEN`: The input is hashed using Blake3
    /// - If `nonce.len() <= NONCE_LEN`: The input is padded with zeros
    ///
    /// # Edge Cases
    ///
    /// - Empty slice: Results in a nonce of all zeros
    /// - Exactly `NONCE_LEN` bytes: No transformation is applied, but the bytes are copied
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// // Empty slice
    /// let empty: &[u8] = &[];
    /// let nonce_empty = Nonce::from(empty);
    /// assert_eq!(*nonce_empty.as_bytes(), [0u8; 32]);
    ///
    /// // Dynamic slice
    /// let data = b"dynamic length data".to_vec();
    /// let nonce_dynamic = Nonce::from(data.as_slice());
    /// ```
    fn from(nonce: &[u8]) -> Self {
        // If the slice is longer than `NONCE_LEN` bytes, it will be hashed
        if nonce.len() > NONCE_LEN {
            let mut hasher = Hasher::new();
            hasher.update(nonce);
            let result = hasher.finalize();
            Self(result.into())
        } else {
            // If the slice is equal to or shorter than `NONCE_LEN` bytes, it will be padded with zeros
            let mut padded = [0u8; NONCE_LEN];
            padded[..nonce.len()].copy_from_slice(nonce);
            Self(padded)
        }
    }
}

impl From<Vec<u8>> for Nonce {
    /// Creates a Nonce from an owned vector of bytes.
    ///
    /// This implementation converts the vector to a slice and applies the same
    /// padding/hashing rules as the slice implementation.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// let vec = vec![1, 2, 3, 4];
    /// let nonce = Nonce::from(vec);
    /// ```
    fn from(nonce: Vec<u8>) -> Self {
        Self::from(nonce.as_slice())
    }
}

impl From<&Vec<u8>> for Nonce {
    /// Creates a Nonce from a reference to a vector of bytes.
    ///
    /// This implementation converts the vector to a slice and applies the same
    /// padding/hashing rules as the slice implementation.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// let vec = vec![1, 2, 3, 4];
    /// let nonce = Nonce::from(vec.as_slice());
    /// ```
    fn from(nonce: &Vec<u8>) -> Self {
        Self::from(nonce.as_slice())
    }
}

impl From<&str> for Nonce {
    /// Creates a Nonce from a string slice.
    ///
    /// This implementation converts the string to bytes and then applies
    /// the standard padding/hashing rules. This is particularly useful for
    /// creating deterministic nonces from human-readable identifiers.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// let nonce = Nonce::from("account:user123");
    /// ```
    ///
    /// # Note
    ///
    /// String-based nonces are convenient but be aware that:
    /// - Different string encodings could produce different nonces
    /// - Unicode strings may have unexpected byte representations
    fn from(nonce: &str) -> Self {
        Self::from(nonce.as_bytes())
    }
}

impl From<String> for Nonce {
    /// Creates a Nonce from an owned String.
    ///
    /// This implementation converts the String to bytes and applies the
    /// same padding/hashing rules as the string slice implementation.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// let nonce = Nonce::from(String::from("account:user123"));
    /// ```
    fn from(nonce: String) -> Self {
        Self::from(nonce.as_str())
    }
}

impl From<&String> for Nonce {
    /// Creates a Nonce from a reference to a String.
    ///
    /// This implementation is similar to the owned String implementation,
    /// converting the string to bytes and applying the standard rules.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// let nonce = Nonce::from(&String::from("account:user123"));
    /// ```
    fn from(nonce: &String) -> Self {
        Self::from(nonce.as_str())
    }
}

impl From<u64> for Nonce {
    /// Creates a Nonce from a u64 integer.
    ///
    /// The integer is converted to bytes in little-endian format before being
    /// padded to `NONCE_LEN` bytes. This is useful for creating sequential or indexed nonces.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// // Create sequential nonces
    /// let nonce1 = Nonce::from(1u64);
    /// let nonce2 = Nonce::from(2u64);
    /// ```
    ///
    /// # Note
    ///
    /// Since u64 is 8 bytes, the resulting nonce will always be padded rather than hashed.
    /// The first 8 bytes will contain the little-endian representation of the integer,
    /// and the remaining 24 bytes will be zeros.
    fn from(nonce: u64) -> Self {
        Self::from(nonce.to_le_bytes())
    }
}

impl fmt::Display for Nonce {
    /// Formats the Nonce as a hexadecimal string.
    ///
    /// This implementation converts the `NONCE_LEN`-byte nonce to a 64-character
    /// hexadecimal string representation, which is useful for logging,
    /// debugging, and serialization to human-readable formats.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rialo_types::Nonce;
    /// let nonce = Nonce::from([1u8; 32]);
    /// let hex_string = nonce.to_string();
    /// println!("Nonce: {}", nonce); // Prints the hex representation
    /// ```
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", hex::encode(self.0))
    }
}
