use crate::func::utils::must_be_same_type;
use crate::types::array::Array;
use crate::types::map::Map;
use crate::types::struct_type::Struct;
use crate::types::tuple::Tuple;
use crate::types::{Type, BOOLEAN, DOUBLE, INT, STRING, TIMESTAMP, UNKNOWN};
use ordermap::OrderMap;
use rstest::rstest;

#[rstest]
#[case::single_int(vec![&INT], INT)]
#[case::single_string(vec![&STRING], STRING)]
#[case::single_unknown(vec![&UNKNOWN], UNKNOWN)]
#[case::identical_ints(vec![&INT, &INT, &INT], INT)]
#[case::identical_strings(vec![&STRING, &STRING], STRING)]
#[case::identical_booleans(vec![&BOOLEAN, &BOOLEAN, &BOOLEAN], BOOLEAN)]
#[case::unknown_first(vec![&UNKNOWN, &INT], INT)]
#[case::unknown_second(vec![&INT, &UNKNOWN], INT)]
#[case::unknown_middle(vec![&STRING, &UNKNOWN, &STRING], STRING)]
#[case::multiple_unknowns(vec![&UNKNOWN, &DOUBLE, &UNKNOWN], DOUBLE)]
#[case::all_unknowns(vec![&UNKNOWN, &UNKNOWN, &UNKNOWN], UNKNOWN)]
fn test_must_be_same_type_success(#[case] types: Vec<&Type>, #[case] expected: Type) {
    let result = must_be_same_type(types.into_iter()).unwrap();
    assert_eq!(result, expected);
}

#[rstest]
#[case::int_vs_string(vec![&INT, &STRING])]
#[case::boolean_vs_timestamp(vec![&BOOLEAN, &TIMESTAMP])]
#[case::double_vs_string(vec![&DOUBLE, &STRING])]
fn test_must_be_same_type_incompatible_primitives(#[case] types: Vec<&Type>) {
    let result = must_be_same_type(types.into_iter());
    assert!(result.is_err());
}

#[rstest]
#[case::int_with_unknown(INT, UNKNOWN, INT)]
#[case::unknown_with_int(UNKNOWN, INT, INT)]
#[case::string_identical(STRING, STRING, STRING)]
#[case::int_identical(INT, INT, INT)]
#[case::double_with_unknown(DOUBLE, UNKNOWN, DOUBLE)]
fn test_must_be_same_type_arrays_two_elements(
    #[case] elem1: Type,
    #[case] elem2: Type,
    #[case] expected_elem: Type,
) {
    let arr1 = Array::new(elem1).into();
    let arr2 = Array::new(elem2).into();
    let expected = Array::new(expected_elem).into();

    let result = must_be_same_type(vec![&arr1, &arr2].into_iter()).unwrap();
    assert_eq!(result, expected);
}

#[rstest]
#[case::int_vs_string(Array::new(INT).into(), Array::new(STRING).into())]
#[case::boolean_vs_double(Array::new(BOOLEAN).into(), Array::new(DOUBLE).into())]
#[case::string_vs_timestamp(Array::new(STRING).into(), Array::new(TIMESTAMP).into())]
fn test_must_be_same_type_incompatible_arrays(#[case] arr1: Type, #[case] arr2: Type) {
    let result = must_be_same_type(vec![&arr1, &arr2].into_iter());
    assert!(result.is_err());
}

#[rstest]
#[case::multiple_with_unknown(
    vec![Array::new(INT).into(), Array::new(INT).into(), Array::new(UNKNOWN).into()],
    Array::new(INT).into()
)]
#[case::nested_arrays(
    vec![Array::new(Array::new(INT).into()).into(), Array::new(Array::new(UNKNOWN).into()).into()],
    Array::new(Array::new(INT).into()).into()
)]
fn test_must_be_same_type_array_scenarios(#[case] types: Vec<Type>, #[case] expected: Type) {
    let type_refs: Vec<&Type> = types.iter().collect();
    let result = must_be_same_type(type_refs.into_iter()).unwrap();
    assert_eq!(result, expected);
}

#[rstest]
#[case::with_unknown(
    vec![Map::new(INT, STRING).into(), Map::new(INT, UNKNOWN).into()],
    Map::new(INT, STRING).into()
)]
#[case::identical(
    vec![Map::new(INT, STRING).into(), Map::new(INT, STRING).into()],
    Map::new(INT, STRING).into()
)]
fn test_must_be_same_type_maps_success(#[case] types: Vec<Type>, #[case] expected: Type) {
    let type_refs: Vec<&Type> = types.iter().collect();
    let result = must_be_same_type(type_refs.into_iter()).unwrap();
    assert_eq!(result, expected);
}

