//! Integration tests for the filter module (parse + eval together)

use jarq::filter::evaluate_all;
use simd_json::OwnedValue as Value;
use simd_json::json;

#[test]
fn test_evaluate_all_single_input() {
    let inputs = vec![json!({"name": "alice"})];
    let result = evaluate_all(".name", &inputs).unwrap();
    assert_eq!(result, vec![json!("alice")]);
}

#[test]
fn test_evaluate_all_multiple_inputs() {
    let inputs = vec![json!({"name": "alice"}), json!({"name": "bob"})];
    let result = evaluate_all(".name", &inputs).unwrap();
    assert_eq!(result, vec![json!("alice"), json!("bob")]);
}

#[test]
fn test_evaluate_all_with_iterate() {
    let inputs = vec![json!([1, 2]), json!([3, 4])];
    let result = evaluate_all(".[]", &inputs).unwrap();
    assert_eq!(result, vec![json!(1), json!(2), json!(3), json!(4)]);
}

#[test]
fn test_evaluate_all_empty_inputs() {
    let inputs: Vec<Value> = vec![];
    let result = evaluate_all(".", &inputs).unwrap();
    assert!(result.is_empty());
}

#[test]
fn test_evaluate_all_primitives() {
    let inputs = vec![json!(1), json!(2), json!(3)];
    let result = evaluate_all(".", &inputs).unwrap();
    assert_eq!(result, vec![json!(1), json!(2), json!(3)]);
}

#[test]
fn test_evaluate_all_mixed_types() {
    let inputs = vec![
        json!({"x": 1}),
        json!([1, 2]),
        json!("hello"),
        json!(42),
        json!(null),
    ];
    let result = evaluate_all(".", &inputs).unwrap();
    assert_eq!(result.len(), 5);
}

// Builtin integration tests
#[test]
fn test_builtin_length() {
    let inputs = vec![json!([1, 2, 3])];
    let result = evaluate_all("length", &inputs).unwrap();
    assert_eq!(result, vec![json!(3)]);
}

#[test]
fn test_builtin_keys() {
    let inputs = vec![json!({"b": 1, "a": 2})];
    let result = evaluate_all("keys", &inputs).unwrap();
    assert_eq!(result, vec![json!(["a", "b"])]); // sorted
}

#[test]
fn test_builtin_type() {
    let inputs = vec![
        json!(null),
        json!(true),
        json!(42),
        json!("hi"),
        json!([]),
        json!({}),
    ];
    let result = evaluate_all("type", &inputs).unwrap();
    assert_eq!(
        result,
        vec![
            json!("null"),
            json!("boolean"),
            json!("number"),
            json!("string"),
            json!("array"),
            json!("object")
        ]
    );
}

#[test]
fn test_builtin_in_pipe() {
    let inputs = vec![json!({"items": [1, 2, 3]})];
    let result = evaluate_all(".items | length", &inputs).unwrap();
    assert_eq!(result, vec![json!(3)]);
}

#[test]
fn test_builtin_iterate_length() {
    let inputs = vec![json!(["ab", "cde", "f"])];
    let result = evaluate_all(".[] | length", &inputs).unwrap();
    assert_eq!(result, vec![json!(2), json!(3), json!(1)]);
}

#[test]
fn test_builtin_sort_reverse() {
    let inputs = vec![json!([3, 1, 2])];
    let result = evaluate_all("sort | reverse", &inputs).unwrap();
    assert_eq!(result, vec![json!([3, 2, 1])]);
}

#[test]
fn test_builtin_keys_iterate() {
    let inputs = vec![json!({"c": 3, "a": 1, "b": 2})];
    let result = evaluate_all("keys | .[]", &inputs).unwrap();
    assert_eq!(result, vec![json!("a"), json!("b"), json!("c")]);
}

#[test]
fn test_builtin_first_last() {
    let inputs = vec![json!([1, 2, 3])];
    assert_eq!(evaluate_all("first", &inputs).unwrap(), vec![json!(1)]);
    assert_eq!(evaluate_all("last", &inputs).unwrap(), vec![json!(3)]);
}

#[test]
fn test_builtin_unique() {
    let inputs = vec![json!([1, 2, 1, 3, 2])];
    let result = evaluate_all("unique", &inputs).unwrap();
    assert_eq!(result, vec![json!([1, 2, 3])]);
}

#[test]
fn test_builtin_flatten_add() {
    let inputs = vec![json!([[1, 2], [3, 4]])];
    assert_eq!(
        evaluate_all("flatten", &inputs).unwrap(),
        vec![json!([1, 2, 3, 4])]
    );
    assert_eq!(
        evaluate_all("add", &inputs).unwrap(),
        vec![json!([1, 2, 3, 4])]
    );
}

#[test]
fn test_builtin_add_numbers() {
    let inputs = vec![json!([1, 2, 3])];
    let result = evaluate_all("add", &inputs).unwrap();
    assert_eq!(result, vec![json!(6.0)]);
}

