//   Copyright 2025 The Tari Project
//   SPDX-License-Identifier: BSD-3-Clause

use minicbor::{CborLen, Decode, Encode};
use tari_template_abi::rust::{fmt, ops::Deref};

use crate::{crypto::InvalidByteLengthError, hex::write_hex_fmt};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encode, Decode, CborLen)]
#[cbor(transparent)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(transparent))]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize))]
#[cfg_attr(feature = "ts", derive(ts_rs::TS), ts(export))]
pub struct Scalar32Bytes(
    #[cfg_attr(feature = "serde", serde(with = "crate::serde_helpers::fixed_hex"))]
    #[cfg_attr(feature = "ts", ts(type = "string"))]
    #[cbor(with = "minicbor::bytes")]
    [u8; Scalar32Bytes::length()],
);

impl Scalar32Bytes {
    pub const fn length() -> usize {
        32
    }

    pub const fn zero() -> Self {
        Self([0u8; Self::length()])
    }

    pub fn is_zero(&self) -> bool {
        self.0.iter().all(|&b| b == 0)
    }

    pub fn from_bytes(bytes: &[u8]) -> Result<Self, InvalidByteLengthError> {
        if bytes.len() != Self::length() {
            return Err(InvalidByteLengthError {
                size: bytes.len(),
                expected: Self::length(),
            });
        }

        let mut key = [0u8; Self::length()];
        key.copy_from_slice(bytes);
        Ok(Self(key))
    }

    pub fn as_bytes(&self) -> &[u8] {
        &self.0
    }

    pub fn into_array(self) -> [u8; Self::length()] {
        self.0
    }
}

impl TryFrom<&[u8]> for Scalar32Bytes {
    type Error = InvalidByteLengthError;

    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
        Self::from_bytes(value)
    }
}

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

impl Deref for Scalar32Bytes {
    type Target = [u8; Self::length()];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

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

impl fmt::Display for Scalar32Bytes {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write_hex_fmt(f, &self.0)
    }
}
