//! Unit tests for JVMRS

#[cfg(test)]
mod tests {
    use crate::class_file::ClassFile;
    use crate::class_file::ParseError;
    use crate::class_loader::{ClassLoader, parse_classpath};
    use crate::debug::{DebugConfig, JvmDebugger};
    use crate::error::{
        ClassLoadingError, JvmError, to_class_loading_error, to_parse_error, to_runtime_error,
    };
    use crate::memory::{Heap, HeapArray, Monitor, StackFrame, Value};
    use std::path::PathBuf;

    /// Build a minimal valid class file: class "Hello" with no super, no methods
    fn minimal_class_file_bytes() -> Vec<u8> {
        vec![
            0xCA, 0xFE, 0xBA, 0xBE, // magic
            0x00, 0x00, // minor version
            0x00, 0x34, // major version (52 = Java 8)
            0x00, 0x03, // constant pool count (3 entries: 0=placeholder, 1=Utf8, 2=Class)
            0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F, // CP[1]: Utf8 "Hello"
            0x07, 0x00, 0x01, // CP[2]: Class name_index=1
            0x00, 0x21, // access_flags (public super)
            0x00, 0x02, // this_class (index 2)
            0x00, 0x00, // super_class (0 = none)
            0x00, 0x00, // interfaces_count
            0x00, 0x00, // fields_count
            0x00, 0x00, // methods_count
            0x00, 0x00, // attributes_count
        ]
    }

    /// Test invalid magic number
    #[test]
    fn test_class_file_invalid_magic() {
        let bad_magic = vec![
            0xDE, 0xAD, 0xBE, 0xEF, // wrong magic
            0x00, 0x00, 0x00, 0x34, 0x00, 0x03, 0x01, 0x00, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F,
            0x07, 0x00, 0x01, 0x00, 0x21, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00,
        ];
        let result = ClassFile::parse(&bad_magic);
        assert!(result.is_err());
        if let Err(ParseError::InvalidMagic(magic)) = result {
            assert_eq!(magic, 0xDEADBEEF);
        } else {
            panic!("Expected InvalidMagic error");
        }
    }

    /// Test invalid constant pool tag
    #[test]
    fn test_class_file_invalid_constant_pool_tag() {
        let bad_tag = vec![
            0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x34, 0x00, 0x03, 0xFF, 0x00, 0x05, 0x48,
            0x65, 0x6C, 0x6C, 0x6F, // invalid tag 0xFF
        ];
        let result = ClassFile::parse(&bad_tag);
        assert!(result.is_err());
        if let Err(ParseError::InvalidConstantPoolTag(tag)) = result {
            assert_eq!(tag, 0xFF);
        } else {
            panic!("Expected InvalidConstantPoolTag error");
        }
    }

    /// Test that parsing fails gracefully for incomplete class files
    #[test]
    fn test_class_file_incomplete_parse() {
        let class_bytes = vec![
            0xCA, 0xFE, 0xBA, 0xBE, 0x00, 0x00, 0x00, 0x34, 0x00,
            0x10, // constant pool count but no entries
        ];
        let result = ClassFile::parse(&class_bytes);
        assert!(
            result.is_err(),
            "Should fail to parse incomplete class file"
        );
    }

    /// Test valid minimal class file parsing
    #[test]
    fn test_class_file_valid_parse() {
        let class_bytes = minimal_class_file_bytes();
        let result = ClassFile::parse(&class_bytes);
        assert!(
            result.is_ok(),
            "Should parse valid minimal class file: {:?}",
            result
        );
        let class_file = result.unwrap();
        assert_eq!(class_file.magic, 0xCAFEBABE);
        assert_eq!(class_file.major_version, 52);
        assert_eq!(class_file.get_class_name(), Some("Hello".to_string()));
        assert_eq!(class_file.get_super_class_name(), None);
        assert_eq!(class_file.get_string(1), Some("Hello".to_string()));
        assert!(class_file.find_method("main", "()V").is_none()); // No methods
    }

