use super::super::*;

pub(in super::super) const MAX_ARRAY_RECURSION_DEPTH: usize = 64;

pub(in super::super) fn array_element_matches(
    left: &StoredValue,
    right: &StoredValue,
) -> Result<bool> {
    if left.is_null() && right.is_null() {
        return Ok(true);
    }
    Ok(matches!(values_equal(left, right)?, Some(true)))
}

fn array_recursion_depth_error() -> Error {
    Error::Other("array value is too deep".to_owned())
}

pub(in super::super) fn array_values_equal(
    left: &[StoredValue],
    right: &[StoredValue],
) -> Result<bool> {
    array_values_equal_inner(left, right, 0)
}

fn array_values_equal_inner(
    left: &[StoredValue],
    right: &[StoredValue],
    depth: usize,
) -> Result<bool> {
    if depth >= MAX_ARRAY_RECURSION_DEPTH {
        return Err(array_recursion_depth_error());
    }
    if left.len() != right.len() {
        return Ok(false);
    }

    for (left, right) in left.iter().zip(right.iter()) {
        let equal = match (left, right) {
            (StoredValue::Array(left), StoredValue::Array(right)) => {
                array_values_equal_inner(left, right, depth + 1)?
            }
            _ => array_element_matches(left, right)?,
        };
        if !equal {
            return Ok(false);
        }
    }

    Ok(true)
}

fn function_not_found(function_name: &str, arg_types: impl Into<String>) -> Error {
    Error::Other(format!(
        "function {}({}) does not exist",
        function_name,
        arg_types.into()
    ))
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct ArrayShape {
    dims: Vec<usize>,
}

impl ArrayShape {
    fn ndims(&self) -> usize {
        self.dims.len()
    }

    fn is_empty(&self) -> bool {
        self.dims.is_empty()
    }
}

fn array_shape(values: &[StoredValue]) -> Option<ArrayShape> {
    array_shape_inner(values, false, 0).map(|dims| ArrayShape { dims })
}

fn array_shape_inner(values: &[StoredValue], nested: bool, depth: usize) -> Option<Vec<usize>> {
    if depth >= MAX_ARRAY_RECURSION_DEPTH {
        return None;
    }
    if values.is_empty() {
        return Some(if nested { vec![0] } else { vec![] });
    }

    let mut saw_array = false;
    let mut saw_scalar = false;
    let mut child_shape: Option<Vec<usize>> = None;

    for value in values {
        match value {
            StoredValue::Array(nested_values) => {
                if saw_scalar {
                    return None;
                }
                saw_array = true;
                let nested_shape = array_shape_inner(nested_values, true, depth + 1)?;
                match child_shape.as_ref() {
                    Some(expected) if *expected != nested_shape => return None,
                    None => child_shape = Some(nested_shape),
                    _ => {}
                }
            }
            StoredValue::Null => {}
            _ => {
                if saw_array {
                    return None;
                }
                saw_scalar = true;
            }
        }
    }

    if saw_array {
        let child_shape = child_shape?;
        let mut dims = Vec::with_capacity(1 + child_shape.len());
        dims.push(values.len());
        dims.extend(child_shape);
        Some(dims)
    } else {
        Some(vec![values.len()])
    }
}

pub(in super::super) fn array_dimension_arg(value: Option<StoredValue>) -> Option<i32> {
    match value {
        Some(StoredValue::Int32(value)) => Some(value),
        Some(StoredValue::Int64(value)) => i32::try_from(value).ok(),
        None => Some(1),
        _ => None,
    }
}

pub(in super::super) fn eval_make_array_function(args: Vec<StoredValue>) -> Result<StoredValue> {
    Ok(StoredValue::Array(args))
}

fn array_length_at_dimension(values: &[StoredValue], dim: i32, depth: usize) -> Option<i32> {
    if depth >= MAX_ARRAY_RECURSION_DEPTH {
        return None;
    }
    if dim <= 0 {
        return None;
    }
    if values.is_empty() {
        return None;
    }
    if dim == 1 {
        return i32::try_from(values.len()).ok();
    }

    let mut expected = None;
    let mut saw_nested = false;
    for value in values {
        match value {
            StoredValue::Array(nested) => {
                saw_nested = true;
                let length = array_length_at_dimension(nested, dim - 1, depth + 1)?;
                if expected.is_some_and(|previous| previous != length) {
                    return None;
                }
                expected = Some(length);
            }
            StoredValue::Null => return None,
            _ => return None,
        }
    }

    saw_nested.then_some(expected).flatten()
}

fn array_cardinality(values: &[StoredValue], depth: usize) -> Option<i32> {
    if depth >= MAX_ARRAY_RECURSION_DEPTH {
        return None;
    }
    let mut count = 0_i32;
    for value in values {
        let addend = match value {
            StoredValue::Array(nested) => array_cardinality(nested, depth + 1)?,
            _ => 1,
        };
        count = count.checked_add(addend)?;
    }
    Some(count)
}

pub(in super::super) fn eval_array_length_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let array = match iter.next().unwrap() {
        StoredValue::Array(values) => values,
        StoredValue::Null => return Ok(StoredValue::Null),
        _ => return Ok(StoredValue::Null),
    };
    let Some(dim) = array_dimension_arg(iter.next()) else {
        return Ok(StoredValue::Null);
    };
    Ok(array_length_at_dimension(&array, dim, 0)
        .map(StoredValue::Int32)
        .unwrap_or(StoredValue::Null))
}

