//! Error handling improvement validation tests
//!
//! These tests validate that the improved error handling
//! and ToJvmError trait work correctly.

use jvmrs::error::{
    JvmError, 
    RuntimeError, 
    MemoryError, 
    ParseError,
    ClassLoadingError,
    ToJvmError,
    to_runtime_error,
    to_parse_error,
    to_memory_error,
    to_class_loading_error
};

#[test]
fn test_string_error_conversion() {
    // Test string to JvmError conversion
    let error: JvmError = "Test runtime error".to_jvm_error();
    match error {
        JvmError::RuntimeError(ref rt_error) => {
            assert_eq!(rt_error.to_string(), "Unimplemented feature: Test runtime error");
        },
        _ => panic!("Expected RuntimeError variant"),
    }
}

#[test]
fn test_str_error_conversion() {
    // Test &str to JvmError conversion
    let error: JvmError = "Another test error".to_jvm_error();
    match error {
        JvmError::RuntimeError(ref rt_error) => {
            assert_eq!(rt_error.to_string(), "Unimplemented feature: Another test error");
        },
        _ => panic!("Expected RuntimeError variant"),
    }
}

#[test]
fn test_runtime_error_conversion() {
    let original_error = RuntimeError::IllegalArgument("Invalid value".to_string());
    let error: JvmError = original_error.to_jvm_error();
    
    match error {
        JvmError::RuntimeError(converted) => {
            assert_eq!(converted.to_string(), "Illegal argument: Invalid value");
        },
        _ => panic!("Expected RuntimeError variant"),
    }
}

#[test]
fn test_memory_error_conversion() {
    let original_error = MemoryError::OutOfMemory;
    let error: JvmError = original_error.to_jvm_error();
    
    match error {
        JvmError::MemoryError(converted) => {
            assert_eq!(converted.to_string(), "Out of memory");
        },
        _ => panic!("Expected MemoryError variant"),
    }
}

#[test]
fn test_parse_error_conversion() {
    let original_error = ParseError::InvalidMagic(0xDEADBEEF);
    let error: JvmError = original_error.to_jvm_error();
    
    match error {
        JvmError::ParseError(converted) => {
            assert_eq!(converted.to_string(), "Invalid magic number: 0xDEADBEEF (expected 0xCAFEBABE)");
        },
        _ => panic!("Expected ParseError variant"),
    }
}

#[test]
fn test_class_loading_error_conversion() {
    let original_error = ClassLoadingError::ClassFileNotFound("MissingClass.class".to_string());
    let error: JvmError = original_error.to_jvm_error();
    
    match error {
        JvmError::ClassLoadingError(converted) => {
            assert_eq!(converted.to_string(), "Class file not found: MissingClass.class");
        },
        _ => panic!("Expected ClassLoadingError variant"),
    }
}

#[test]
fn test_error_type_preservation() {
    // Test that error types are preserved through conversion
    let runtime_error = RuntimeError::NullPointerException;
    let memory_error: JvmError = runtime_error.to_jvm_error();
    
    // The error should still be a RuntimeError after conversion
    match memory_error {
        JvmError::RuntimeError(rt) => {
            assert!(matches!(rt, RuntimeError::NullPointerException));
        },
        _ => panic!("Expected RuntimeError variant"),
    }
}

#[test]
fn test_error_chaining() {
    // Test error conversion in a chain
    let parse_error = ParseError::InvalidUtf8String;
    let runtime_error = RuntimeError::ClassCastException("String".to_string(), "Integer".to_string());
    let memory_error = MemoryError::InvalidArrayLength(1000);
    
    let jvm_errors: Vec<JvmError> = vec![
        parse_error.to_jvm_error(),
        runtime_error.to_jvm_error(),
        memory_error.to_jvm_error(),
    ];
    
    assert_eq!(jvm_errors.len(), 3);
    
    // Verify each error maintains its specific type
    match &jvm_errors[0] {
        JvmError::ParseError(_) => {},
        _ => panic!("Expected ParseError"),
    }
    
    match &jvm_errors[1] {
        JvmError::RuntimeError(_) => {},
        _ => panic!("Expected RuntimeError"),
    }
    
    match &jvm_errors[2] {
        JvmError::MemoryError(_) => {},
        _ => panic!("Expected MemoryError"),
    }
}