    // ========== Class Loader ==========
    #[test]
    fn test_class_loader_classpath_parsing() {
        let cp = parse_classpath("a:b:c");
        assert_eq!(cp.len(), 3);
        assert_eq!(cp[0], PathBuf::from("a"));
        assert_eq!(cp[1], PathBuf::from("b"));
        assert_eq!(cp[2], PathBuf::from("c"));
    }

    #[test]
    fn test_class_loader_classpath_parsing_empty() {
        let cp = parse_classpath("");
        assert!(cp.is_empty());
    }

    #[test]
    fn test_class_loader_classpath_parsing_whitespace() {
        let cp = parse_classpath(" a : b : c ");
        assert_eq!(cp.len(), 3);
        assert_eq!(cp[0], PathBuf::from("a"));
        assert_eq!(cp[1], PathBuf::from("b"));
        assert_eq!(cp[2], PathBuf::from("c"));
    }

    // ========== Error Handling ==========
    #[test]
    fn test_error_display() {
        use crate::error::{MemoryError, NativeError};
        
        // Test JvmError display
        let err = JvmError::ParseError(crate::error::ParseError::InvalidMagic(0xDEADBEEF));
        assert_eq!(
            format!("{}", err),
            "Parse error: Invalid magic number: 0xDEADBEEF (expected 0xCAFEBABE)"
        );

        // Test MemoryError display
        let out_of_memory = MemoryError::OutOfMemory;
        assert_eq!(format!("{}", out_of_memory), "Out of memory");

        // Test ClassLoadingError display
        let class_file_not_found = ClassLoadingError::ClassFileNotFound("Test.class".to_string());
        assert_eq!(
            format!("{}", class_file_not_found),
            "Class file not found: Test.class"
        );

        // Test NativeError display
        let native_method_not_found = NativeError::NativeMethodNotFound(
            "java.lang.System".to_string(),
            "currentTimeMillis".to_string(),
        );
        assert_eq!(
            format!("{}", native_method_not_found),
            "Native method java.lang.System.currentTimeMillis not found"
        );
    }

    // ========== Descriptor ==========
    #[test]
    fn test_descriptor_count_parameters() {
        use crate::interpreter::descriptor;
        assert_eq!(descriptor::count_parameters("()V"), 0);
        assert_eq!(descriptor::count_parameters("(I)I"), 1);
        assert_eq!(descriptor::count_parameters("(II)I"), 2);
        assert_eq!(descriptor::count_parameters("(J)J"), 2); // long takes 2
        assert_eq!(descriptor::count_parameters("(D)D"), 2); // double takes 2
        assert_eq!(descriptor::count_parameters("([Ljava/lang/String;)V"), 1);
        assert_eq!(descriptor::count_parameters("(Ljava/lang/String;)V"), 1);
    }

    #[test]
    fn test_descriptor_parse_params() {
        use crate::interpreter::descriptor;
        assert!(descriptor::parse_method_params("()V").is_empty());
        assert_eq!(descriptor::parse_method_params("(I)I"), ["int"]);
        assert_eq!(descriptor::parse_method_params("(II)I"), ["int", "int"]);
        assert_eq!(descriptor::parse_method_params("(F)F"), ["float"]);
        assert_eq!(
            descriptor::parse_method_params("(Ljava/lang/String;)V"),
            ["object"]
        );
        assert_eq!(
            descriptor::parse_method_params("([I[[Ljava/lang/String;J)V"),
            ["array", "array", "long"]
        );
    }

    #[test]
    fn test_descriptor_malformed_inputs_do_not_panic() {
        use crate::interpreter::descriptor;
        assert_eq!(descriptor::count_parameters("I)V"), 0);
        assert_eq!(descriptor::count_parameters("([Ljava/lang/String)V"), 0);
        assert_eq!(
            descriptor::parse_method_params("([Ljava/lang/String)V"),
            Vec::<String>::new()
        );
    }

