//! Hex decoding utilities with validation.
//!
//! This module provides validated hex decoding functions that check string length
//! BEFORE attempting to decode, providing clear error messages.

use crate::error::{ApiError, Result};

/// Decode a 32-byte hash from hex string.
///
/// Validates that the hex string is exactly 64 characters (32 bytes) before decoding.
pub fn decode_hash_32(hex: &str) -> Result<[u8; 32]> {
    const EXPECTED_BYTES: usize = 32;
    const EXPECTED_HEX_CHARS: usize = EXPECTED_BYTES * 2;

    // Check length FIRST before attempting decode
    if hex.len() != EXPECTED_HEX_CHARS {
        return Err(ApiError::InvalidRequest(format!(
            "Expected {} hex characters ({} bytes), got {}",
            EXPECTED_HEX_CHARS,
            EXPECTED_BYTES,
            hex.len()
        )));
    }

    let bytes = hex::decode(hex).map_err(|e| ApiError::InvalidHex(e.to_string()))?;

    if bytes.len() != EXPECTED_BYTES {
        return Err(ApiError::InvalidHashLength { expected: EXPECTED_BYTES, actual: bytes.len() });
    }

    let mut hash = [0u8; 32];
    hash.copy_from_slice(&bytes);
    Ok(hash)
}

/// Decode an 8-byte hash from hex string.
///
/// Validates that the hex string is exactly 16 characters (8 bytes) before decoding.
pub fn decode_hash_8(hex: &str) -> Result<[u8; 8]> {
    const EXPECTED_BYTES: usize = 8;
    const EXPECTED_HEX_CHARS: usize = EXPECTED_BYTES * 2;

    // Check length FIRST before attempting decode
    if hex.len() != EXPECTED_HEX_CHARS {
        return Err(ApiError::InvalidRequest(format!(
            "Expected {} hex characters ({} bytes), got {}",
            EXPECTED_HEX_CHARS,
            EXPECTED_BYTES,
            hex.len()
        )));
    }

    let bytes = hex::decode(hex).map_err(|e| ApiError::InvalidHex(e.to_string()))?;

    if bytes.len() != EXPECTED_BYTES {
        return Err(ApiError::InvalidHashLength { expected: EXPECTED_BYTES, actual: bytes.len() });
    }

    let mut hash = [0u8; 8];
    hash.copy_from_slice(&bytes);
    Ok(hash)
}

/// Decode a 32-byte agent ID from hex string.
///
/// Agent IDs are Ed25519 public keys (32 bytes).
/// Validates that the hex string is exactly 64 characters before decoding.
pub fn decode_agent_id(hex: &str) -> Result<[u8; 32]> {
    decode_hash_32(hex)
}

/// Decode a 16-byte node ID from hex string.
///
/// Node IDs are used in HLC timestamps for distributed ordering.
/// Validates that the hex string is exactly 32 characters (16 bytes) before decoding.
pub fn decode_node_id(hex: &str) -> Result<[u8; 16]> {
    const EXPECTED_BYTES: usize = 16;
    const EXPECTED_HEX_CHARS: usize = EXPECTED_BYTES * 2;

    // Check length FIRST before attempting decode
    if hex.len() != EXPECTED_HEX_CHARS {
        return Err(ApiError::InvalidRequest(format!(
            "Expected {} hex characters ({} bytes), got {}",
            EXPECTED_HEX_CHARS,
            EXPECTED_BYTES,
            hex.len()
        )));
    }

    let bytes = hex::decode(hex).map_err(|e| ApiError::InvalidHex(e.to_string()))?;

    if bytes.len() != EXPECTED_BYTES {
        return Err(ApiError::InvalidHashLength { expected: EXPECTED_BYTES, actual: bytes.len() });
    }

    let mut node_id = [0u8; 16];
    node_id.copy_from_slice(&bytes);
    Ok(node_id)
}

/// Decode a 64-byte signature from hex string.
///
/// Validates that the hex string is exactly 128 characters (64 bytes) before decoding.
pub fn decode_signature(hex: &str) -> Result<[u8; 64]> {
    const EXPECTED_BYTES: usize = 64;
    const EXPECTED_HEX_CHARS: usize = EXPECTED_BYTES * 2;

    // Check length FIRST before attempting decode
    if hex.len() != EXPECTED_HEX_CHARS {
        return Err(ApiError::InvalidRequest(format!(
            "Expected {} hex characters ({} bytes), got {}",
            EXPECTED_HEX_CHARS,
            EXPECTED_BYTES,
            hex.len()
        )));
    }

    let bytes = hex::decode(hex).map_err(|e| ApiError::InvalidHex(e.to_string()))?;

    if bytes.len() != EXPECTED_BYTES {
        return Err(ApiError::InvalidHashLength { expected: EXPECTED_BYTES, actual: bytes.len() });
    }

    let mut sig = [0u8; 64];
    sig.copy_from_slice(&bytes);
    Ok(sig)
}

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

    #[test]
    fn test_decode_hash_32_valid() {
        let valid_hex = "a".repeat(64);
        let result = decode_hash_32(&valid_hex);
        assert!(result.is_ok());
    }

    #[test]
    fn test_decode_hash_32_wrong_length() {
        let short_hex = "abc";
        let result = decode_hash_32(short_hex);
        assert!(result.is_err());
        let err = result.unwrap_err().to_string();
        assert!(err.contains("Expected 64 hex characters"));
        assert!(err.contains("got 3"));
    }

    #[test]
    fn test_decode_hash_8_valid() {
        let valid_hex = "a".repeat(16);
        let result = decode_hash_8(&valid_hex);
        assert!(result.is_ok());
    }

    #[test]
    fn test_decode_hash_8_wrong_length() {
        let short_hex = "abc";
        let result = decode_hash_8(short_hex);
        assert!(result.is_err());
        let err = result.unwrap_err().to_string();
        assert!(err.contains("Expected 16 hex characters"));
    }

    #[test]
    fn test_decode_signature_valid() {
        let valid_hex = "b".repeat(128);
        let result = decode_signature(&valid_hex);
        assert!(result.is_ok());
    }

    #[test]
    fn test_decode_signature_wrong_length() {
        let short_hex = "xyz";
        let result = decode_signature(short_hex);
        assert!(result.is_err());
        let err = result.unwrap_err().to_string();
        assert!(err.contains("Expected 128 hex characters"));
        assert!(err.contains("got 3"));
    }

    #[test]
    fn test_decode_hash_32_invalid_hex() {
        let invalid_hex = "z".repeat(64);
        let result = decode_hash_32(&invalid_hex);
        assert!(result.is_err());
    }
}