pub(in super::super) fn eval_array_upper_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let array = match iter.next().unwrap() {
        StoredValue::Array(values) => values,
        StoredValue::Null => return Ok(StoredValue::Null),
        _ => return Ok(StoredValue::Null),
    };
    let Some(dim) = array_dimension_arg(iter.next()) else {
        return Ok(StoredValue::Null);
    };
    Ok(array_length_at_dimension(&array, dim, 0)
        .filter(|length| *length > 0)
        .map(StoredValue::Int32)
        .unwrap_or(StoredValue::Null))
}

pub(in super::super) fn eval_array_lower_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let array = match iter.next().unwrap() {
        StoredValue::Array(values) => values,
        StoredValue::Null => return Ok(StoredValue::Null),
        _ => return Ok(StoredValue::Null),
    };
    let Some(dim) = array_dimension_arg(iter.next()) else {
        return Ok(StoredValue::Null);
    };
    Ok(array_length_at_dimension(&array, dim, 0)
        .filter(|length| *length > 0)
        .map(|_| StoredValue::Int32(1))
        .unwrap_or(StoredValue::Null))
}

pub(in super::super) fn eval_cardinality_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    eval_unary_function(function_name, args, |value| match value {
        StoredValue::Array(values) => Ok(array_cardinality(&values, 0)
            .map(StoredValue::Int32)
            .unwrap_or(StoredValue::Null)),
        _ => Ok(StoredValue::Null),
    })
}

pub(in super::super) fn eval_array_position_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let array = match iter.next().unwrap() {
        StoredValue::Array(values) => values,
        StoredValue::Null => return Ok(StoredValue::Null),
        _ => return Ok(StoredValue::Null),
    };
    let Some(shape) = array_shape(&array) else {
        return Err(array_multidimensional_unsupported_error(true));
    };
    if shape.ndims() > 1 {
        return Err(array_multidimensional_unsupported_error(true));
    }
    let needle = iter.next().unwrap();
    for (index, value) in array.iter().enumerate() {
        if array_element_matches(value, &needle)? {
            return Ok(StoredValue::Int32((index + 1) as i32));
        }
    }
    Ok(StoredValue::Null)
}

pub(in super::super) fn eval_eq_any_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let array = match iter.next().unwrap() {
        StoredValue::Array(values) => values,
        StoredValue::Null => return Ok(StoredValue::Null),
        _ => return Ok(StoredValue::Null),
    };
    let needle = iter.next().unwrap();

    if array.is_empty() {
        return Ok(StoredValue::Boolean(false));
    }

    let mut has_null = false;
    for value in &array {
        if needle.is_null() || value.is_null() {
            has_null = true;
            continue;
        }
        if matches!(values_equal(value, &needle)?, Some(true)) {
            return Ok(StoredValue::Boolean(true));
        }
    }

    if has_null {
        Ok(StoredValue::Null)
    } else {
        Ok(StoredValue::Boolean(false))
    }
}

