use crate::eval::evaluator::eval;
use crate::value::Value;
use hamelin_lib::tree::ast::identifier::SimpleIdentifier;
use hamelin_lib::tree::builder::{
    array, field, index, int, string, struct_literal, ExpressionBuilder,
};
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::type_check_expression;
use ordermap::OrderMap;
use std::sync::Arc;

use super::test_helpers::test_context;

#[test]
fn test_eval_complex_nested_access() {
    let ctx = test_context();

    // Create complex nested structure: struct with array of structs
    let expr = index(
        field(
            struct_literal().field(
                "items",
                array()
                    .element(
                        struct_literal()
                            .field("id", int(1))
                            .field("value", string("first")),
                    )
                    .element(
                        struct_literal()
                            .field("id", int(2))
                            .field("value", string("second")),
                    )
                    .element(
                        struct_literal()
                            .field("id", int(3))
                            .field("value", string("third")),
                    ),
            ),
            "items",
        ),
        int(1),
    );

    let result = ctx.eval_expr(&expr);

    let mut expected_fields = OrderMap::new();
    expected_fields.insert("id".parse::<SimpleIdentifier>().unwrap(), Value::Int(2));
    expected_fields.insert(
        "value".parse::<SimpleIdentifier>().unwrap(),
        Value::String("second".to_string()),
    );

    assert_eq!(result, Value::Struct(expected_fields));

    // Now access a field of that indexed struct
    let expr = field(
        index(
            field(
                struct_literal().field(
                    "items",
                    array()
                        .element(
                            struct_literal()
                                .field("id", int(1))
                                .field("value", string("first")),
                        )
                        .element(
                            struct_literal()
                                .field("id", int(2))
                                .field("value", string("second")),
                        )
                        .element(
                            struct_literal()
                                .field("id", int(3))
                                .field("value", string("third")),
                        ),
                ),
                "items",
            ),
            int(2), // Access third element (0-indexed)
        ),
        "value",
    );

    let result = ctx.eval_expr(&expr);
    assert_eq!(result, Value::String("third".to_string()));
}

#[test]
fn test_eval_array_of_arrays_access() {
    let ctx = test_context();

    // Create array of arrays
    let expr = index(
        index(
            array()
                .element(array().element(int(1)).element(int(2)))
                .element(array().element(int(3)).element(int(4)))
                .element(array().element(int(5)).element(int(6))),
            int(1), // Second array [3, 4]
        ),
        int(0), // First element of that array (3)
    );

    let result = ctx.eval_expr(&expr);
    assert_eq!(result, Value::Int(3));
}

#[test]
fn test_eval_struct_containing_nested_arrays() {
    let ctx = test_context();

    // Create a struct with nested arrays and access deeply
    let expr = index(
        index(
            field(
                struct_literal().field(
                    "matrix",
                    array()
                        .element(array().element(int(1)).element(int(2)).element(int(3)))
                        .element(array().element(int(4)).element(int(5)).element(int(6)))
                        .element(array().element(int(7)).element(int(8)).element(int(9))),
                ),
                "matrix",
            ),
            int(2), // Third row [7, 8, 9]
        ),
        int(1), // Second element (8)
    );

    let result = ctx.eval_expr(&expr);
    assert_eq!(result, Value::Int(8));
}

#[test]
fn test_eval_array_of_structs_with_arrays() {
    let ctx = test_context();

    // Create array of structs where each struct contains an array
    let expr = index(
        field(
            index(
                array()
                    .element(
                        struct_literal()
                            .field("name", string("group1"))
                            .field("values", array().element(int(10)).element(int(20))),
                    )
                    .element(
                        struct_literal()
                            .field("name", string("group2"))
                            .field("values", array().element(int(30)).element(int(40))),
                    ),
                int(1), // Second group
            ),
            "values", // Get the values array
        ),
        int(0), // First value in that array
    );

    let result = ctx.eval_expr(&expr);
    assert_eq!(result, Value::Int(30));
}

#[test]
fn test_eval_deeply_nested_struct_access() {
    let ctx = test_context();

    // Create deeply nested struct and access the deepest value
    let expr = field(
        field(
            field(
                field(
                    struct_literal().field(
                        "level1",
                        struct_literal().field(
                            "level2",
                            struct_literal().field(
                                "level3",
                                struct_literal()
                                    .field("level4", struct_literal().field("value", int(42))),
                            ),
                        ),
                    ),
                    "level1",
                ),
                "level2",
            ),
            "level3",
        ),
        "level4",
    );

    let result = ctx.eval_expr(&field(expr, "value"));
    assert_eq!(result, Value::Int(42));
}

