use std::fmt::Debug;
use std::str::FromStr;

use hex::FromHexError;
use itertools::Itertools;

use crate::utils::sha256::IntoHash256;

#[derive(Hash, PartialEq, PartialOrd, Eq, Ord, bytemuck::Pod, bytemuck::Zeroable, Copy, Clone, Default)]
#[repr(C, align(8))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
pub struct Hash256(pub [u8; 32]);

#[derive(Debug, thiserror::Error)]
pub enum HashError {
    #[error(":{0}")]
    FromHex(#[from] FromHexError),
    #[error("Wrong vec len (should be 32)")]
    VecWrongLength,
}

impl Hash256 {
    /// Calculated from [].hash_256()
    pub const EMPTY: Self = Hash256([
        227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, 65, 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85,
    ]);

    pub fn chain_hash(self, add: &impl IntoHash256) -> Self {
        let mut bytes = [0u8; 64];
        bytes[..32].copy_from_slice(&self.0);
        bytes[32..].copy_from_slice(&add.hash_256().0);
        bytes.as_slice().hash_256()
    }
}

impl From<Hash256> for primitive_types::H256 {
    fn from(value: Hash256) -> Self {
        value.0.into()
    }
}

impl Debug for Hash256 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("Sha256").field(&self.to_string()).finish()
    }
}

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

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

impl From<primitive_types::H256> for Hash256 {
    fn from(val: primitive_types::H256) -> Self {
        Hash256(val.0)
    }
}

impl TryFrom<&'_ [u8]> for Hash256 {
    type Error = HashError;

    fn try_from(v: &'_ [u8]) -> Result<Self, Self::Error> {
        let sha256 = v.try_into().map_err(|_| HashError::VecWrongLength)?;
        Ok(Self(sha256))
    }
}

impl std::fmt::Display for Hash256 {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", hex::encode(self.0))
    }
}

impl FromStr for Hash256 {
    type Err = HashError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let sha256 = hex::decode(s)?.into_iter().collect_vec();
        let sha256: [u8; 32] = sha256.try_into().map_err(|_| HashError::VecWrongLength)?;

        Ok(Hash256(sha256))
    }
}

impl<'de> serde::Deserialize<'de> for Hash256 {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let v = <String>::deserialize(deserializer)?;
        Hash256::from_str(&v).map_err(serde::de::Error::custom)
    }
}

impl serde::Serialize for Hash256 {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        serializer.collect_str(self)
    }
}
