//! Validation error type for newtype wrappers
//!
//! This module provides the ValidationError type used throughout the SMMU implementation
//! for reporting validation failures in newtype constructors.

use core::fmt;

/// Helper function to format u64 with underscores for readability
fn format_number_with_underscores(value: u64) -> String {
    let s = value.to_string();
    let bytes = s.as_bytes();
    let len = bytes.len();
    let mut result = String::new();

    for (i, &byte) in bytes.iter().enumerate() {
        if i > 0 && (len - i) % 3 == 0 {
            result.push('_');
        }
        result.push(byte as char);
    }
    result
}

/// Helper function to format hex u64 with underscores for readability
fn format_hex_with_underscores(value: u64) -> String {
    let hex = format!("{value:X}");
    let bytes = hex.as_bytes();
    let len = bytes.len();
    let mut result = String::from("0x");

    for (i, &byte) in bytes.iter().enumerate() {
        if i > 0 && (len - i) % 4 == 0 {
            result.push('_');
        }
        result.push(byte as char);
    }
    result
}

/// Validation error for newtype wrapper construction and operations
///
/// Provides detailed context about validation failures including:
/// - Field name (e.g., StreamID, PASID)
/// - Invalid value that was provided
/// - Constraint that was violated
/// - Specific error types for different validation scenarios
///
/// # Examples
///
/// ```
/// use smmu::ValidationError;
///
/// let error = ValidationError::OutOfRange {
///     field: "PASID".to_string(),
///     value: 1_048_576,
///     max: 1_048_575,
/// };
///
/// println!("Validation failed: {}", error);
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ValidationError {
    /// Value is out of the valid range
    OutOfRange {
        /// Field name
        field: String,
        /// Invalid value provided
        value: u64,
        /// Maximum allowed value
        max: u64,
    },

    /// Address is not properly aligned
    InvalidAlignment {
        /// Address value
        address: u64,
        /// Required alignment (e.g., 0x1000 for 4KB pages)
        required_alignment: u64,
    },

    /// Invalid access type bit pattern
    InvalidAccessType {
        /// Invalid bit pattern
        bits: u8,
    },

    /// Invalid security state encoding
    InvalidSecurityState {
        /// Invalid security state bits
        bits: u8,
    },

    /// Invalid translation stage configuration
    InvalidTranslationStage {
        /// Invalid stage bits
        bits: u8,
    },

    /// Invalid fault type code
    InvalidFaultType {
        /// Invalid fault code
        code: u8,
    },

    /// Invalid state transition
    InvalidStateTransition {
        /// Current state
        from: String,
        /// Attempted target state
        to: String,
    },

    /// Permission denied
    PermissionDenied {
        /// Requested permissions
        requested: String,
        /// Available permissions
        available: String,
    },

    /// Security violation
    SecurityViolation {
        /// Accessing security state
        from_state: String,
        /// Target security state
        to_state: String,
    },

    /// Invalid configuration
    InvalidConfiguration {
        /// Reason for configuration error
        reason: String,
    },

    /// Invalid PASID value
    InvalidPASID {
        /// Invalid PASID value
        value: u32,
    },

    /// Generic validation error (for backward compatibility)
    Generic {
        /// Field name
        field: String,
        /// Invalid value as string
        value: String,
        /// Constraint description
        constraint: String,
    },
}

impl ValidationError {
    /// Creates a new generic validation error with context
    ///
    /// # Arguments
    ///
    /// * `field` - Name of the field that failed validation
    /// * `value` - The invalid value that was provided
    /// * `constraint` - Description of the constraint that was violated
    #[must_use]
    pub fn new(field: &str, value: &str, constraint: &str) -> Self {
        Self::Generic {
            field: field.to_string(),
            value: value.to_string(),
            constraint: constraint.to_string(),
        }
    }
}

impl fmt::Display for ValidationError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::OutOfRange { field, value, max } => {
                write!(f, "Validation error for {field}: value {} exceeds maximum {}",
                    format_number_with_underscores(*value),
                    format_number_with_underscores(*max))
            },
            Self::InvalidAlignment { address, required_alignment } => {
                write!(f, "Address {} is not aligned to {}",
                    format_hex_with_underscores(*address),
                    format_hex_with_underscores(*required_alignment))
            },
            Self::InvalidAccessType { bits } => {
                write!(f, "Invalid access type bit pattern: {bits:#b}")
            },
            Self::InvalidSecurityState { bits } => {
                write!(f, "Invalid security state encoding: {bits:#b}")
            },
            Self::InvalidTranslationStage { bits } => {
                write!(f, "Invalid translation stage configuration: {bits:#b}")
            },
            Self::InvalidFaultType { code } => {
                write!(f, "Invalid fault type code: {code:#x}")
            },
            Self::InvalidStateTransition { from, to } => {
                write!(f, "Invalid state transition from {from} to {to}")
            },
            Self::PermissionDenied { requested, available } => {
                write!(f, "Permission denied: requested {requested} but only {available} available")
            },
            Self::SecurityViolation { from_state, to_state } => {
                write!(f, "Security violation: {from_state} cannot access {to_state}")
            },
            Self::InvalidConfiguration { reason } => {
                write!(f, "Invalid configuration: {reason}")
            },
            Self::InvalidPASID { value } => {
                let formatted = format_number_with_underscores(u64::from(*value));
                write!(f, "Invalid PASID value: {formatted}")
            },
            Self::Generic { field, value, constraint } => {
                write!(f, "Validation error for {field}: value '{value}' {constraint}")
            },
        }
    }
}

#[cfg(feature = "std")]
impl std::error::Error for ValidationError {}