#[test]
fn test_eval_mixed_access_patterns() {
    use hamelin_lib::tree::builder::boolean;

    let ctx = test_context();

    // Create a complex structure with mixed access patterns
    // struct.array[index].struct.field
    let expr = field(
        index(
            field(
                struct_literal().field(
                    "data",
                    array()
                        .element(
                            struct_literal()
                                .field("active", boolean(false))
                                .field("info", struct_literal().field("code", int(100))),
                        )
                        .element(
                            struct_literal()
                                .field("active", boolean(true))
                                .field("info", struct_literal().field("code", int(200))),
                        ),
                ),
                "data",
            ),
            int(1), // Second element
        ),
        "info",
    );

    let result = ctx.eval_expr(&field(expr, "code"));
    assert_eq!(result, Value::Int(200));
}

#[test]
fn test_eval_chained_field_access_with_variables() {
    use hamelin_lib::tree::builder::field_ref;
    use hamelin_lib::types::{struct_type::Struct, Type, BOOLEAN, STRING};

    use super::test_helpers::TestContext;

    // Create complex nested structure value
    let mut settings_fields = OrderMap::new();
    settings_fields.insert(
        "debug".parse::<SimpleIdentifier>().unwrap(),
        Value::Boolean(true),
    );

    let mut config_fields = OrderMap::new();
    config_fields.insert(
        "settings".parse::<SimpleIdentifier>().unwrap(),
        Value::Struct(settings_fields),
    );

    // Create context with nested struct types
    let settings_struct = Struct::default().with_str("debug", BOOLEAN);
    let config_struct = Struct::default().with_str("settings", Type::Struct(settings_struct));

    let mut ctx = TestContext::default();
    ctx.set(
        "config",
        Value::Struct(config_fields),
        Type::Struct(config_struct),
    );
    ctx.set("name", Value::String("test_config".to_string()), STRING);

    // Access nested field through variable reference
    let expr = type_check_expression(
        field(field(field_ref("config"), "settings"), "debug").build(),
        ExpressionTypeCheckOptions::builder()
            .bindings(Arc::new(ctx.translation_env.clone()))
            .build(),
    )
    .output;
    let result = eval(&expr, &ctx.env).unwrap();

    assert_eq!(result, Value::Boolean(true));
}

#[test]
fn test_eval_array_slice_simulation() {
    let ctx = test_context();

    // Simulate array slicing by accessing multiple indices
    let base_array = || {
        array()
            .element(int(10))
            .element(int(20))
            .element(int(30))
            .element(int(40))
            .element(int(50))
    };

    // Access elements 1, 2, 3
    let elem1 = ctx.eval_expr(&index(base_array(), int(1)));
    let elem2 = ctx.eval_expr(&index(base_array(), int(2)));
    let elem3 = ctx.eval_expr(&index(base_array(), int(3)));

    assert_eq!(elem1, Value::Int(20));
    assert_eq!(elem2, Value::Int(30));
    assert_eq!(elem3, Value::Int(40));

    // Verify we can access the elements correctly
    // (We can't easily create a new array from Values, so just verify the access works)
    let expected_values = vec![Value::Int(20), Value::Int(30), Value::Int(40)];
    let actual_values = vec![elem1, elem2, elem3];
    assert_eq!(actual_values, expected_values);
}

#[test]
fn test_eval_complex_navigation_with_error_handling() {
    let ctx = test_context();

    // Create valid complex structure
    let valid_expr = field(
        index(
            field(
                struct_literal().field(
                    "users",
                    array().element(struct_literal().field("id", int(1))),
                ),
                "users",
            ),
            int(0),
        ),
        "id",
    );

    let result = ctx.eval_expr(&valid_expr);
    assert_eq!(result, Value::Int(1));

    // Test with out of bounds access
    let invalid_expr = field(
        index(
            field(
                struct_literal().field(
                    "users",
                    array().element(struct_literal().field("id", int(1))),
                ),
                "users",
            ),
            int(10), // Out of bounds
        ),
        "id",
    );

    let result = ctx.try_eval_expr(&invalid_expr);
    assert!(result.is_err());
}
