// Integration tests for Anonymous Records (Issue #109 Workstream 3)
// Tests compilation and execution of anonymous record operations

use fusabi::run_source;
use fusabi_vm::Value;

// ============================================================================
// BASIC ANONYMOUS RECORD LITERAL TESTS
// ============================================================================

#[test]
fn test_empty_anonymous_record() {
    let source = r#"{||}"#;
    let result = run_source(source).unwrap();
    assert!(matches!(result, Value::Record(_)));
    if let Value::Record(rec) = result {
        assert_eq!(rec.lock().unwrap().len(), 0);
    }
}

#[test]
fn test_single_field_anonymous_record() {
    let source = r#"{| name = "Alice" |}"#;
    let result = run_source(source).unwrap();
    assert!(matches!(result, Value::Record(_)));
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert_eq!(borrowed.len(), 1);
        assert_eq!(
            *borrowed.get("name").unwrap(),
            Value::Str("Alice".to_string())
        );
    }
}

#[test]
fn test_multi_field_anonymous_record() {
    let source = r#"{| name = "Bob"; age = 30; active = true |}"#;
    let result = run_source(source).unwrap();
    assert!(matches!(result, Value::Record(_)));
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert_eq!(borrowed.len(), 3);
        assert_eq!(
            *borrowed.get("name").unwrap(),
            Value::Str("Bob".to_string())
        );
        assert_eq!(*borrowed.get("age").unwrap(), Value::Int(30));
        assert_eq!(*borrowed.get("active").unwrap(), Value::Bool(true));
    }
}

#[test]
fn test_anonymous_record_with_computed_fields() {
    let source = r#"{| x = 5 + 3; y = 10 * 2; sum = (5 + 3) + (10 * 2) |}"#;
    let result = run_source(source).unwrap();
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert_eq!(*borrowed.get("x").unwrap(), Value::Int(8));
        assert_eq!(*borrowed.get("y").unwrap(), Value::Int(20));
        assert_eq!(*borrowed.get("sum").unwrap(), Value::Int(28));
    }
}

#[test]
fn test_anonymous_record_trailing_semicolon() {
    let source = r#"{| x = 1; y = 2; |}"#;
    let result = run_source(source).unwrap();
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert_eq!(borrowed.len(), 2);
        assert_eq!(*borrowed.get("x").unwrap(), Value::Int(1));
        assert_eq!(*borrowed.get("y").unwrap(), Value::Int(2));
    }
}

// ============================================================================
// FIELD ACCESS TESTS
// ============================================================================

#[test]
fn test_anonymous_record_field_access() {
    let source = r#"
        let person = {| name = "Charlie"; age = 25 |} in
        person.name
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Str("Charlie".to_string()));
}

#[test]
fn test_anonymous_record_multiple_field_accesses() {
    let source = r#"
        let person = {| name = "Diana"; age = 35; city = "NYC" |} in
        person.age
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(35));
}

#[test]
fn test_anonymous_record_chained_field_access() {
    let source = r#"
        let config = {| server = {| host = "localhost"; port = 8080 |} |} in
        config.server.host
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Str("localhost".to_string()));
}

// ============================================================================
// RECORD UPDATE TESTS (with anonymous records)
// ============================================================================

#[test]
fn test_anonymous_record_update() {
    let source = r#"
        let person = {| name = "Eve"; age = 28 |} in
        let updated = { person with age = 29 } in
        updated.age
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(29));
}

#[test]
fn test_anonymous_record_multi_field_update() {
    let source = r#"
        let person = {| name = "Frank"; age = 40; city = "LA" |} in
        let updated = { person with age = 41; city = "SF" } in
        updated.city
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Str("SF".to_string()));
}

#[test]
fn test_anonymous_record_update_preserves_original() {
    let source = r#"
        let person = {| name = "Grace"; age = 30 |} in
        let updated = { person with age = 31 } in
        person.age
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(30));
}