    // ========== Heap array operations ==========
    #[test]
    fn test_heap_array_get_set() {
        use crate::memory::HeapArray;
        let mut heap = Heap::new();
        let addr = heap.allocate_array(HeapArray::IntArray(vec![0, 0, 0]));
        heap.array_set(addr, 1, Value::Int(42)).unwrap();
        let val = heap.array_get(addr, 1).unwrap();
        assert_eq!(val, Value::Int(42));
    }

    #[test]
    fn test_heap_array_length() {
        use crate::memory::HeapArray;
        let mut heap = Heap::new();
        let addr = heap.allocate_array(HeapArray::IntArray(vec![1, 2, 3, 4, 5]));
        assert_eq!(heap.array_length(addr).unwrap(), 5);
    }

    #[test]
    fn test_heap_get_field_set_field() {
        let mut heap = Heap::new();
        let addr = heap.allocate("Test".to_string());
        heap.set_field(addr, "x".to_string(), Value::Int(100))
            .unwrap();
        let val = heap.get_field(addr, "x").unwrap();
        assert_eq!(val, Value::Int(100));
    }

    // ========== Memory static fields ==========
    #[test]
    fn test_memory_static_fields() {
        use crate::memory::Memory;
        let mut mem = Memory::new();
        mem.set_static("Test".to_string(), "field".to_string(), Value::Int(123));
        let val = mem.get_static("Test", "field").unwrap();
        assert_eq!(*val, Value::Int(123));
    }


    // ========== Interpreter run_main integration ==========
    #[test]
    fn test_interpreter_run_minimal() {
        use crate::interpreter::Interpreter;
        use std::path::PathBuf;
        let examples = PathBuf::from("examples");
        if !examples.exists() {
            return;
        }
        let mut interpreter = Interpreter::with_classpath(vec![examples]);
        interpreter.set_jit_enabled(false);
        let result = interpreter.run_main("Minimal");
        assert!(result.is_ok(), "run_main Minimal failed: {:?}", result);
    }

    #[test]
    fn test_interpreter_run_test_get_static() {
        use crate::interpreter::Interpreter;
        use std::path::PathBuf;
        let examples = PathBuf::from("examples");
        if !examples.exists() {
            return;
        }
        let mut interpreter = Interpreter::with_classpath(vec![examples]);
        interpreter.set_jit_enabled(false);
        let result = interpreter.run_main("TestGetStatic");
        assert!(
            result.is_ok(),
            "run_main TestGetStatic failed: {:?}",
            result
        );
    }

    #[test]
    fn test_interpreter_run_test_ldc() {
        use crate::interpreter::Interpreter;
        use std::path::PathBuf;
        let examples = PathBuf::from("examples");
        if !examples.exists() {
            return;
        }
        let mut interpreter = Interpreter::with_classpath(vec![examples]);
        interpreter.set_jit_enabled(false);
        let result = interpreter.run_main("TestLdc");
        assert!(result.is_ok(), "run_main TestLdc failed: {:?}", result);
    }

    #[test]
    fn test_interpreter_memory_access() {
        use crate::interpreter::Interpreter;
        let interpreter = Interpreter::new();
        let memory = interpreter.memory();
        assert_eq!(memory.heap.object_count(), 0);
        assert_eq!(memory.heap.array_count(), 0);
    }

    // ========== Class loader load from examples ==========
    #[test]
    fn test_class_loader_load_minimal() {
        let examples = PathBuf::from("examples");
        if !examples.exists() {
            return;
        }
        let mut loader = ClassLoader::new(vec![examples]);
        let result = loader.load_class("Minimal");
        assert!(result.is_ok(), "load Minimal: {:?}", result);
        assert!(loader.is_class_loaded("Minimal"));
        let class = loader.get_class("Minimal").unwrap();
        assert_eq!(class.get_class_name(), Some("Minimal".to_string()));
    }

