use std::fmt::{self, Write};
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};

use thiserror::Error;
use vecmap::map::{Entry, VecMap};

use crate::completion::{CompletionItem, CompletionItemKind};
use crate::err::NonMergeableTypes;
use crate::sql::expression::identifier::{Identifier, SimpleIdentifier};
use crate::types::Type;
use crate::write_utils::pad;

#[derive(Debug, Clone, Default, Eq)]
pub struct Struct {
    pub fields: Box<VecMap<SimpleIdentifier, Type>>,
}

/// Custom PartialEq implementation for order-sensitive struct comparison.
///
/// **Why this exists:** VecMap's derived `PartialEq` is ORDER-AGNOSTIC - it checks
/// if all keys exist in both maps with equal values, regardless of field order.
/// This is problematic for Hamelin because:
///
/// 1. **SQL row types are order-sensitive.** Trino's INSERT statements require exact
///    field order matching between source and target row types. Two structs with
///    fields `{a: int, b: int}` and `{b: int, a: int}` produce incompatible SQL.
///
/// 2. **Struct merge logic depends on order-sensitive equality.** The `merge()` method
///    uses `if tv == ov` to decide whether to recursively merge nested types or just
///    clone. With order-agnostic equality, structs with different field orders would
///    be considered equal, causing the merge to skip recursive processing and
///    potentially produce wrong field ordering in the result.
///
/// This implementation compares fields pairwise in iteration order, ensuring that
/// `{a: int, b: int} != {b: int, a: int}`.
impl PartialEq for Struct {
    fn eq(&self, other: &Self) -> bool {
        if self.fields.len() != other.fields.len() {
            return false;
        }
        // Compare fields pairwise in order (not just by key lookup)
        self.fields
            .iter()
            .zip(other.fields.iter())
            .all(|((k1, v1), (k2, v2))| k1 == k2 && v1 == v2)
    }
}

impl Struct {
    pub fn new(fields: impl IntoIterator<Item = (SimpleIdentifier, Type)>) -> Self {
        Self {
            fields: Box::new(fields.into_iter().collect()),
        }
    }

    pub fn lookup(&self, id: &SimpleIdentifier) -> Option<&Type> {
        self.fields.get(id)
    }

    pub fn lookup_nested(&self, id: &SimpleIdentifier) -> Option<&Struct> {
        self.fields.get(id).and_then(|v| match v {
            Type::Struct(s) => Some(s),
            _ => None,
        })
    }

    /// Iterate over all flattened field paths in this struct.
    ///
    /// For nested structs, yields the full path (e.g., `x.y.z`) not just the leaf name.
    /// This is useful for expanding struct fields without going through TypeEnvironment.
    ///
    /// Example: `{a: int, b: {c: string, d: bool}}` yields:
    /// - `(a, int)`
    /// - `(b.c, string)`
    /// - `(b.d, bool)`
    pub fn iter_flattened(&self) -> impl Iterator<Item = (Identifier, &Type)> {
        let mut results = Vec::new();
        self.collect_flattened(&[], &mut results);
        results.into_iter()
    }

    fn collect_flattened<'a>(
        &'a self,
        prefix: &[SimpleIdentifier],
        results: &mut Vec<(Identifier, &'a Type)>,
    ) {
        for (field_name, field_type) in self.fields.iter() {
            let mut path = prefix.to_vec();
            path.push(field_name.clone());

            match field_type {
                Type::Struct(nested) => {
                    nested.collect_flattened(&path, results);
                }
                other => {
                    // from_simples returns None only for empty vec, which we can't have here
                    let full_id = Identifier::from_simples(path).unwrap();
                    results.push((full_id, other));
                }
            }
        }
    }

    pub fn drop(&self, key: Identifier) -> Result<Self, DropError> {
        match key {
            Identifier::Simple(simple_identifier) => {
                let mut new_fields = self.fields.clone();
                new_fields.remove(&simple_identifier);
                Ok(Self { fields: new_fields })
            }
            Identifier::Compound(compound_identifier) => {
                let first = compound_identifier.first();
                if let Some(v) = self.fields.get(first) {
                    if let Type::Struct(struct_type) = v {
                        Ok(self.with(
                            first.clone().into(),
                            struct_type.drop(compound_identifier.rest())?.into(),
                        ))
                    } else {
                        return Err(DropError::NonStruct);
                    }
                } else {
                    return Err(DropError::NestedNotFound);
                }
            }
        }
    }