pub(in super::super) fn eval_array_cat_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    fn array_value_leaf_type_name(values: &[StoredValue]) -> Result<Option<&'static str>> {
        array_value_leaf_type_name_inner(values, 0)
    }

    fn array_value_leaf_type_name_inner(
        values: &[StoredValue],
        depth: usize,
    ) -> Result<Option<&'static str>> {
        if depth >= MAX_ARRAY_RECURSION_DEPTH {
            return Err(array_recursion_depth_error());
        }
        for value in values {
            match value {
                StoredValue::Array(nested) => {
                    if let Some(data_type) = array_value_leaf_type_name_inner(nested, depth + 1)? {
                        return Ok(Some(data_type));
                    }
                }
                StoredValue::Null => {}
                other => return Ok(Some(stored_value_pg_display_name(other))),
            }
        }
        Ok(None)
    }

    fn array_value_type_name(values: &[StoredValue]) -> Result<String> {
        let base = array_value_leaf_type_name(values)?.unwrap_or("text");
        let rank = array_shape(values)
            .map(|shape| shape.ndims())
            .unwrap_or(1)
            .max(1);
        Ok(format!("{}{}", base, "[]".repeat(rank)))
    }

    fn array_cat_common_element_type(left: &str, right: &str) -> Option<String> {
        if left == right {
            return Some(left.to_owned());
        }
        match (left, right) {
            ("integer", "bigint") | ("bigint", "integer") => Some("bigint".to_owned()),
            ("integer", "double precision") | ("double precision", "integer") => {
                Some("double precision".to_owned())
            }
            ("bigint", "double precision") | ("double precision", "bigint") => {
                Some("double precision".to_owned())
            }
            ("integer", "numeric") | ("numeric", "integer") => Some("numeric".to_owned()),
            ("bigint", "numeric") | ("numeric", "bigint") => Some("numeric".to_owned()),
            _ => None,
        }
    }

    fn array_cat_arg_signature(value: &StoredValue) -> Result<String> {
        match value {
            StoredValue::Array(values) => array_value_type_name(values),
            StoredValue::Null => Ok("unknown".to_owned()),
            other => Ok(stored_value_pg_display_name(other).to_owned()),
        }
    }

    let arg_types = args
        .iter()
        .map(array_cat_arg_signature)
        .collect::<Result<Vec<_>>>()?;
    let mut result: Option<(Vec<StoredValue>, Vec<usize>, Option<String>)> = None;
    let mut has_array = false;
    for arg in args {
        match arg {
            StoredValue::Array(values) => {
                let Some(shape) = array_shape(&values) else {
                    return Err(array_cat_incompatible_arrays());
                };
                let array_type = array_value_leaf_type_name(&values)?.map(str::to_owned);
                match result.as_mut() {
                    None => {
                        result = Some((values, shape.dims, array_type));
                    }
                    Some((existing, existing_shape, existing_type)) => {
                        if existing_shape.is_empty() {
                            if shape.is_empty() {
                                continue;
                            }
                            *existing = values;
                            *existing_shape = shape.dims;
                            *existing_type = array_type;
                            continue;
                        }
                        if shape.is_empty() {
                            continue;
                        }
                        if let (Some(left), Some(right)) =
                            (existing_type.as_ref(), array_type.as_ref())
                        {
                            if array_cat_common_element_type(left, right).is_none() {
                                return Err(function_not_found(
                                    function_name,
                                    arg_types.join(", "),
                                ));
                            }
                        }
                        let existing_values = std::mem::take(existing);
                        let existing_shape_values = std::mem::take(existing_shape);
                        let Some((merged, merged_shape)) = array_cat_merge_shapes(
                            existing_values,
                            existing_shape_values,
                            values,
                            shape.dims,
                        ) else {
                            return Err(array_cat_incompatible_arrays());
                        };
                        *existing = merged;
                        *existing_shape = merged_shape;
                        *existing_type = match (existing_type.take(), array_type) {
                            (Some(left), Some(right)) => {
                                array_cat_common_element_type(&left, &right).or(Some(left))
                            }
                            (Some(left), None) => Some(left),
                            (None, Some(right)) => Some(right),
                            (None, None) => None,
                        };
                    }
                }
                has_array = true;
            }
            StoredValue::Null => {}
            _ => return Err(function_not_found(function_name, arg_types.join(", "))),
        }
    }
    if !has_array {
        Ok(StoredValue::Null)
    } else {
        Ok(StoredValue::Array(
            result.map(|(values, ..)| values).unwrap_or_default(),
        ))
    }
}