#[rstest]
fn test_must_be_same_type_maps_incompatible_keys() {
    let map_int_string = Map::new(INT, STRING).into();
    let map_string_int = Map::new(STRING, INT).into();

    // map(int, string) + map(string, int) should fail (key types don't match)
    let result = must_be_same_type(vec![&map_int_string, &map_string_int].into_iter());
    assert!(result.is_err());
}

#[rstest]
#[case::unknown_second_element(
    vec![Tuple::new(vec![INT, STRING]).into(), Tuple::new(vec![UNKNOWN, STRING]).into()],
    Tuple::new(vec![INT, STRING]).into()
)]
#[case::unknown_first_element(
    vec![Tuple::new(vec![INT, UNKNOWN]).into(), Tuple::new(vec![INT, STRING]).into()],
    Tuple::new(vec![INT, STRING]).into()
)]
fn test_must_be_same_type_tuples_success(#[case] types: Vec<Type>, #[case] expected: Type) {
    let type_refs: Vec<&Type> = types.iter().collect();
    let result = must_be_same_type(type_refs.into_iter()).unwrap();
    assert_eq!(result, expected);
}

#[rstest]
fn test_must_be_same_type_structs_incompatible() {
    // Create struct {a: int, b: string}
    let mut fields1 = OrderMap::new();
    fields1.insert("a".parse().unwrap(), INT);
    fields1.insert("b".parse().unwrap(), STRING);
    let struct1 = Struct::new(fields1).into();

    // Create struct {a: unknown, b: string}
    let mut fields2 = OrderMap::new();
    fields2.insert("a".parse().unwrap(), UNKNOWN);
    fields2.insert("b".parse().unwrap(), STRING);
    let struct2 = Struct::new(fields2).into();

    // struct {a: int, b: string} + struct {a: unknown, b: string} should fail - no struct merging
    let result = must_be_same_type(vec![&struct1, &struct2].into_iter());
    assert!(result.is_err());
}

#[rstest]
fn test_must_be_same_type_struct_different_fields_incompatible() {
    // Create struct {a: int}
    let mut fields1 = OrderMap::new();
    fields1.insert("a".parse().unwrap(), INT);
    let struct1 = Struct::new(fields1).into();

    // Create struct {b: string}
    let mut fields2 = OrderMap::new();
    fields2.insert("b".parse().unwrap(), STRING);
    let struct2 = Struct::new(fields2).into();

    // struct {a: int} + struct {b: string} should fail - no struct merging allowed
    let result = must_be_same_type(vec![&struct1, &struct2].into_iter());
    assert!(result.is_err());
}

#[rstest]
fn test_must_be_same_type_struct_identical() {
    // Create identical struct {a: int, b: string}
    let mut fields1 = OrderMap::new();
    fields1.insert("a".parse().unwrap(), INT);
    fields1.insert("b".parse().unwrap(), STRING);
    let struct1 = Struct::new(fields1).into();

    let mut fields2 = OrderMap::new();
    fields2.insert("a".parse().unwrap(), INT);
    fields2.insert("b".parse().unwrap(), STRING);
    let struct2 = Struct::new(fields2).into();

    // Identical structs should work
    let result = must_be_same_type(vec![&struct1, &struct2].into_iter()).unwrap();
    assert_eq!(result, struct1);
}

#[rstest]
#[case::array_vs_map(vec![Array::new(INT).into(), Map::new(INT, STRING).into()])]
#[case::primitive_vs_array(vec![INT, Array::new(INT).into()])]
fn test_must_be_same_type_mixed_incompatible(#[case] types: Vec<Type>) {
    let type_refs: Vec<&Type> = types.iter().collect();
    let result = must_be_same_type(type_refs.into_iter());
    assert!(result.is_err());
}

#[rstest]
fn test_must_be_same_type_empty_iterator() {
    let empty_vec: Vec<&Type> = vec![];
    let result = must_be_same_type(empty_vec.into_iter()).unwrap();
    assert_eq!(result, UNKNOWN);
}

#[rstest]
fn test_must_be_same_type_complex_nested_scenario() {
    // Test a complex scenario with multiple levels of nesting and unknowns
    let arr_map_int_unknown = Array::new(Map::new(INT, UNKNOWN).into()).into();
    let arr_map_int_string = Array::new(Map::new(INT, STRING).into()).into();
    let expected = Array::new(Map::new(INT, STRING).into()).into();

    // array(map(int, unknown)) + array(map(int, string)) → array(map(int, string))
    let result =
        must_be_same_type(vec![&arr_map_int_unknown, &arr_map_int_string].into_iter()).unwrap();
    assert_eq!(result, expected);
}
