use rex::{
    engine::{Engine, Module, Value},
    parser::parse as parse_rex,
    typesystem::{BuiltinTypeId, Type},
};

#[tokio::test]
async fn record_update_end_to_end() {
    let code = r#"
        type Foo = Bar { x: i32, y: i32, z: i32 };
        type Sum = A { x: i32 } | B { x: i32 };

        let
            foo: Foo = Bar { x = 1, y = 2, z = 3 },
            foo2 = { foo with { x = 6 } },
            sum: Sum = A { x = 1 },
            sum2 = match sum with {
                case A {x} -> { sum with { x = x + 1 } };
                case B {x} -> { sum with { x = x + 2 } };
            }
        in
            (foo2.x, match sum2 with { case A {x} -> x; case B {x} -> x; })
    "#;
    let program = parse_rex(code).unwrap();

    let mut engine = Engine::with_prelude(()).unwrap();
    let mut module = Module::global();
    module.add_decls(program.decls.clone());
    engine.inject_module(module).unwrap();
    let (value_handle, ty) = engine
        .into_evaluator()
        .eval(program.body.as_ref().unwrap().as_ref())
        .await
        .unwrap();
    assert_eq!(
        ty,
        Type::tuple(vec![
            Type::builtin(BuiltinTypeId::I32),
            Type::builtin(BuiltinTypeId::I32)
        ])
    );
    let Value::Tuple(items) = value_handle.value().unwrap() else {
        panic!("expected tuple, got {}", value_handle.type_name().unwrap());
    };
    assert_eq!(items.len(), 2);

    let a_handle = &items[0];
    let Value::I32(a) = a_handle.value().unwrap() else {
        panic!("expected i32, got {}", a_handle.type_name().unwrap());
    };
    let b_handle = &items[1];
    let Value::I32(b) = b_handle.value().unwrap() else {
        panic!("expected i32, got {}", b_handle.type_name().unwrap());
    };
    assert_eq!(a, 6);
    assert_eq!(b, 2);
}