    pub fn replace(&self, key: Identifier, typ: Type) -> Self {
        match key {
            Identifier::Simple(simple_identifier) => {
                let mut fields = VecMap::new();
                for (k, v) in self.fields.iter() {
                    if k == &simple_identifier {
                        fields.insert(k.clone(), typ.clone());
                    } else {
                        fields.insert(k.clone(), v.clone());
                    }
                }
                Self::new(fields)
            }
            Identifier::Compound(compound_identifier) => {
                let first = compound_identifier.first();
                let mut fields = VecMap::new();
                for (k, v) in self.fields.iter() {
                    if k == first {
                        if let Type::Struct(struct_type) = v {
                            fields.insert(
                                k.clone(),
                                Type::Struct(
                                    struct_type.replace(compound_identifier.rest(), typ.clone()),
                                ),
                            );
                        } else {
                            fields.insert(k.clone(), v.clone());
                        }
                    } else {
                        fields.insert(k.clone(), v.clone());
                    }
                }
                Self::new(fields)
            }
        }
    }

    pub fn merge_prepend_overwrite(
        self,
        other: &Struct,
    ) -> std::result::Result<Self, NonMergeableTypes> {
        let mut new_fields = VecMap::new();
        for (k, v) in self.fields.iter() {
            if !other.fields.contains_key(k)
                || v.clone()
                    .merge(other.fields.get(k).unwrap().clone())
                    .is_ok()
            {
                new_fields.insert(k.clone(), v.clone());
            }
        }

        Self::new(new_fields).merge(other)
    }

    pub fn prepend_overwrite(&self, other: &Struct) -> Self {
        let mut new_fields = other.fields.clone();
        for (k, v) in self.fields.iter() {
            if !new_fields.contains_key(k) {
                new_fields.insert(k.clone(), v.clone());
            }
        }
        Self { fields: new_fields }
    }