    // ========== Annotations ==========
    #[test]
    fn test_annotations_on_minimal_class() {
        let examples = PathBuf::from("examples");
        if !examples.exists() {
            return;
        }
        let mut loader = ClassLoader::new(vec![examples]);
        if loader.load_class("Minimal").is_err() {
            return;
        }
        let class = match loader.get_class("Minimal") {
            Some(c) => c,
            None => return,
        };
        let annotations = class.get_class_annotations();
        assert!(annotations.is_empty() || !annotations.is_empty());
    }

    // ========== AOP ==========
    #[test]
    fn test_aop_create_proxy() {
        use crate::aop::create_proxy;
        let _proxy = create_proxy("java/lang/Object".to_string(), 1, None);
    }

    // ========== Interpreter Opcode Tests ==========
    #[test]
    fn test_interpreter_stack_ops() {
        use crate::interpreter::Interpreter;
        use crate::memory::{StackFrame, Value};
        use crate::class_file::ClassFile;

        let mut interpreter = Interpreter::new();
        // Minimal class file just for dispatch
        let class = ClassFile::parse(&minimal_class_file_bytes()).unwrap();
        let code = vec![];
        let mut frame = StackFrame::new(10, 10, "test".to_string());

        // Test pop2 (0x58)
        // Case 1: 2 cat 1 values
        frame.push(Value::Int(1)).unwrap();
        frame.push(Value::Int(2)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x58).unwrap();
        assert!(frame.stack.is_empty());

        // Case 2: 1 cat 2 value
        frame.push(Value::Long(123)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x58).unwrap();
        assert!(frame.stack.is_empty());

        // Test dup2 (0x5c)
        // Case 1: 2 cat 1 values
        frame.push(Value::Int(1)).unwrap();
        frame.push(Value::Int(2)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x5c).unwrap();
        // Stack: 1, 2, 1, 2
        assert_eq!(frame.stack.len(), 4);
        assert_eq!(frame.pop().unwrap(), Value::Int(2));
        assert_eq!(frame.pop().unwrap(), Value::Int(1));
        assert_eq!(frame.pop().unwrap(), Value::Int(2));
        assert_eq!(frame.pop().unwrap(), Value::Int(1));

        // Case 2: 1 cat 2 value
        frame.push(Value::Long(123)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x5c).unwrap();
        // Stack: 123, 123
        assert_eq!(frame.stack.len(), 2);
        assert_eq!(frame.pop().unwrap(), Value::Long(123));
        assert_eq!(frame.pop().unwrap(), Value::Long(123));

        // Test dup2_x1 (0x5d)
        // Case 1: 3 cat 1 values: v3, v2, v1 -> v2, v1, v3, v2, v1
        frame.push(Value::Int(3)).unwrap();
        frame.push(Value::Int(2)).unwrap();
        frame.push(Value::Int(1)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x5d).unwrap();
        // Expected: 2, 1, 3, 2, 1 (top is 1)
        assert_eq!(frame.pop().unwrap(), Value::Int(1));
        assert_eq!(frame.pop().unwrap(), Value::Int(2));
        assert_eq!(frame.pop().unwrap(), Value::Int(3));
        assert_eq!(frame.pop().unwrap(), Value::Int(1));
        assert_eq!(frame.pop().unwrap(), Value::Int(2));
        assert!(frame.stack.is_empty());
    }

