//! Centralized validation module to eliminate entropy
//!
//! PMAT found `DataValidation` pattern repeated 10 times (2289 lines)
//! This module centralizes all validation logic with O(1) complexity.

use crate::runtime::{InterpreterError, Value};

/// Validate exact argument count - O(1) complexity
#[inline]
pub fn validate_arg_count(
    function_name: &str,
    args: &[Value],
    expected: usize,
) -> Result<(), InterpreterError> {
    if args.len() != expected {
        return Err(InterpreterError::RuntimeError(format!(
            "{}() expects exactly {} argument{}, got {}",
            function_name,
            expected,
            if expected == 1 { "" } else { "s" },
            args.len()
        )));
    }
    Ok(())
}

/// Validate minimum argument count - O(1) complexity
#[inline]
pub fn validate_min_args(
    function_name: &str,
    args: &[Value],
    min: usize,
) -> Result<(), InterpreterError> {
    if args.len() < min {
        return Err(InterpreterError::RuntimeError(format!(
            "{}() expects at least {} argument{}, got {}",
            function_name,
            min,
            if min == 1 { "" } else { "s" },
            args.len()
        )));
    }
    Ok(())
}

/// Validate argument count range - O(1) complexity
#[inline]
pub fn validate_arg_range(
    function_name: &str,
    args: &[Value],
    min: usize,
    max: usize,
) -> Result<(), InterpreterError> {
    if args.len() < min || args.len() > max {
        return Err(InterpreterError::RuntimeError(format!(
            "{}() expects {}-{} arguments, got {}",
            function_name,
            min,
            max,
            args.len()
        )));
    }
    Ok(())
}

/// Validate numeric argument - O(1) complexity
#[inline]
pub fn validate_numeric(
    function_name: &str,
    value: &Value,
    arg_name: &str,
) -> Result<(), InterpreterError> {
    match value {
        Value::Integer(_) | Value::Float(_) => Ok(()),
        _ => Err(InterpreterError::RuntimeError(format!(
            "{}() expects {} to be a number, got {}",
            function_name,
            arg_name,
            value.type_name()
        ))),
    }
}

/// Validate string argument - O(1) complexity
#[inline]
pub fn validate_string(
    function_name: &str,
    value: &Value,
    arg_name: &str,
) -> Result<(), InterpreterError> {
    match value {
        Value::String(_) => Ok(()),
        _ => Err(InterpreterError::RuntimeError(format!(
            "{}() expects {} to be a string, got {}",
            function_name,
            arg_name,
            value.type_name()
        ))),
    }
}

/// Validate array argument - O(1) complexity
#[inline]
pub fn validate_array(
    function_name: &str,
    value: &Value,
    arg_name: &str,
) -> Result<(), InterpreterError> {
    match value {
        Value::Array(_) => Ok(()),
        _ => Err(InterpreterError::RuntimeError(format!(
            "{}() expects {} to be an array, got {}",
            function_name,
            arg_name,
            value.type_name()
        ))),
    }
}

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

    #[test]
    fn test_validate_arg_count_exact() {
        let args = vec![Value::Integer(1), Value::Integer(2)];
        assert!(validate_arg_count("test", &args, 2).is_ok());
        assert!(validate_arg_count("test", &args, 3).is_err());
    }

    #[test]
    fn test_validate_min_args() {
        let args = vec![Value::Integer(1), Value::Integer(2)];
        assert!(validate_min_args("test", &args, 1).is_ok());
        assert!(validate_min_args("test", &args, 2).is_ok());
        assert!(validate_min_args("test", &args, 3).is_err());
    }

    #[test]
    fn test_validate_arg_range_boundaries() {
        // Mutation test: Verify < and > operators (not ==)
        // MISSED: replace < with == in validate_arg_range (line 54:19)
        // MISSED: replace > with == in validate_arg_range (line 54:39)

        // Test boundary values
        let args_too_few = vec![Value::Integer(1)];
        let args_min = vec![Value::Integer(1), Value::Integer(2)];
        let args_mid = vec![Value::Integer(1), Value::Integer(2), Value::Integer(3)];
        let args_max = vec![
            Value::Integer(1),
            Value::Integer(2),
            Value::Integer(3),
            Value::Integer(4),
        ];
        let args_too_many = vec![
            Value::Integer(1),
            Value::Integer(2),
            Value::Integer(3),
            Value::Integer(4),
            Value::Integer(5),
        ];

        // Range: 2-4 arguments
        assert!(
            validate_arg_range("test", &args_too_few, 2, 4).is_err(),
            "Should reject fewer than min"
        );
        assert!(
            validate_arg_range("test", &args_min, 2, 4).is_ok(),
            "Should accept exactly min"
        );
        assert!(
            validate_arg_range("test", &args_mid, 2, 4).is_ok(),
            "Should accept middle value"
        );
        assert!(
            validate_arg_range("test", &args_max, 2, 4).is_ok(),
            "Should accept exactly max"
        );
        assert!(
            validate_arg_range("test", &args_too_many, 2, 4).is_err(),
            "Should reject more than max"
        );
    }

    #[test]
    fn test_validate_string_match_arm() {
        // Mutation test: Verify String match arm is tested
        // MISSED: delete match arm Value::String(_) in validate_string (line 92:9)

        // Test String value is accepted
        assert!(
            validate_string("test", &Value::from_string("hello".to_string()), "arg").is_ok(),
            "Should accept string value"
        );

        // Test non-String value is rejected
        assert!(
            validate_string("test", &Value::Integer(42), "arg").is_err(),
            "Should reject integer value"
        );
        assert!(
            validate_string("test", &Value::Float(3.14), "arg").is_err(),
            "Should reject float value"
        );
        assert!(
            validate_string("test", &Value::Bool(true), "arg").is_err(),
            "Should reject bool value"
        );
    }

    #[test]
    fn test_validate_numeric() {
        assert!(validate_numeric("test", &Value::Integer(42), "arg").is_ok());
        assert!(validate_numeric("test", &Value::Float(3.14), "arg").is_ok());
        assert!(validate_numeric(
            "test",
            &Value::from_string("not a number".to_string()),
            "arg"
        )
        .is_err());
    }
}

#[cfg(test)]
mod property_tests {
    use super::*;
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn test_validation_is_constant_time(arg_count in 0..100usize, expected in 0..100usize) {
            let args: Vec<Value> = (0..arg_count).map(|i| Value::Integer(i as i64)).collect();

            // Validation should be O(1) - just comparing lengths
            // Not O(n) iterating through arguments
            let _ = validate_arg_count("test", &args, expected);

            // This completes in constant time regardless of arg_count
            // Test passes without panic
        }

        #[test]
        fn test_error_messages_are_informative(func_name: String, expected in 0..100usize, actual in 0..100usize) {
            let args: Vec<Value> = (0..actual).map(|i| Value::Integer(i as i64)).collect();

            if expected != actual {
                let result = validate_arg_count(&func_name, &args, expected);
                if let Err(InterpreterError::RuntimeError(msg)) = result {
                    prop_assert!(msg.contains(&func_name));
                    prop_assert!(msg.contains(&expected.to_string()));
                    prop_assert!(msg.contains(&actual.to_string()));
                }
            }
        }
    }
}