fn array_cat_incompatible_arrays() -> Error {
    Error::Other("cannot concatenate incompatible arrays".to_owned())
}

fn array_append_prepend_error() -> Error {
    Error::Other("argument must be empty or one-dimensional array".to_owned())
}

fn array_multidimensional_unsupported_error(searching: bool) -> Error {
    let message = if searching {
        "searching for elements in multidimensional arrays is not supported"
    } else {
        "removing elements from multidimensional arrays is not supported"
    };
    Error::Other(message.to_owned())
}

fn array_cat_merge_shapes(
    left: Vec<StoredValue>,
    left_shape: Vec<usize>,
    right: Vec<StoredValue>,
    right_shape: Vec<usize>,
) -> Option<(Vec<StoredValue>, Vec<usize>)> {
    if left_shape.is_empty() {
        return Some((right, right_shape));
    }
    if right_shape.is_empty() {
        return Some((left, left_shape));
    }

    // PostgreSQL array_cat accepts equal ranks and ranks that differ by one;
    // the lower-ranked array becomes one element on the outer dimension.
    match left_shape.len().cmp(&right_shape.len()) {
        std::cmp::Ordering::Equal => {
            if left_shape.len() > 1 && left_shape[1..] != right_shape[1..] {
                return None;
            }
            let mut result = left;
            result.extend(right);
            let mut shape = left_shape;
            shape[0] += right_shape[0];
            Some((result, shape))
        }
        std::cmp::Ordering::Less if left_shape.len() + 1 == right_shape.len() => {
            if left_shape != right_shape[1..] {
                return None;
            }
            let mut result = Vec::with_capacity(left.len() + right.len());
            result.push(StoredValue::Array(left));
            result.extend(right);
            let mut shape = right_shape;
            shape[0] += 1;
            Some((result, shape))
        }
        std::cmp::Ordering::Greater if right_shape.len() + 1 == left_shape.len() => {
            if right_shape != left_shape[1..] {
                return None;
            }
            let mut result = left;
            result.push(StoredValue::Array(right));
            let mut shape = left_shape;
            shape[0] += 1;
            Some((result, shape))
        }
        _ => None,
    }
}

pub(in super::super) fn eval_array_append_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let mut array = match iter.next().unwrap() {
        StoredValue::Array(values) => values,
        StoredValue::Null => Vec::new(),
        _ => return Ok(StoredValue::Null),
    };
    let Some(shape) = array_shape(&array) else {
        return Err(array_append_prepend_error());
    };
    if shape.ndims() > 1 {
        return Err(array_append_prepend_error());
    }
    array.push(iter.next().unwrap());
    Ok(StoredValue::Array(array))
}

pub(in super::super) fn eval_array_prepend_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let value = iter.next().unwrap();
    let mut array = match iter.next().unwrap() {
        StoredValue::Array(values) => values,
        StoredValue::Null => Vec::new(),
        _ => return Ok(StoredValue::Null),
    };
    let Some(shape) = array_shape(&array) else {
        return Err(array_append_prepend_error());
    };
    if shape.ndims() > 1 {
        return Err(array_append_prepend_error());
    }
    array.insert(0, value);
    Ok(StoredValue::Array(array))
}

pub(in super::super) fn eval_array_remove_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if args.len() != 2 {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let array = match iter.next().unwrap() {
        StoredValue::Array(values) => values,
        StoredValue::Null => return Ok(StoredValue::Null),
        _ => return Ok(StoredValue::Null),
    };
    let Some(shape) = array_shape(&array) else {
        return Err(array_multidimensional_unsupported_error(false));
    };
    if shape.ndims() > 1 {
        return Err(array_multidimensional_unsupported_error(false));
    }
    let needle = iter.next().unwrap();
    let mut result = Vec::with_capacity(array.len());
    for value in array {
        if !array_element_matches(&value, &needle)? {
            result.push(value);
        }
    }
    Ok(StoredValue::Array(result))
}