#[test]
fn test_builtin_empty() {
    let inputs = vec![json!(1), json!(2), json!(3)];
    let result = evaluate_all("empty", &inputs).unwrap();
    assert!(result.is_empty());
}

#[test]
fn test_builtin_not() {
    let inputs = vec![json!(null), json!(false), json!(true), json!(0)];
    let result = evaluate_all("not", &inputs).unwrap();
    assert_eq!(
        result,
        vec![json!(true), json!(true), json!(false), json!(false)]
    );
}

#[test]
fn test_builtin_min_max() {
    let inputs = vec![json!([3, 1, 4, 1, 5])];
    assert_eq!(evaluate_all("min", &inputs).unwrap(), vec![json!(1)]);
    assert_eq!(evaluate_all("max", &inputs).unwrap(), vec![json!(5)]);
}

#[test]
fn test_field_named_length() {
    // .length should access a field named "length", not the builtin
    let inputs = vec![json!({"length": 42})];
    let result = evaluate_all(".length", &inputs).unwrap();
    assert_eq!(result, vec![json!(42)]);
}

// Array construction integration tests
#[test]
fn test_array_construction_eval() {
    let inputs = vec![json!({"a": 1, "b": 2})];
    let result = evaluate_all("[.a, .b]", &inputs).unwrap();
    assert_eq!(result, vec![json!([1, 2])]);
}

#[test]
fn test_array_construction_empty() {
    let inputs = vec![json!({"a": 1})];
    let result = evaluate_all("[]", &inputs).unwrap();
    assert_eq!(result, vec![json!([])]);
}

#[test]
fn test_array_collect_iterate() {
    let inputs = vec![json!([1, 2, 3])];
    let result = evaluate_all("[.[]]", &inputs).unwrap();
    assert_eq!(result, vec![json!([1, 2, 3])]);
}

#[test]
fn test_array_with_pipes() {
    let inputs = vec![json!({"items": [1, 2, 3]})];
    let result = evaluate_all("[.items | length]", &inputs).unwrap();
    assert_eq!(result, vec![json!([3])]);
}

#[test]
fn test_array_nested() {
    let inputs = vec![json!({"x": 1, "y": 2})];
    let result = evaluate_all("[[.x], [.y]]", &inputs).unwrap();
    assert_eq!(result, vec![json!([[1], [2]])]);
}

// Object construction integration tests
#[test]
fn test_object_construction_eval() {
    let inputs = vec![json!({"first": "Alice", "last": "Smith"})];
    let result = evaluate_all("{name: .first, surname: .last}", &inputs).unwrap();
    assert_eq!(result, vec![json!({"name": "Alice", "surname": "Smith"})]);
}

#[test]
fn test_object_construction_empty() {
    let inputs = vec![json!({"a": 1})];
    let result = evaluate_all("{}", &inputs).unwrap();
    assert_eq!(result, vec![json!({})]);
}

#[test]
fn test_object_with_pipe() {
    let inputs = vec![json!({"items": [1, 2, 3]})];
    let result = evaluate_all("{count: .items | length}", &inputs).unwrap();
    assert_eq!(result, vec![json!({"count": 3})]);
}

#[test]
fn test_object_dynamic_key_eval() {
    // {(.key): .value} with {"key": "name", "value": "Alice"}
    let inputs = vec![json!({"key": "name", "value": "Alice"})];
    let result = evaluate_all("{(.key): .value}", &inputs).unwrap();
    assert_eq!(result, vec![json!({"name": "Alice"})]);
}

#[test]
fn test_object_dynamic_key_error_non_string() {
    // Dynamic key must be a string
    let inputs = vec![json!({"key": 123, "value": "Alice"})];
    let result = evaluate_all("{(.key): .value}", &inputs);
    assert!(result.is_err());
}

#[test]
fn test_object_with_array_value() {
    let inputs = vec![json!({"x": 1, "y": 2})];
    let result = evaluate_all("{point: [.x, .y]}", &inputs).unwrap();
    assert_eq!(result, vec![json!({"point": [1, 2]})]);
}

#[test]
fn test_array_of_objects() {
    let inputs = vec![json!({"a": 1, "b": 2})];
    let result = evaluate_all("[{x: .a}, {x: .b}]", &inputs).unwrap();
    assert_eq!(result, vec![json!([{"x": 1}, {"x": 2}])]);
}

#[test]
fn test_object_construction_with_iterate() {
    // .[] | {(.key): .val} should work per element
    let inputs = vec![json!([{"key": "a", "val": 1}, {"key": "b", "val": 2}])];
    let result = evaluate_all(".[] | {(.key): .val}", &inputs).unwrap();
    assert_eq!(result, vec![json!({"a": 1}), json!({"b": 2})]);
}

#[test]
fn test_object_quoted_key() {
    let inputs = vec![json!({"foo": "bar"})];
    let result = evaluate_all("{\"my-key\": .foo}", &inputs).unwrap();
    assert_eq!(result, vec![json!({"my-key": "bar"})]);
}
