//! Hex string parsing via the [`FromStr`] trait.

use super::U384;
use core::fmt;
use core::str::FromStr;

/// Error type returned when parsing a hex string into a [`U384`] fails.
///
/// Possible failure reasons: empty input, invalid hex character, or
/// value exceeds 384 bits (more than 96 hex digits).
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseU384Error;

impl fmt::Display for ParseU384Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("invalid hex string for U384")
    }
}

/// Parses a hexadecimal string into a [`U384`].
///
/// Accepts lowercase and uppercase hex digits (`0-9`, `a-f`, `A-F`).
/// An optional `0x` or `0X` prefix is stripped before parsing. Leading
/// zeros are permitted. The string must represent a value that fits in
/// 384 bits (at most 96 hex digits after stripping the prefix and
/// leading zeros).
///
/// # Examples
///
/// ```
/// use cnfy_uint::u384::U384;
///
/// let v: U384 = "ff".parse().unwrap();
/// assert_eq!(v, U384::from_be_limbs([0, 0, 0, 0, 0, 255]));
///
/// let v: U384 = "0xFF".parse().unwrap();
/// assert_eq!(v, U384::from_be_limbs([0, 0, 0, 0, 0, 255]));
/// ```
impl FromStr for U384 {
    type Err = ParseU384Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let hex = s
            .strip_prefix("0x")
            .or_else(|| s.strip_prefix("0X"))
            .unwrap_or(s);

        if hex.is_empty() {
            return Err(ParseU384Error);
        }

        // Strip leading zeros, but keep at least one digit
        let hex = hex.trim_start_matches('0');
        let hex = if hex.is_empty() { "0" } else { hex };

        if hex.len() > 96 {
            return Err(ParseU384Error);
        }

        // Pad to 96 hex chars and parse into bytes
        let mut bytes = [0u8; 48];
        let hex_bytes = hex.as_bytes();
        let offset = 96 - hex_bytes.len();

        let mut i = 0;
        while i < hex_bytes.len() {
            let nibble = match hex_bytes[i] {
                b'0'..=b'9' => hex_bytes[i] - b'0',
                b'a'..=b'f' => hex_bytes[i] - b'a' + 10,
                b'A'..=b'F' => hex_bytes[i] - b'A' + 10,
                _ => return Err(ParseU384Error),
            };

            let byte_idx = (offset + i) / 2;
            if (offset + i) % 2 == 0 {
                bytes[byte_idx] = nibble << 4;
            } else {
                bytes[byte_idx] |= nibble;
            }
            i += 1;
        }

        Ok(U384::from_be_bytes(bytes))
    }
}

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

    /// Parse "0" yields zero.
    #[test]
    fn zero() {
        assert_eq!("0".parse::<U384>().unwrap(), U384::ZERO);
    }

    /// Parse "1" yields one.
    #[test]
    fn one() {
        assert_eq!("1".parse::<U384>().unwrap(), U384::ONE);
    }

    /// Parse "ff" yields 255.
    #[test]
    fn lowercase_ff() {
        assert_eq!(
            "ff".parse::<U384>().unwrap(),
            U384::from_be_limbs([0, 0, 0, 0, 0, 255]),
        );
    }

    /// Parse "FF" yields 255.
    #[test]
    fn uppercase_ff() {
        assert_eq!(
            "FF".parse::<U384>().unwrap(),
            U384::from_be_limbs([0, 0, 0, 0, 0, 255]),
        );
    }

    /// Parse with 0x prefix.
    #[test]
    fn hex_prefix_lower() {
        assert_eq!(
            "0xff".parse::<U384>().unwrap(),
            U384::from_be_limbs([0, 0, 0, 0, 0, 255]),
        );
    }

    /// Parse with 0X prefix.
    #[test]
    fn hex_prefix_upper() {
        assert_eq!(
            "0XFF".parse::<U384>().unwrap(),
            U384::from_be_limbs([0, 0, 0, 0, 0, 255]),
        );
    }

    /// Leading zeros are stripped.
    #[test]
    fn leading_zeros() {
        assert_eq!(
            "000ff".parse::<U384>().unwrap(),
            U384::from_be_limbs([0, 0, 0, 0, 0, 255]),
        );
    }

    /// Parse all zeros.
    #[test]
    fn all_zeros() {
        assert_eq!("0000".parse::<U384>().unwrap(), U384::ZERO);
    }

    /// Parse MAX value (96 f's).
    #[test]
    fn max_value() {
        let hex = "f".repeat(96);
        assert_eq!(hex.parse::<U384>().unwrap(), U384::MAX);
    }

    /// 97 hex digits (without leading zeros) exceeds 384 bits.
    #[test]
    fn too_large() {
        let hex = format!("1{}", "0".repeat(96));
        assert_eq!(hex.parse::<U384>(), Err(ParseU384Error));
    }

    /// Empty string is an error.
    #[test]
    fn empty_string() {
        assert_eq!("".parse::<U384>(), Err(ParseU384Error));
    }

    /// Just "0x" with no digits is an error.
    #[test]
    fn prefix_only() {
        assert_eq!("0x".parse::<U384>(), Err(ParseU384Error));
    }

    /// Invalid character is an error.
    #[test]
    fn invalid_char() {
        assert_eq!("0xGG".parse::<U384>(), Err(ParseU384Error));
    }

    /// Round-trip with LowerHex formatting.
    #[test]
    fn round_trip_lower_hex() {
        let original = U384::from_be_limbs([0x1234, 0x5678, 0x9ABC, 0xDEF0, 0x1111, 0x2222]);
        let hex = format!("{:x}", original);
        let parsed: U384 = hex.parse().unwrap();
        assert_eq!(parsed, original);
    }

    /// Round-trip with 0x prefix formatting.
    #[test]
    fn round_trip_prefixed() {
        let original = U384::from_be_limbs([0x1234, 0x5678, 0x9ABC, 0xDEF0, 0x1111, 0x2222]);
        let hex = format!("{:#x}", original);
        let parsed: U384 = hex.parse().unwrap();
        assert_eq!(parsed, original);
    }

    /// Single digit parses correctly.
    #[test]
    fn single_digit() {
        assert_eq!(
            "a".parse::<U384>().unwrap(),
            U384::from_be_limbs([0, 0, 0, 0, 0, 10]),
        );
    }
}
