//! Lowercase hexadecimal formatting via [`fmt::LowerHex`].
use super::U256;
use core::fmt;

/// Formats a [`U256`] as a lowercase hexadecimal string without leading
/// zeros (except for the value zero itself, which formats as `"0"`).
///
/// Supports the `#` alternate flag for a `0x` prefix, matching the
/// standard library convention for `u64` and other integer types.
///
/// # Examples
///
/// ```
/// use cnfy_uint::u256::U256;
///
/// let v = U256::from_be_limbs([0, 0, 0, 255]);
/// assert_eq!(format!("{:x}", v), "ff");
/// assert_eq!(format!("{:#x}", v), "0xff");
/// ```
impl fmt::LowerHex for U256 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Find the first non-zero limb from MSB (index 3) down
        let first_nonzero = (0..4).rev().find(|&i| self.0[i] != 0);

        match first_nonzero {
            None => {
                // All limbs are zero
                if f.alternate() {
                    f.write_str("0x0")
                } else {
                    f.write_str("0")
                }
            }
            Some(idx) => {
                if f.alternate() {
                    f.write_str("0x")?;
                }
                // First non-zero limb: no leading zeros
                write!(f, "{:x}", self.0[idx])?;
                // Remaining limbs: pad to 16 hex digits each (descending)
                let mut i = idx;
                while i > 0 {
                    i -= 1;
                    write!(f, "{:016x}", self.0[i])?;
                }
                Ok(())
            }
        }
    }
}

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

    /// Zero formats as "0".
    #[test]
    fn zero() {
        assert_eq!(format!("{:x}", U256::ZERO), "0");
    }

    /// Zero with alternate flag formats as "0x0".
    #[test]
    fn zero_alternate() {
        assert_eq!(format!("{:#x}", U256::ZERO), "0x0");
    }

    /// Small value formats without leading zeros.
    #[test]
    fn small_value() {
        let v = U256::from_be_limbs([0, 0, 0, 255]);
        assert_eq!(format!("{:x}", v), "ff");
    }

    /// Value spanning two limbs shows full padding on lower limb.
    #[test]
    fn two_limbs() {
        let v = U256::from_be_limbs([0, 0, 1, 0]);
        assert_eq!(format!("{:x}", v), "10000000000000000");
    }

    /// MAX value fills all hex digits.
    #[test]
    fn max_value() {
        let s = format!("{:x}", U256::MAX);
        assert_eq!(
            s,
            "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
        );
        assert_eq!(s.len(), 64);
    }

    /// Alternate flag adds 0x prefix.
    #[test]
    fn alternate_prefix() {
        let v = U256::from_be_limbs([0, 0, 0, 0xAB]);
        assert_eq!(format!("{:#x}", v), "0xab");
    }

    /// ONE formats as "1".
    #[test]
    fn one() {
        assert_eq!(format!("{:x}", U256::ONE), "1");
    }

    /// Value in the MSB limb only.
    #[test]
    fn msb_limb() {
        let v = U256::from_be_limbs([0xCAFE, 0, 0, 0]);
        assert_eq!(
            format!("{:x}", v),
            "cafe000000000000000000000000000000000000000000000000"
        );
    }
}