    pub fn merge(&self, other: &Struct) -> std::result::Result<Self, NonMergeableTypes> {
        let mut new_fields = VecMap::new();
        let mut i = 0;
        let mut j = 0;
        let this_len = self.fields.len();
        let that_len = other.fields.len();

        while i < this_len || j < that_len {
            match (self.fields.get_index(i), other.fields.get_index(j)) {
                // keys are the same, merge types and add to new fields
                (Some((tk, tv)), Some((ok, ov))) if tk == ok => {
                    let merged = if tv == ov {
                        tv.clone()
                    } else {
                        tv.clone()
                            .merge(ov.clone())
                            .map_err(|e| e.prepend_at(tk.clone().into()))?
                    };
                    new_fields.insert(tk.clone(), merged);
                    i += 1;
                    j += 1;
                }

                // both keys are present in the other struct, but at different
                // positions. add the key from the self struct because it comes
                // first alphabetically
                (Some((tk, tv)), Some((ok, _)))
                    if self.fields.contains_key(ok)
                        && other.fields.contains_key(tk)
                        && tk.name < ok.name =>
                {
                    let other_value = other
                        .fields
                        .get(tk)
                        .expect("key must exist since we just checked");
                    let merged = if tv == other_value {
                        tv.clone()
                    } else {
                        tv.clone()
                            .merge(other_value.clone())
                            .map_err(|e| e.prepend_at(tk.clone().into()))?
                    };
                    new_fields.insert(tk.clone(), merged);
                    i += 1;
                }

                // both keys are present in the other struct, but at different
                // positions. add the key from the other struct because it comes
                // first alphabetically
                (Some((tk, _)), Some((ok, ov)))
                    if self.fields.contains_key(ok) && other.fields.contains_key(tk) =>
                {
                    let this_value = self
                        .fields
                        .get(ok)
                        .expect("key must exist since we just checked");
                    let merged = if ov == this_value {
                        ov.clone()
                    } else {
                        ov.clone().merge(this_value.clone()).map_err(|mut e| {
                            std::mem::swap(&mut e.other, &mut e.base);
                            e.prepend_at(ok.clone().into())
                        })?
                    };
                    new_fields.insert(ok.clone(), merged);
                    j += 1;
                }

                // key from self struct not present in other struct, but key
                // from other struct is present in self struct. add key and type
                // from self struct to new fields
                (Some((tk, tv)), Some((ok, _))) if self.fields.contains_key(ok) => {
                    new_fields.insert(tk.clone(), tv.clone());
                    i += 1;
                }

                // key from other struct not present in self struct, but key
                // from self struct is present in other struct. add key and type
                // from other struct to new fields
                (Some((tk, _)), Some((ok, ov))) if other.fields.contains_key(tk) => {
                    new_fields.insert(ok.clone(), ov.clone());
                    j += 1;
                }

                // neither key is present in the other struct. add key and type
                // from self struct to new fields because it comes first
                // alphabetically
                (Some((tk, tv)), Some((ok, _))) if tk.name < ok.name => {
                    new_fields.insert(tk.clone(), tv.clone());
                    i += 1;
                }

                // neither key is present in the other struct. add key and type
                // from other struct to new fields because it comes first
                // alphabetically
                (Some(_), Some((ok, ov))) => {
                    new_fields.insert(ok.clone(), ov.clone());
                    j += 1;
                }

                // no more keys from other struct. key from self struct is
                // present in other struct so merge the types and add to new
                // fields
                (Some((tk, tv)), None) if other.fields.contains_key(tk) => {
                    let other_value = other
                        .fields
                        .get(tk)
                        .expect("key must exist since we just checked");
                    let merged = if tv == other_value {
                        tv.clone()
                    } else {
                        tv.clone()
                            .merge(other_value.clone())
                            .map_err(|e| e.prepend_at(tk.clone().into()))?
                    };
                    new_fields.insert(tk.clone(), merged);
                    i += 1;
                }

                // no more keys from other struct. add key and type from self
                // struct to new fields
                (Some((tk, tv)), None) => {
                    new_fields.insert(tk.clone(), tv.clone());
                    i += 1;
                }

                // no more keys from self struct. key from other struct is
                // present in self struct so merge the types and add to new
                // fields
                (None, Some((ok, ov))) if self.fields.contains_key(ok) => {
                    let this_value = self
                        .fields
                        .get(ok)
                        .expect("key must exist since we just checked");
                    let merged = if ov == this_value {
                        ov.clone()
                    } else {
                        ov.clone().merge(this_value.clone()).map_err(|mut e| {
                            std::mem::swap(&mut e.other, &mut e.base);
                            e.prepend_at(ok.clone().into())
                        })?
                    };
                    new_fields.insert(ok.clone(), merged);
                    j += 1;
                }

                // no more keys from self struct. add key and type from other
                // struct to new fields
                (None, Some((ok, ov))) => {
                    new_fields.insert(ok.clone(), ov.clone());
                    j += 1;
                }

                // no more keys, break loop
                (None, None) => {
                    break;
                }
            }
        }

        Ok(Self::new(new_fields))
    }

    /// Insert a field into this struct, modifying it in place
    pub fn insert(&mut self, key: Identifier, value: Type) {
        match key {
            Identifier::Simple(simple_identifier) => {
                self.fields.insert(simple_identifier, value);
            }
            Identifier::Compound(compound_identifier) => {
                let first = compound_identifier.first();
                if let Entry::Occupied(mut entry) = self.fields.entry(first.clone()) {
                    if let Type::Struct(struct_type) = entry.get_mut() {
                        struct_type.insert(compound_identifier.rest(), value);
                    }
                } else {
                    let mut nested = Struct::default();
                    nested.insert(compound_identifier.rest(), value);
                    self.fields.insert(first.clone(), Type::Struct(nested));
                }
            }
        }
    }

    pub fn with(&self, key: Identifier, value: Type) -> Self {
        let mut new_struct = self.clone();
        new_struct.insert(key, value);
        new_struct
    }