// ============================================================================
// NESTED ANONYMOUS RECORD TESTS
// ============================================================================

#[test]
fn test_nested_anonymous_record_literal() {
    let source = r#"
        {|
            user = {| name = "Henry"; id = 101 |};
            metadata = {| created = "2024-01-01"; version = 1 |}
        |}
    "#;
    let result = run_source(source).unwrap();
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert!(matches!(borrowed.get("user").unwrap(), Value::Record(_)));
        assert!(matches!(
            borrowed.get("metadata").unwrap(),
            Value::Record(_)
        ));
    }
}

#[test]
fn test_nested_anonymous_record_access() {
    let source = r#"
        let data = {|
            user = {| name = "Iris"; profile = {| age = 32; bio = "Developer" |} |}
        |} in
        data.user.profile.bio
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Str("Developer".to_string()));
}

#[test]
fn test_nested_anonymous_record_update() {
    let source = r#"
        let data = {| user = {| name = "Jack"; age = 25 |} |} in
        let updated = { data with user = {| name = "Jack"; age = 26 |} } in
        updated.user.age
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(26));
}

// ============================================================================
// MIXED REGULAR AND ANONYMOUS RECORDS
// ============================================================================

#[test]
fn test_mix_regular_and_anonymous_records() {
    let source = r#"
        let anon = {| x = 10; y = 20 |} in
        let regular = { a = 1; b = 2 } in
        anon.x + regular.a
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(11));
}

#[test]
fn test_anonymous_record_nested_in_regular() {
    let source = r#"
        let outer = { inner = {| value = 42 |} } in
        outer.inner.value
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(42));
}

#[test]
fn test_regular_record_nested_in_anonymous() {
    let source = r#"
        let outer = {| inner = { value = 99 } |} in
        outer.inner.value
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(99));
}

// ============================================================================
// ANONYMOUS RECORDS WITH FUNCTIONS
// ============================================================================

#[test]
#[ignore = "TODO: Requires compiler support for record field variable shadowing"]
fn test_function_returning_anonymous_record() {
    let source = r#"
        let makePerson name age = {| name = name; age = age |} in
        makePerson "Kelly" 27
    "#;
    let result = run_source(source).unwrap();
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert_eq!(
            *borrowed.get("name").unwrap(),
            Value::Str("Kelly".to_string())
        );
        assert_eq!(*borrowed.get("age").unwrap(), Value::Int(27));
    }
}

#[test]
#[ignore = "TODO: Requires compiler support for parameter variable scoping"]
fn test_function_taking_anonymous_record() {
    let source = r#"
        let getAge person = person.age in
        let person = {| name = "Larry"; age = 45 |} in
        getAge person
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(45));
}

#[test]
#[ignore = "TODO: Requires compiler support for parameter variable scoping in record updates"]
fn test_function_updating_anonymous_record() {
    let source = r#"
        let incrementAge person = { person with age = person.age + 1 } in
        let person = {| name = "Mary"; age = 29 |} in
        let older = incrementAge person in
        older.age
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(30));
}

// ============================================================================
// ANONYMOUS RECORDS IN DATA STRUCTURES
// ============================================================================

#[test]
#[ignore = "TODO: Requires runtime support for List.head with records"]
fn test_anonymous_record_in_list() {
    let source = r#"
        let people = [
            {| name = "Oscar"; age = 28 |};
            {| name = "Pam"; age = 32 |}
        ] in
        let first = List.head people in
        first.name
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Str("Oscar".to_string()));
}

#[test]
fn test_anonymous_record_with_array_field() {
    let source = r#"
        let data = {| values = [|1; 2; 3|]; count = 3 |} in
        data.count
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(3));
}

#[test]
fn test_anonymous_record_with_list_field() {
    let source = r#"
        let data = {| items = [1; 2; 3]; total = 3 |} in
        data.total
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(3));
}