pub(in super::super) fn eval_string_to_array_function(
    function_name: &str,
    args: Vec<StoredValue>,
) -> Result<StoredValue> {
    if !(2..=3).contains(&args.len()) {
        return Err(Error::Other(format!(
            "DB9 function '{}' expects 2 or 3 args, got {}",
            function_name,
            args.len()
        )));
    }

    let mut iter = args.into_iter();
    let text = match iter.next().unwrap() {
        StoredValue::Text(value) => value,
        StoredValue::Null => return Ok(StoredValue::Null),
        other => stored_value_to_string(&other)?,
    };
    let delimiter = match iter.next().unwrap() {
        StoredValue::Text(value) => Some(value),
        StoredValue::Null => None,
        other => Some(stored_value_to_string(&other)?),
    };
    let null_text = iter
        .next()
        .map(|value| -> Result<Option<String>> {
            match value {
                StoredValue::Text(value) => Ok(Some(value)),
                StoredValue::Null => Ok(None),
                other => Ok(Some(stored_value_to_string(&other)?)),
            }
        })
        .transpose()?
        .flatten();

    if text.is_empty() {
        if null_text.as_ref().is_some_and(|value| value.is_empty()) {
            return Ok(StoredValue::Array(vec![StoredValue::Null]));
        }
        return Ok(StoredValue::Array(Vec::new()));
    }

    let parts = match delimiter {
        None => text
            .chars()
            .map(|ch| {
                let part = ch.to_string();
                if null_text
                    .as_ref()
                    .is_some_and(|null_text| &part == null_text)
                {
                    StoredValue::Null
                } else {
                    StoredValue::Text(part)
                }
            })
            .collect(),
        // PostgreSQL treats an empty delimiter as "do not split"; only a NULL
        // delimiter splits the string into characters.
        Some(delimiter) if delimiter.is_empty() => {
            if null_text
                .as_ref()
                .is_some_and(|null_text| &text == null_text)
            {
                vec![StoredValue::Null]
            } else {
                vec![StoredValue::Text(text)]
            }
        }
        Some(delimiter) => text
            .split(&delimiter)
            .map(|part| {
                if null_text
                    .as_ref()
                    .is_some_and(|null_text| part == null_text)
                {
                    StoredValue::Null
                } else {
                    StoredValue::Text(part.to_owned())
                }
            })
            .collect(),
    };
    Ok(StoredValue::Array(parts))
}

#[cfg(test)]
mod tests {
    use super::*;

    fn nested_array() -> StoredValue {
        StoredValue::Array(vec![
            StoredValue::Array(vec![StoredValue::Int32(1), StoredValue::Int32(2)]),
            StoredValue::Array(vec![StoredValue::Int32(3), StoredValue::Int32(4)]),
        ])
    }

    fn deeply_nested_array(depth: usize) -> StoredValue {
        let mut value = StoredValue::Int32(1);
        for _ in 0..depth {
            value = StoredValue::Array(vec![value]);
        }
        value
    }