    #[test]
    fn test_interpreter_arithmetic_ops() {
        use crate::interpreter::Interpreter;
        use crate::memory::{StackFrame, Value};
        use crate::class_file::ClassFile;

        let mut interpreter = Interpreter::new();
        let class = ClassFile::parse(&minimal_class_file_bytes()).unwrap();
        let code = vec![];
        let mut frame = StackFrame::new(10, 10, "test".to_string());

        // Test ishl (0x78)
        frame.push(Value::Int(2)).unwrap(); // value
        frame.push(Value::Int(1)).unwrap(); // shift
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x78).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(4));

        // Test iand (0x7e)
        frame.push(Value::Int(0b1100)).unwrap();
        frame.push(Value::Int(0b1010)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x7e).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(0b1000));
        
        // Test lshl (0x79)
        frame.push(Value::Long(2)).unwrap(); // value
        frame.push(Value::Int(1)).unwrap(); // shift
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x79).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Long(4));
    }
    
    #[test]
    fn test_interpreter_conversions() {
        use crate::interpreter::Interpreter;
        use crate::memory::{StackFrame, Value};
        use crate::class_file::ClassFile;

        let mut interpreter = Interpreter::new();
        let class = ClassFile::parse(&minimal_class_file_bytes()).unwrap();
        let code = vec![];
        let mut frame = StackFrame::new(10, 10, "test".to_string());
        
        // Test i2l (0x85)
        frame.push(Value::Int(123)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x85).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Long(123));
        
        // Test i2f (0x86)
        frame.push(Value::Int(100)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x86).unwrap();
        let val = frame.pop().unwrap();
        match val {
            Value::Float(f) => assert_eq!(f, 100.0),
            _ => panic!("Expected float"),
        }
    }

    #[test]
    fn test_interpreter_arith_cmp_ops() {
        use crate::interpreter::Interpreter;
        use crate::memory::{StackFrame, Value};
        use crate::class_file::ClassFile;

        let mut interpreter = Interpreter::new();
        let class = ClassFile::parse(&minimal_class_file_bytes()).unwrap();
        let code = vec![];
        let mut frame = StackFrame::new(10, 10, "test".to_string());

        // Test ladd (0x61)
        frame.push(Value::Long(100)).unwrap();
        frame.push(Value::Long(200)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x61).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Long(300));

        // Test fadd (0x62)
        frame.push(Value::Float(1.5)).unwrap();
        frame.push(Value::Float(2.5)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x62).unwrap();
        match frame.pop().unwrap() {
            Value::Float(f) => assert_eq!(f, 4.0),
            _ => panic!("Expected float"),
        }

        // Test dadd (0x63)
        frame.push(Value::Double(1.5)).unwrap();
        frame.push(Value::Double(2.5)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x63).unwrap();
        match frame.pop().unwrap() {
            Value::Double(d) => assert_eq!(d, 4.0),
            _ => panic!("Expected double"),
        }

        // Test lcmp (0x94)
        frame.push(Value::Long(10)).unwrap();
        frame.push(Value::Long(20)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x94).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(-1)); // 10 < 20

        frame.push(Value::Long(20)).unwrap();
        frame.push(Value::Long(10)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x94).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(1)); // 20 > 10

        frame.push(Value::Long(10)).unwrap();
        frame.push(Value::Long(10)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x94).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(0)); // 10 == 10

        // Test fcmpl (0x95)
        frame.push(Value::Float(1.0)).unwrap();
        frame.push(Value::Float(f32::NAN)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x95).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(-1)); // 1.0 < NaN -> -1

        // Test fcmpg (0x96)
        frame.push(Value::Float(1.0)).unwrap();
        frame.push(Value::Float(f32::NAN)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x96).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(1)); // 1.0 < NaN -> 1

        // Test dcmpl (0x97)
        frame.push(Value::Double(1.0)).unwrap();
        frame.push(Value::Double(2.0)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x97).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(-1)); // 1.0 < 2.0
    }

    #[test]
    fn test_interpreter_array_ops() {
        use crate::interpreter::Interpreter;
        use crate::memory::{StackFrame, Value, HeapArray};
        use crate::class_file::ClassFile;

        let mut interpreter = Interpreter::new();
        let class = ClassFile::parse(&minimal_class_file_bytes()).unwrap();
        let code = vec![];
        let mut frame = StackFrame::new(10, 10, "test".to_string());

        // Test baload (0x33) with sign extension
        // Create a byte array with -1 (0xFF)
        let addr = interpreter.memory.heap.allocate_array(HeapArray::ByteArray(vec![0xFF]));
        frame.push(Value::ArrayRef(addr)).unwrap();
        frame.push(Value::Int(0)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x33).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(-1));

        // Test caload (0x34) with zero extension
        // Create a char array with 0xFFFF (should be 65535, not -1)
        let addr = interpreter.memory.heap.allocate_array(HeapArray::CharArray(vec![0xFFFF]));
        frame.push(Value::ArrayRef(addr)).unwrap();
        frame.push(Value::Int(0)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x34).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(65535));

        // Test saload (0x35) with sign extension
        // Create a short array with -1 (0xFFFF)
        let addr = interpreter.memory.heap.allocate_array(HeapArray::ShortArray(vec![-1]));
        frame.push(Value::ArrayRef(addr)).unwrap();
        frame.push(Value::Int(0)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x35).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Int(-1));

        // Test laload/lastore (0x2f / 0x50)
        let addr = interpreter.memory.heap.allocate_array(HeapArray::LongArray(vec![0; 1]));
        
        // Store
        frame.push(Value::ArrayRef(addr)).unwrap();
        frame.push(Value::Int(0)).unwrap();
        frame.push(Value::Long(1234567890123)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x50).unwrap();

        // Load
        frame.push(Value::ArrayRef(addr)).unwrap();
        frame.push(Value::Int(0)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x2f).unwrap();
        assert_eq!(frame.pop().unwrap(), Value::Long(1234567890123));

        // Test faload/fastore (0x30 / 0x51)
        let addr = interpreter.memory.heap.allocate_array(HeapArray::FloatArray(vec![0.0; 1]));

        // Store
        frame.push(Value::ArrayRef(addr)).unwrap();
        frame.push(Value::Int(0)).unwrap();
        frame.push(Value::Float(3.14)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x51).unwrap();

        // Load
        frame.push(Value::ArrayRef(addr)).unwrap();
        frame.push(Value::Int(0)).unwrap();
        interpreter.dispatch_instruction(&class, &code, &mut frame, 0x30).unwrap();
        match frame.pop().unwrap() {
            Value::Float(f) => assert!((f - 3.14).abs() < 1e-6),
            _ => panic!("Expected float"),
        }
    }

    // ========== Serialization ==========
    #[test]
    fn test_serialize_deserialize_value() {
        use crate::serialization::{deserialize_value, serialize_value};
        let v = Value::Int(42);
        let bytes = serialize_value(&v).unwrap();
        let restored = deserialize_value(&bytes).unwrap();
        assert_eq!(v, restored);

        let v2 = Value::Reference(100);
        let bytes2 = serialize_value(&v2).unwrap();
        let restored2 = deserialize_value(&bytes2).unwrap();
        assert_eq!(v2, restored2);
    }

    #[test]
    fn test_serialize_deserialize_object() {
        use crate::memory::HeapObject;
        use crate::serialization::{deserialize_object, serialize_object};
        use std::collections::HashMap;
        let mut fields = HashMap::new();
        fields.insert("x".to_string(), Value::Int(10));
        fields.insert("y".to_string(), Value::Int(20));
        let obj = HeapObject {
            fields,
            class_name: "TestClass".to_string(),
            marked: false,
            string_data: None,
            monitor: None,
        };
        let bytes = serialize_object(&obj).unwrap();
        let restored = deserialize_object(&bytes, "TestClass").unwrap();
        assert_eq!(obj.class_name, restored.class_name);
        assert_eq!(obj.fields.get("x"), restored.fields.get("x"));
        assert_eq!(obj.fields.get("y"), restored.fields.get("y"));
    }

    #[test]
    fn test_serialize_deserialize_object_with_string() {
        use crate::memory::HeapObject;
        use crate::serialization::{deserialize_object, serialize_object};
        use std::collections::HashMap;
        let obj = HeapObject {
            fields: HashMap::new(),
            class_name: "java/lang/String".to_string(),
            marked: false,
            string_data: Some("hello".to_string()),
            monitor: None,
        };
        let bytes = serialize_object(&obj).unwrap();
        let restored = deserialize_object(&bytes, "java/lang/String").unwrap();
        assert_eq!(obj.class_name, restored.class_name);
        assert_eq!(restored.string_data, Some("hello".to_string()));
    }

    #[test]
    fn test_serialize_all_value_types() {
        use crate::serialization::{deserialize_value, serialize_value};
        let cases = [
            Value::Null,
            Value::Int(-1),
            Value::Long(12345678901234),
            Value::Float(3.14),
            Value::Double(2.718),
            Value::Reference(999),
            Value::ArrayRef(100),
        ];
        for v in cases {
            let bytes = serialize_value(&v).unwrap();
            let restored = deserialize_value(&bytes).unwrap();
            assert_eq!(v, restored, "Failed for {:?}", v);
        }
    }

    #[test]
    fn test_is_serializable_class() {
        use crate::class_file::ClassFile;
        use crate::serialization::is_serializable_class;
        let class = ClassFile::parse(&minimal_class_file_bytes()).unwrap();
        assert!(!is_serializable_class(&class));
    }

    // ========== Interpreter reflection ==========
    #[test]
    fn test_interpreter_new_instance() {
        use crate::interpreter::Interpreter;
        use std::path::PathBuf;
        let examples = PathBuf::from("examples");
        if !examples.exists() {
            return;
        }
        let mut interpreter = Interpreter::with_classpath(vec![examples]);
        interpreter.set_jit_enabled(false);
        let obj = interpreter.new_instance("Minimal", &[]).unwrap();
        let addr = obj.as_reference().unwrap();
        assert!(addr > 0);
    }

    #[test]
    fn test_interpreter_get_object_class() {
        use crate::interpreter::Interpreter;
        use std::path::PathBuf;
        let examples = PathBuf::from("examples");
        if !examples.exists() {
            return;
        }
        let mut interpreter = Interpreter::with_classpath(vec![examples]);
        interpreter.set_jit_enabled(false);
        let obj = interpreter.new_instance("Minimal", &[]).unwrap();
        let class_name = interpreter.get_object_class(&obj).unwrap();
        assert_eq!(class_name, "Minimal");
    }

    #[test]
    fn test_interpreter_get_set_field_value() {
        use crate::interpreter::Interpreter;
        use std::path::PathBuf;
        let examples = PathBuf::from("examples");
        if !examples.exists() {
            return;
        }
        let mut interpreter = Interpreter::with_classpath(vec![examples]);
        interpreter.set_jit_enabled(false);
        let obj = interpreter.new_instance("Minimal", &[]).unwrap();
        interpreter.set_field_value(&obj, "x", Value::Int(42)).ok();
        let val = interpreter.get_field_value(&obj, "x");
        if val.is_ok() {
            assert_eq!(val.unwrap(), Value::Int(42));
        }
    }

    #[test]
    fn test_interpreter_memory_mut() {
        use crate::interpreter::Interpreter;
        use crate::memory::HeapArray;
        let mut interpreter = Interpreter::new();
        let mem = interpreter.memory_mut();
        let addr = mem.heap.allocate_array(HeapArray::IntArray(vec![1, 2, 3]));
        assert_eq!(mem.heap.array_length(addr).unwrap(), 3);
    }

    #[test]
    fn test_reflection_api_delegates_to_interpreter() {
        use crate::interpreter::Interpreter;
        use crate::reflection::ReflectionApi;
        use std::path::PathBuf;
        let examples = PathBuf::from("examples");
        if !examples.exists() {
            return;
        }
        let mut interpreter = Interpreter::with_classpath(vec![examples]);
        interpreter.set_jit_enabled(false);
        let api = ReflectionApi::new();
        let obj = api.new_instance(&mut interpreter, "Minimal", &[]).unwrap();
        let class_name = api.get_object_class(&interpreter, &obj).unwrap();
        assert_eq!(class_name, "Minimal");
    }

    #[test]
    fn test_interpreter_reflection_errors() {
        use crate::interpreter::Interpreter;
        use crate::memory::Value;
        let interpreter = Interpreter::new();
        let err = interpreter.get_field_value(&Value::Int(1), "x");
        assert!(err.is_err());
        let err2 = interpreter.get_object_class(&Value::Int(1));
        assert!(err2.is_err());
    }
}