// ============================================================================
// COMPLEX SCENARIOS
// ============================================================================

#[test]
fn test_multiple_anonymous_record_updates() {
    let source = r#"
        let person = {| name = "Tom"; age = 20; city = "NYC" |} in
        let person2 = { person with age = 21 } in
        let person3 = { person2 with city = "LA" } in
        person3.city
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Str("LA".to_string()));
}

#[test]
fn test_anonymous_record_equality_fields() {
    let source = r#"
        let p1 = {| x = 5; y = 10 |} in
        let p2 = {| x = 5; y = 10 |} in
        p1.x = p2.x && p1.y = p2.y
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Bool(true));
}

#[test]
fn test_anonymous_record_with_boolean_fields() {
    let source = r#"
        let flags = {| active = true; verified = false; admin = true |} in
        flags.active && flags.admin
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Bool(true));
}

#[test]
fn test_anonymous_record_field_arithmetic() {
    let source = r#"
        let rect = {| width = 10; height = 20 |} in
        rect.width * rect.height
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(200));
}

#[test]
fn test_anonymous_record_update_with_computation() {
    let source = r#"
        let counter = {| count = 5 |} in
        let updated = { counter with count = counter.count * 2 } in
        updated.count
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(10));
}

#[test]
fn test_deeply_nested_anonymous_records() {
    let source = r#"
        let config = {|
            app = {|
                name = "MyApp";
                settings = {|
                    debug = true;
                    port = 3000
                |}
            |}
        |} in
        config.app.settings.port
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Int(3000));
}

// ============================================================================
// EDGE CASES
// ============================================================================

#[test]
fn test_anonymous_record_with_string_field_names() {
    let source = r#"
        {| firstName = "Uma"; lastName = "Volt" |}
    "#;
    let result = run_source(source).unwrap();
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert_eq!(borrowed.len(), 2);
        assert!(borrowed.contains_key("firstName"));
        assert!(borrowed.contains_key("lastName"));
    }
}

#[test]
fn test_anonymous_record_field_shadowing() {
    let source = r#"
        let name = "Wendy" in
        let person = {| name = name; age = 24 |} in
        person.name
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Str("Wendy".to_string()));
}

#[test]
fn test_anonymous_record_in_match_value() {
    let source = r#"
        let person = {| name = "Sam"; age = 40 |} in
        match person.age with
        | 40 -> "forty"
        | _ -> "other"
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Str("forty".to_string()));
}

#[test]
fn test_match_on_anonymous_record_field() {
    let source = r#"
        let status = {| code = 200; message = "OK" |} in
        match status.code with
        | 200 -> true
        | _ -> false
    "#;
    let result = run_source(source).unwrap();
    assert_eq!(result, Value::Bool(true));
}

// ============================================================================
// ANONYMOUS RECORD SYNTAX VARIATIONS
// ============================================================================

#[test]
fn test_anonymous_record_no_semicolons() {
    let source = r#"{| x = 1 |}"#;
    let result = run_source(source).unwrap();
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert_eq!(borrowed.len(), 1);
        assert_eq!(*borrowed.get("x").unwrap(), Value::Int(1));
    }
}

#[test]
fn test_anonymous_record_multiline() {
    let source = r#"
        {|
            a = 1;
            b = 2;
            c = 3
        |}
    "#;
    let result = run_source(source).unwrap();
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert_eq!(borrowed.len(), 3);
    }
}

#[test]
fn test_anonymous_record_inline_expressions() {
    let source = r#"
        {| result = if true then 1 else 0; flag = 1 > 0 |}
    "#;
    let result = run_source(source).unwrap();
    if let Value::Record(rec) = result {
        let borrowed = rec.lock().unwrap();
        assert_eq!(*borrowed.get("result").unwrap(), Value::Int(1));
        assert_eq!(*borrowed.get("flag").unwrap(), Value::Bool(true));
    }
}