    pub fn with_str(&self, key: impl Into<String>, value: Type) -> Self {
        let mut new_struct = self.clone();
        new_struct.insert(SimpleIdentifier::new(&key.into()).into(), value);
        new_struct
    }

    pub fn remove_substructure(&self) -> Self {
        Self {
            fields: self
                .fields
                .clone()
                .into_iter()
                .filter(|(_, typ)| !matches!(typ, Type::Struct(_)))
                .collect::<VecMap<_, _>>()
                .into(),
        }
    }

    pub fn fmt_indented(
        &self,
        f: &mut impl Write,
        indentation: usize,
        max_fields: usize,
    ) -> fmt::Result {
        write!(f, "{{")?;
        let fields_length = self.fields.len();
        let num_subfields = self.subfields();
        let display_multiline = num_subfields > 5 || fields_length > max_fields;
        if display_multiline {
            writeln!(f)?;
            pad(f, indentation + 4)?;
        }
        for (i, (k, v)) in self.fields.iter().enumerate() {
            if i >= max_fields {
                write!(f, "...")?;
                break;
            }
            write!(f, "{}: ", k.to_hamelin())?;
            v.fmt_indented(f, indentation + 4, max_fields)?;
            if i != fields_length - 1 {
                write!(f, ",")?;
                if display_multiline {
                    writeln!(f)?;
                    pad(f, indentation + 4)?;
                } else {
                    write!(f, " ")?;
                }
            }
        }

        if display_multiline {
            writeln!(f)?;
            pad(f, indentation)?;
        }
        write!(f, "}}")
    }

    pub fn subfields(&self) -> usize {
        self.fields.iter().map(|(_, v)| v.subfields() + 1).sum()
    }

    pub fn keys(&self) -> impl Iterator<Item = &SimpleIdentifier> {
        self.fields.keys()
    }

    pub fn autocomplete_suggestions(&self, prepend_dot: bool) -> Vec<CompletionItem> {
        self.fields
            .iter()
            .map(|(ident, typ)| {
                let mut item = CompletionItem::new(ident.to_hamelin())
                    .with_detail(typ.short_name())
                    .with_section("Fields".to_string())
                    .with_kind(CompletionItemKind::Variable);
                if typ.to_string() != typ.short_name() {
                    item = item.with_description(typ.to_string())
                }
                if prepend_dot {
                    item = item.with_apply_string(format!(".{}", ident.to_hamelin()))
                }
                item
            })
            .collect()
    }

    pub fn nested_autocomplete_suggestions(
        &self,
        ident: &SimpleIdentifier,
        prepend_dot: bool,
    ) -> Vec<CompletionItem> {
        fn traverse<'a>(str: &'a Struct, ident: &SimpleIdentifier) -> Vec<(Identifier, &'a Type)> {
            str.fields
                .iter()
                .flat_map(|(key, value)| {
                    let mut maybe_nest = match value {
                        Type::Struct(s) => traverse(s, ident)
                            .into_iter()
                            .map(|(nk, nv)| (Identifier::from(nk.prepend(key.clone().into())), nv))
                            .collect(),

                        Type::Array(a) => match a.element_type.as_ref() {
                            Type::Struct(s) => traverse(s, ident)
                                .into_iter()
                                .map(|(nk, nv)| {
                                    (Identifier::from(nk.prepend(key.clone().into())), nv)
                                })
                                .collect(),
                            _ => vec![],
                        },

                        _ => vec![],
                    };

                    if key.name.contains(&ident.name) {
                        maybe_nest.push((key.clone().into(), value));
                    }

                    maybe_nest
                })
                .collect()
        }

        traverse(self, ident)
            .into_iter()
            .map(|(ident, typ)| {
                let mut item = CompletionItem::new(ident.to_hamelin())
                    .with_detail(typ.short_name())
                    .with_section("Fields".to_string())
                    .with_kind(CompletionItemKind::Variable);
                if typ.to_string() != typ.short_name() {
                    item = item.with_description(typ.to_string())
                }
                if prepend_dot {
                    item = item.with_apply_string(format!(".{}", ident.to_hamelin()))
                }
                item
            })
            .collect()
    }
}

impl Display for Struct {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        self.fmt_indented(f, 0, usize::MAX)
    }
}