#[test]
fn test_error_display_format() {
    let error = "Display test".to_jvm_error();
    let display_str = format!("{}", error);
    
    // Should contain "Runtime error:" prefix
    assert!(display_str.contains("Runtime error"));
    assert!(display_str.contains("Display test"));
    
    // Test specific error display formats
    let null_error = RuntimeError::NullPointerException.to_jvm_error();
    let null_display = format!("{}", null_error);
    assert_eq!(null_display, "Runtime error: Null pointer exception");
    
    let memory_error = MemoryError::OutOfMemory.to_jvm_error();
    let memory_display = format!("{}", memory_error);
    assert_eq!(memory_display, "Memory error: Out of memory");
}

#[test]
fn test_error_macro_conversion() {
    // Test the convert_result! macro (this is more of a syntax test)
    // In real usage, this would be used to convert Result types
    
    let result: Result<i32, RuntimeError> = Ok(42);
    // let converted = convert_result!(result);
    let converted = result.map_err(|e| e.to_jvm_error());
    assert!(converted.is_ok());
    assert_eq!(converted.unwrap(), 42);
    
    let error_result: Result<i32, RuntimeError> = Err(RuntimeError::NullPointerException);
    // let converted_error = convert_result!(error_result);
    let converted_error = error_result.map_err(|e| e.to_jvm_error());
    assert!(converted_error.is_err());
    match converted_error {
        Err(JvmError::RuntimeError(_)) => {},
        _ => panic!("Expected JvmError::RuntimeError"),
    }
}

#[test]
fn test_error_edge_cases() {
    // Test empty string conversion
    let empty_error: JvmError = "".to_jvm_error();
    match empty_error {
        JvmError::RuntimeError(ref rt_error) => {
            assert_eq!(rt_error.to_string(), "Unimplemented feature: ");
        },
        _ => panic!("Expected RuntimeError variant"),
    }
    
    // Test very long string
    let long_string = "A".repeat(10000);
    let long_error: JvmError = long_string.to_jvm_error();
    match long_error {
        JvmError::RuntimeError(ref rt_error) => {
            assert!(rt_error.to_string().len() > 1000);
        },
        _ => panic!("Expected RuntimeError variant"),
    }
}

#[test]
fn test_error_from_helper_functions() {
    // Test the existing helper functions still work
    let runtime_error = to_runtime_error("Test helper error");
    assert!(matches!(runtime_error, JvmError::RuntimeError(_)));
    
    let parse_error = ParseError::InvalidMagic(0x1234);
    let converted = to_parse_error(parse_error);
    assert!(matches!(converted, JvmError::ParseError(_)));
    
    let memory_error = MemoryError::InvalidHeapAddress(100);
    let converted = to_memory_error(memory_error);
    assert!(matches!(converted, JvmError::MemoryError(_)));
    
    let class_error = ClassLoadingError::ClassFileNotFound("test.class".to_string());
    let converted = to_class_loading_error(class_error);
    assert!(matches!(converted, JvmError::ClassLoadingError(_)));
}

#[test]
fn test_error_comparison() {
    // Test that converted errors maintain equality with originals
    let original = RuntimeError::IllegalArgument("test".to_string());
    let converted: JvmError = original.clone().to_jvm_error();
    
    // The converted error should contain the same original error
    match converted {
        JvmError::RuntimeError(rt) => {
            assert_eq!(rt.to_string(), original.to_string());
        },
        _ => panic!("Expected RuntimeError variant"),
    }
}

#[test]
fn test_error_clone_and_debug() {
    // Test that errors can be cloned and debug printed
    let original = RuntimeError::NullPointerException;
    let converted: JvmError = original.to_jvm_error();
    
    // Clone should work
    let cloned = converted.clone();
    assert!(matches!(cloned, JvmError::RuntimeError(_)));
    
    // Debug format should work
    let debug_str = format!("{:?}", converted);
    assert!(!debug_str.is_empty());
    assert!(debug_str.contains("RuntimeError"));
}