    #[test]
    fn array_dimension_functions_support_nested_dimensions() {
        assert_eq!(
            eval_array_length_function("array_length", vec![nested_array(), StoredValue::Int32(1)])
                .unwrap(),
            StoredValue::Int32(2)
        );
        assert_eq!(
            eval_array_length_function("array_length", vec![nested_array(), StoredValue::Int32(2)])
                .unwrap(),
            StoredValue::Int32(2)
        );
        assert_eq!(
            eval_array_upper_function("array_upper", vec![nested_array(), StoredValue::Int32(2)])
                .unwrap(),
            StoredValue::Int32(2)
        );
        assert_eq!(
            eval_array_lower_function("array_lower", vec![nested_array(), StoredValue::Int32(2)])
                .unwrap(),
            StoredValue::Int32(1)
        );
        assert_eq!(
            eval_array_length_function("array_length", vec![nested_array(), StoredValue::Int32(3)])
                .unwrap(),
            StoredValue::Null
        );
        assert_eq!(
            eval_array_length_function(
                "array_length",
                vec![StoredValue::Array(vec![]), StoredValue::Int32(1)]
            )
            .unwrap(),
            StoredValue::Null
        );
        assert_eq!(
            eval_array_length_function("array_length", vec![nested_array(), StoredValue::Int32(0)])
                .unwrap(),
            StoredValue::Null
        );
        assert_eq!(
            eval_array_length_function("array_length", vec![nested_array(), StoredValue::Null])
                .unwrap(),
            StoredValue::Null
        );
        assert_eq!(
            eval_array_length_function(
                "array_length",
                vec![nested_array(), StoredValue::Int64(i64::MAX)]
            )
            .unwrap(),
            StoredValue::Null
        );
        assert_eq!(
            eval_cardinality_function("cardinality", vec![nested_array()]).unwrap(),
            StoredValue::Int32(4)
        );
        assert_eq!(
            eval_cardinality_function(
                "cardinality",
                vec![StoredValue::Array(vec![
                    StoredValue::Array(vec![StoredValue::Int32(1), StoredValue::Null]),
                    StoredValue::Array(vec![StoredValue::Int32(3)]),
                ])]
            )
            .unwrap(),
            StoredValue::Int32(3)
        );
        assert_eq!(
            eval_cardinality_function("cardinality", vec![StoredValue::Array(vec![])]).unwrap(),
            StoredValue::Int32(0)
        );
        assert_eq!(
            eval_array_length_function(
                "array_length",
                vec![
                    StoredValue::Array(vec![
                        StoredValue::Array(vec![StoredValue::Int32(1)]),
                        StoredValue::Array(vec![StoredValue::Int32(2), StoredValue::Int32(3)]),
                    ]),
                    StoredValue::Int32(2),
                ]
            )
            .unwrap(),
            StoredValue::Null
        );
        assert_eq!(
            eval_array_length_function(
                "array_length",
                vec![
                    StoredValue::Array(vec![
                        StoredValue::Array(vec![StoredValue::Int32(1)]),
                        StoredValue::Null,
                    ]),
                    StoredValue::Int32(2),
                ]
            )
            .unwrap(),
            StoredValue::Null
        );
    }

    #[test]
    fn array_values_equal_treats_null_elements_as_equal() {
        assert!(array_values_equal(&[StoredValue::Null], &[StoredValue::Null]).unwrap());
        assert!(
            array_values_equal(
                &[StoredValue::Array(vec![StoredValue::Null])],
                &[StoredValue::Array(vec![StoredValue::Null])],
            )
            .unwrap()
        );
    }

    #[test]
    fn array_dimension_functions_reject_excessive_recursion_depth() {
        let mut nested = StoredValue::Int32(1);
        for _ in 0..=MAX_ARRAY_RECURSION_DEPTH {
            nested = StoredValue::Array(vec![nested]);
        }

        assert_eq!(
            eval_array_length_function(
                "array_length",
                vec![
                    nested.clone(),
                    StoredValue::Int32((MAX_ARRAY_RECURSION_DEPTH + 1) as i32),
                ],
            )
            .unwrap(),
            StoredValue::Null
        );
        assert_eq!(
            eval_cardinality_function("cardinality", vec![nested]).unwrap(),
            StoredValue::Null
        );
        let mut nested = StoredValue::Int32(1);
        for _ in 0..=MAX_ARRAY_RECURSION_DEPTH {
            nested = StoredValue::Array(vec![nested]);
        }
        let err =
            eval_array_position_function("array_position", vec![nested, StoredValue::Int32(1)])
                .unwrap_err();
        assert_eq!(
            err.to_string(),
            "searching for elements in multidimensional arrays is not supported"
        );
    }

    #[test]
    fn array_helpers_reject_excessive_recursion_depth() {
        let deep = deeply_nested_array(MAX_ARRAY_RECURSION_DEPTH + 1);

        let err =
            eval_array_cat_function("array_cat", vec![deep.clone(), StoredValue::Array(vec![])])
                .unwrap_err();
        assert_eq!(err.to_string(), "array value is too deep");

        let StoredValue::Array(values) = deep.clone() else {
            unreachable!();
        };
        let err = array_values_equal(&values, &values).unwrap_err();
        assert_eq!(err.to_string(), "array value is too deep");
    }
}