impl Hash for Struct {
    fn hash<H: Hasher>(&self, state: &mut H) {
        for (k, v) in self.fields.iter() {
            k.hash(state);
            v.hash(state);
        }
    }
}

#[derive(Debug, Error)]
pub enum DropError {
    #[error("Cannot drop field from non-struct types.")]
    NonStruct,
    #[error("Nested value not found.")]
    NestedNotFound,
}

#[cfg(test)]
mod tests {
    use crate::types::{array::Array, INT, STRING};

    use super::*;

    use rstest::rstest;

    #[rstest]
    #[case::two_identical_structs(
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT)
    )]
    #[case::structs_with_same_fields_in_different_order(
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT)
            .with(SimpleIdentifier::new("field2").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field2").into(), INT)
            .with(SimpleIdentifier::new("field1").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT)
            .with(SimpleIdentifier::new("field2").into(), INT)
    )]
    #[case::one_field_in_middle(
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT)
            .with(SimpleIdentifier::new("field3").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT)
            .with(SimpleIdentifier::new("field2").into(), INT)
            .with(SimpleIdentifier::new("field3").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT)
            .with(SimpleIdentifier::new("field2").into(), INT)
            .with(SimpleIdentifier::new("field3").into(), INT)
    )]
    #[case::one_field_at_beginning(
        Struct::default()
            .with(SimpleIdentifier::new("field2").into(), INT)
            .with(SimpleIdentifier::new("field3").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT)
            .with(SimpleIdentifier::new("field2").into(), INT)
            .with(SimpleIdentifier::new("field3").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT)
            .with(SimpleIdentifier::new("field2").into(), INT)
            .with(SimpleIdentifier::new("field3").into(), INT)
    )]
    fn test_struct_merge(
        #[case] struct1: Struct,
        #[case] struct2: Struct,
        #[case] expected: Struct,
    ) {
        let merged = struct1.merge(&struct2).unwrap();
        assert_eq!(merged, expected);
    }

    #[rstest]
    #[case::nested_structs_with_same_fields(
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child1").into(), INT)
                    .with(SimpleIdentifier::new("child2").into(), STRING)
                    .into()
            ),
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child1").into(), INT)
                    .with(SimpleIdentifier::new("child2").into(), STRING)
                    .into()
            ),
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child1").into(), INT)
                    .with(SimpleIdentifier::new("child2").into(), STRING)
                    .into()
            )
    )]
    #[case::nested_structs_with_additional_fields(
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child1").into(), INT)
                    .into()
            ),
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child1").into(), INT)
                    .with(SimpleIdentifier::new("child2").into(), STRING)
                    .into()
            ),
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child1").into(), INT)
                    .with(SimpleIdentifier::new("child2").into(), STRING)
                    .into()
            )
    )]
    fn test_nested_struct_merge(
        #[case] struct1: Struct,
        #[case] struct2: Struct,
        #[case] expected: Struct,
    ) {
        let merged = struct1.merge(&struct2).unwrap();
        assert_eq!(merged, expected);
    }

    #[rstest]
    #[case::fields_with_same_name_different_types(
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), STRING),
        NonMergeableTypes::new(INT, STRING).prepend_at(SimpleIdentifier::new("field1").into())
    )]
    #[case::two_fields_different_order_with_incompatible_type(
        Struct::default()
            .with(SimpleIdentifier::new("field1").into(), INT)
            .with(SimpleIdentifier::new("field2").into(), INT),
        Struct::default()
            .with(SimpleIdentifier::new("field2").into(), STRING)
            .with(SimpleIdentifier::new("field1").into(), INT),
        NonMergeableTypes::new(INT, STRING).prepend_at(SimpleIdentifier::new("field2").into())
    )]
    fn test_struct_merge_errors(
        #[case] struct1: Struct,
        #[case] struct2: Struct,
        #[case] expected: NonMergeableTypes,
    ) {
        match struct1.merge(&struct2) {
            Ok(_) => panic!("Expected error"),
            Err(e) => assert_eq!(expected, e),
        }
    }

    /// Test that Struct equality is order-sensitive (same keys/values but different order are not equal)
    #[test]
    fn test_struct_equality_is_order_sensitive() {
        let s1 = Struct::default()
            .with(SimpleIdentifier::new("a").into(), INT)
            .with(SimpleIdentifier::new("b").into(), INT);

        let s2 = Struct::default()
            .with(SimpleIdentifier::new("b").into(), INT)
            .with(SimpleIdentifier::new("a").into(), INT);

        // Same keys and values but different order - should NOT be equal
        assert_ne!(
            s1, s2,
            "Structs with same fields but different order should not be equal"
        );

        // Same order should be equal
        let s3 = Struct::default()
            .with(SimpleIdentifier::new("a").into(), INT)
            .with(SimpleIdentifier::new("b").into(), INT);
        assert_eq!(s1, s3);
    }

    #[rstest]
    #[case::nested_structs_with_incompatible_types(
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child").into(), INT)
                    .into()
            ),
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child").into(), STRING)
                    .into()
            ),
        NonMergeableTypes::new(INT, STRING)
            .prepend_at("parent.child".parse().unwrap())
    )]
    #[case::nested_structs_with_multiple_fields_incompatible_types(
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child1").into(), INT)
                    .with(SimpleIdentifier::new("child2").into(), INT)
                    .into()
            ),
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child2").into(), STRING)
                    .with(SimpleIdentifier::new("child1").into(), INT)
                    .into()
            ),
        NonMergeableTypes::new(INT, STRING)
            .prepend_at("parent.child2".parse().unwrap())
    )]
    #[case::three_level_nested_structs_with_incompatible_types(
            Struct::default()
                .with(SimpleIdentifier::new("top").into(),
                    Struct::default()
                        .with(SimpleIdentifier::new("middle").into(),
                            Struct::default()
                                .with(SimpleIdentifier::new("bottom").into(), INT)
                                .into()
                        )
                        .into()
                ),
            Struct::default()
                .with(SimpleIdentifier::new("top").into(),
                    Struct::default()
                        .with(SimpleIdentifier::new("middle").into(),
                            Struct::default()
                                .with(SimpleIdentifier::new("bottom").into(), STRING)
                                .into()
                        )
                        .into()
                ),
            NonMergeableTypes::new(INT, STRING)
                .prepend_at("top.middle.bottom".parse().unwrap())
        )]
    #[case::non_alphabetical_fields_with_incompatible_types(
        Struct::default()
            .with(SimpleIdentifier::new("zprop1").into(), INT)
            .with(SimpleIdentifier::new("zprop2").into(), INT)
            .with(SimpleIdentifier::new("alpha").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child1").into(), INT)
                    .with(SimpleIdentifier::new("child2").into(), INT)
                    .into()
            ),
        Struct::default()
            .with(SimpleIdentifier::new("alpha").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("child1").into(), STRING)
                    .with(SimpleIdentifier::new("child2").into(), INT)
                    .into()
            )
            .with(SimpleIdentifier::new("zprop1").into(), INT),
        NonMergeableTypes::new(INT, STRING)
            .prepend_at("alpha.child1".parse().unwrap())
    )]
    #[case::failing_key_in_nested_struct_end(
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("field1").into(), INT)
                    .with(SimpleIdentifier::new("field2").into(), INT)
                    .with(SimpleIdentifier::new("field3").into(), INT)
                    .with(SimpleIdentifier::new("field4").into(), INT)
                    .with(SimpleIdentifier::new("field5").into(), INT)
                    .into()
            ),
        Struct::default()
            .with(SimpleIdentifier::new("parent").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("field1").into(), INT)
                    .with(SimpleIdentifier::new("field5").into(), STRING)
                    .into()
            ),
        NonMergeableTypes::new(INT, STRING)
            .prepend_at("parent.field5".parse().unwrap())
    )]

    fn test_nested_struct_merge_errors(
        #[case] struct1: Struct,
        #[case] struct2: Struct,
        #[case] expected: NonMergeableTypes,
    ) {
        match struct1.merge(&struct2) {
            Ok(_) => panic!("Expected error"),
            Err(e) => assert_eq!(expected, e),
        }
    }

    #[rstest]
    #[case::nested_struct_x(
        Struct::default()
            .with(SimpleIdentifier::new("a").into(), INT)
            .with(
                SimpleIdentifier::new("b").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("x").into(), INT)
                    .with(
                        SimpleIdentifier::new("y").into(),
                        Struct::default()
                            .with(SimpleIdentifier::new("z").into(), INT)
                            .into(),
                    )
                    .into(),
            )
            .with(SimpleIdentifier::new("c").into(), INT),
        SimpleIdentifier::new("x"),
        vec![
            CompletionItem::new("b.x".to_string())
                .with_detail("int".to_string())
                .with_kind(CompletionItemKind::Variable)
                .with_section("Fields".to_string())
        ]
    )]
    #[case::nested_struct_z(
        Struct::default()
            .with(SimpleIdentifier::new("a").into(), INT)
            .with(
                SimpleIdentifier::new("b").into(),
                Struct::default()
                    .with(SimpleIdentifier::new("x").into(), INT)
                    .with(
                        SimpleIdentifier::new("y").into(),
                        Struct::default()
                            .with(SimpleIdentifier::new("z").into(), INT)
                            .into(),
                    )
                    .into(),
            )
            .with(SimpleIdentifier::new("c").into(), INT),
        SimpleIdentifier::new("z"),
        vec![
            CompletionItem::new("b.y.z".to_string())
                .with_detail("int".to_string())
                .with_kind(CompletionItemKind::Variable)
                .with_section("Fields".to_string())
        ]
    )]
    #[case::flat_struct_foo(
        Struct::default()
            .with(SimpleIdentifier::new("foo").into(), INT)
            .with(SimpleIdentifier::new("bar").into(), STRING),
        SimpleIdentifier::new("foo"),
        vec![
            CompletionItem::new("foo".to_string())
                .with_detail("int".to_string())
                .with_kind(CompletionItemKind::Variable)
                .with_section("Fields".to_string())
        ]
    )]
    #[case::flat_struct_bar(
        Struct::default()
            .with(SimpleIdentifier::new("foo").into(), INT)
            .with(SimpleIdentifier::new("bar").into(), STRING),
        SimpleIdentifier::new("bar"),
        vec![
            CompletionItem::new("bar".to_string())
                .with_detail("string".to_string())
                .with_kind(CompletionItemKind::Variable)
                .with_section("Fields".to_string())
        ]
    )]
    #[case::array_struct_foo(
        Struct::default()
            .with(
                SimpleIdentifier::new("arr").into(),
                Array::new(
                    Struct::default()
                        .with(SimpleIdentifier::new("foo").into(), INT)
                        .with(SimpleIdentifier::new("bar").into(), INT)
                        .into(),
                )
                .into(),
            )
            .with(SimpleIdentifier::new("other").into(), INT),
        SimpleIdentifier::new("foo"),
        vec![
            CompletionItem::new("arr.foo".to_string())
                .with_detail("int".to_string())
                .with_kind(CompletionItemKind::Variable)
                .with_section("Fields".to_string())
        ]
    )]
    #[case::array_struct_bar(
        Struct::default()
            .with(
                SimpleIdentifier::new("arr").into(),
                Array::new(
                    Struct::default()
                        .with(SimpleIdentifier::new("foo").into(), INT)
                        .with(SimpleIdentifier::new("bar").into(), INT)
                        .into(),
                )
                .into(),
            )
            .with(SimpleIdentifier::new("other").into(), INT),
        SimpleIdentifier::new("bar"),
        vec![
            CompletionItem::new("arr.bar".to_string())
                .with_detail("int".to_string())
                .with_kind(CompletionItemKind::Variable)
                .with_section("Fields".to_string())
        ]
    )]
    fn test_autocomplete_suggestions(
        #[case] s: Struct,
        #[case] ident: SimpleIdentifier,
        #[case] expected: Vec<CompletionItem>,
    ) {
        let mut items = s.nested_autocomplete_suggestions(&ident, false);
        items.sort_by(|a, b| a.label.cmp(&b.label));
        let mut expected_sorted = expected;
        expected_sorted.sort_by(|a, b| a.label.cmp(&b.label));
        assert_eq!(items, expected_sorted);
    }
}
