//! Hash types for the Synor blockchain.
//!
//! Uses BLAKE3 for fast hashing and SHA3-256 for compatibility.

use serde::{Deserialize, Serialize};
use std::fmt;
use thiserror::Error;

/// 256-bit hash value.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Hash256([u8; 32]);

impl Hash256 {
    /// The zero hash (all zeros).
    pub const ZERO: Hash256 = Hash256([0u8; 32]);

    /// Creates a new hash from raw bytes.
    pub const fn from_bytes(bytes: [u8; 32]) -> Self {
        Hash256(bytes)
    }

    /// Creates a hash from a slice. Returns error if slice length != 32.
    pub fn from_slice(slice: &[u8]) -> Result<Self, HashError> {
        if slice.len() != 32 {
            return Err(HashError::InvalidLength {
                expected: 32,
                got: slice.len(),
            });
        }
        let mut bytes = [0u8; 32];
        bytes.copy_from_slice(slice);
        Ok(Hash256(bytes))
    }

    /// Creates a hash from a hex string.
    pub fn from_hex(hex: &str) -> Result<Self, HashError> {
        let bytes = hex::decode(hex).map_err(|e| HashError::InvalidHex(e.to_string()))?;
        Self::from_slice(&bytes)
    }

    /// Returns the hash as a byte slice.
    pub fn as_bytes(&self) -> &[u8; 32] {
        &self.0
    }

    /// Returns the hash as a hex string.
    pub fn to_hex(&self) -> String {
        hex::encode(self.0)
    }

    /// Computes the BLAKE3 hash of the input data.
    pub fn blake3(data: &[u8]) -> Self {
        let hash = blake3::hash(data);
        Hash256(*hash.as_bytes())
    }

    /// Computes the SHA3-256 hash of the input data.
    pub fn sha3_256(data: &[u8]) -> Self {
        use sha3::{Digest, Sha3_256};
        let mut hasher = Sha3_256::new();
        hasher.update(data);
        let result = hasher.finalize();
        let mut bytes = [0u8; 32];
        bytes.copy_from_slice(&result);
        Hash256(bytes)
    }

    /// Computes double BLAKE3 hash (hash of hash).
    pub fn double_blake3(data: &[u8]) -> Self {
        Self::blake3(Self::blake3(data).as_bytes())
    }

    /// Computes the hash of multiple items concatenated.
    pub fn combine(items: &[&[u8]]) -> Self {
        let mut hasher = blake3::Hasher::new();
        for item in items {
            hasher.update(item);
        }
        Hash256(*hasher.finalize().as_bytes())
    }

    /// Computes the Merkle root of a list of hashes.
    pub fn merkle_root(hashes: &[Hash256]) -> Self {
        if hashes.is_empty() {
            return Hash256::ZERO;
        }
        if hashes.len() == 1 {
            return hashes[0];
        }

        let mut current_level: Vec<Hash256> = hashes.to_vec();

        while current_level.len() > 1 {
            let mut next_level = Vec::with_capacity(current_level.len().div_ceil(2));

            for chunk in current_level.chunks(2) {
                let combined = if chunk.len() == 2 {
                    Hash256::combine(&[chunk[0].as_bytes(), chunk[1].as_bytes()])
                } else {
                    // Odd number: duplicate the last hash
                    Hash256::combine(&[chunk[0].as_bytes(), chunk[0].as_bytes()])
                };
                next_level.push(combined);
            }

            current_level = next_level;
        }

        current_level[0]
    }

    /// Returns true if this is the zero hash.
    pub fn is_zero(&self) -> bool {
        self.0 == [0u8; 32]
    }
}

impl fmt::Debug for Hash256 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Hash256({})", self.to_hex())
    }
}

impl fmt::Display for Hash256 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Display first 8 and last 8 hex chars for brevity
        let hex = self.to_hex();
        write!(f, "{}...{}", &hex[..8], &hex[56..])
    }
}

impl AsRef<[u8]> for Hash256 {
    fn as_ref(&self) -> &[u8] {
        &self.0
    }
}

impl From<[u8; 32]> for Hash256 {
    fn from(bytes: [u8; 32]) -> Self {
        Hash256(bytes)
    }
}

impl From<Hash256> for [u8; 32] {
    fn from(hash: Hash256) -> Self {
        hash.0
    }
}

impl Serialize for Hash256 {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        if serializer.is_human_readable() {
            serializer.serialize_str(&self.to_hex())
        } else {
            serializer.serialize_bytes(&self.0)
        }
    }
}

impl<'de> Deserialize<'de> for Hash256 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        if deserializer.is_human_readable() {
            let s = String::deserialize(deserializer)?;
            Hash256::from_hex(&s).map_err(serde::de::Error::custom)
        } else {
            let bytes: Vec<u8> = Vec::deserialize(deserializer)?;
            Hash256::from_slice(&bytes).map_err(serde::de::Error::custom)
        }
    }
}

impl borsh::BorshSerialize for Hash256 {
    fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
        writer.write_all(&self.0)
    }
}

impl borsh::BorshDeserialize for Hash256 {
    fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> {
        let mut bytes = [0u8; 32];
        reader.read_exact(&mut bytes)?;
        Ok(Hash256(bytes))
    }
}

/// Errors that can occur when working with hashes.
#[derive(Debug, Error)]
pub enum HashError {
    #[error("Invalid hash length: expected {expected}, got {got}")]
    InvalidLength { expected: usize, got: usize },

    #[error("Invalid hex string: {0}")]
    InvalidHex(String),
}

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

    #[test]
    fn test_blake3_hash() {
        let data = b"Hello, Synor!";
        let hash = Hash256::blake3(data);
        assert!(!hash.is_zero());
        assert_eq!(hash.to_hex().len(), 64);
    }

    #[test]
    fn test_sha3_hash() {
        let data = b"Hello, Synor!";
        let hash = Hash256::sha3_256(data);
        assert!(!hash.is_zero());
    }

    #[test]
    fn test_hash_from_hex() {
        let hex = "0000000000000000000000000000000000000000000000000000000000000001";
        let hash = Hash256::from_hex(hex).unwrap();
        assert_eq!(hash.0[31], 1);
        assert_eq!(hash.to_hex(), hex);
    }

    #[test]
    fn test_merkle_root() {
        let h1 = Hash256::blake3(b"tx1");
        let h2 = Hash256::blake3(b"tx2");
        let h3 = Hash256::blake3(b"tx3");

        let root = Hash256::merkle_root(&[h1, h2, h3]);
        assert!(!root.is_zero());

        // Same inputs should produce same root
        let root2 = Hash256::merkle_root(&[h1, h2, h3]);
        assert_eq!(root, root2);
    }

    #[test]
    fn test_merkle_root_empty() {
        let root = Hash256::merkle_root(&[]);
        assert!(root.is_zero());
    }

    #[test]
    fn test_hash_display() {
        let hash =
            Hash256::from_hex("abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890")
                .unwrap();
        let display = format!("{}", hash);
        assert!(display.contains("..."));
        assert!(display.starts_with("abcdef12"));
        assert!(display.ends_with("34567890"));
    }
}
