// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::sync::Arc;

use arrow::{
    array::{
        Array, ArrayRef, AsArray, BinaryArray, ListArray, ListBuilder, StringArray, StringBuilder,
    },
    datatypes::DataType,
};
use datafusion::{
    common::{
        Result, ScalarValue,
        cast::{as_binary_array, as_int32_array, as_list_array, as_string_array},
    },
    physical_plan::ColumnarValue,
};
use datafusion_ext_commons::df_execution_err;

pub fn string_lower(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    match &args[0] {
        ColumnarValue::Array(array) => Ok(ColumnarValue::Array(Arc::new(StringArray::from_iter(
            as_string_array(array)?
                .into_iter()
                .map(|s| s.map(|s| s.to_lowercase())),
        )))),
        ColumnarValue::Scalar(ScalarValue::Utf8(Some(str))) => Ok(ColumnarValue::Scalar(
            ScalarValue::Utf8(Some(str.to_lowercase())),
        )),
        ColumnarValue::Scalar(ScalarValue::Utf8(None)) => {
            Ok(ColumnarValue::Scalar(ScalarValue::Utf8(None)))
        }
        _ => df_execution_err!("string_lower only supports literal utf8"),
    }
}

pub fn string_upper(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    match &args[0] {
        ColumnarValue::Array(array) => Ok(ColumnarValue::Array(Arc::new(StringArray::from_iter(
            as_string_array(array)?
                .into_iter()
                .map(|s| s.map(|s| s.to_uppercase())),
        )))),
        ColumnarValue::Scalar(ScalarValue::Utf8(Some(str))) => Ok(ColumnarValue::Scalar(
            ScalarValue::Utf8(Some(str.to_uppercase())),
        )),
        ColumnarValue::Scalar(ScalarValue::Utf8(None)) => {
            Ok(ColumnarValue::Scalar(ScalarValue::Utf8(None)))
        }
        _ => df_execution_err!("string_upper only supports literal utf8"),
    }
}

pub fn string_space(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    let n_array = args[0].clone().into_array(1)?;
    let repeated_string_array = Arc::new(StringArray::from_iter(
        as_int32_array(&n_array)?
            .into_iter()
            .map(|n| n.map(|n| " ".repeat(n.max(0) as usize))),
    ));
    Ok(ColumnarValue::Array(repeated_string_array))
}

pub fn string_repeat(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    let string_array = args[0].clone().into_array(1)?;
    let n = match &args[1] {
        ColumnarValue::Scalar(ScalarValue::Int32(Some(n))) => (*n).max(0),
        ColumnarValue::Scalar(scalar) if scalar.is_null() => {
            return Ok(ColumnarValue::Scalar(ScalarValue::Utf8(None)));
        }
        _ => df_execution_err!("string_repeat n only supports literal int32")?,
    };

    let repeated_string_array: ArrayRef = Arc::new(StringArray::from_iter(
        as_string_array(&string_array)?
            .into_iter()
            .map(|s| s.map(|s| s.repeat(n as usize))),
    ));
    Ok(ColumnarValue::Array(repeated_string_array))
}

pub fn string_split(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    let string_array = args[0].clone().into_array(1)?;
    let pat = match &args[1] {
        ColumnarValue::Scalar(ScalarValue::Utf8(Some(pat))) if !pat.is_empty() => pat,
        _ => df_execution_err!("string_split pattern only supports non-empty literal string")?,
    };

    let mut splitted_builder = ListBuilder::new(StringBuilder::new());
    for s in as_string_array(&string_array)? {
        match s {
            Some(s) => {
                for segment in s.split(pat) {
                    splitted_builder.values().append_value(segment);
                }
                splitted_builder.append(true);
            }
            None => {
                splitted_builder.append_null();
            }
        }
    }
    Ok(ColumnarValue::Array(Arc::new(splitted_builder.finish())))
}

pub fn spark_substring(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    let pos = match &args[1] {
        ColumnarValue::Scalar(ScalarValue::Int64(Some(value))) => *value,
        _ => df_execution_err!("substring pos only supports literal int64")?,
    };
    let len = match &args[2] {
        ColumnarValue::Scalar(ScalarValue::Int64(Some(value))) => *value,
        _ => df_execution_err!("substring len only supports literal int64")?,
    };

    match &args[0] {
        ColumnarValue::Array(array) => match array.data_type() {
            DataType::Utf8 => {
                let result: StringArray = as_string_array(array)?
                    .iter()
                    .map(|value| {
                        value.map(|value| {
                            let chars = value.chars().collect::<Vec<_>>();
                            let (start, end) = substring_range(chars.len(), pos, len);
                            chars[start..end].iter().collect::<String>()
                        })
                    })
                    .collect();
                Ok(ColumnarValue::Array(Arc::new(result)))
            }
            DataType::Binary => {
                let result: BinaryArray = as_binary_array(array)?
                    .iter()
                    .map(|value| {
                        value.map(|value| {
                            let (start, end) = substring_range(value.len(), pos, len);
                            &value[start..end]
                        })
                    })
                    .collect();
                Ok(ColumnarValue::Array(Arc::new(result)))
            }
            other => df_execution_err!("substring only supports utf8 or binary, got {other:?}"),
        },
        ColumnarValue::Scalar(ScalarValue::Utf8(value)) => Ok(ColumnarValue::Scalar(
            ScalarValue::Utf8(value.as_ref().map(|value| {
                let chars = value.chars().collect::<Vec<_>>();
                let (start, end) = substring_range(chars.len(), pos, len);
                chars[start..end].iter().collect::<String>()
            })),
        )),
        ColumnarValue::Scalar(ScalarValue::Binary(value)) => Ok(ColumnarValue::Scalar(
            ScalarValue::Binary(value.as_ref().map(|value| {
                let (start, end) = substring_range(value.len(), pos, len);
                value[start..end].to_vec()
            })),
        )),
        other => df_execution_err!("substring only supports utf8 or binary, got {:?}", other),
    }
}

fn substring_range(total_len: usize, pos: i64, len: i64) -> (usize, usize) {
    if len <= 0 {
        return (0, 0);
    }

    let total_len_i64 = total_len as i64;
    let raw_start = if pos > 0 {
        pos - 1
    } else if pos < 0 {
        total_len_i64.saturating_add(pos)
    } else {
        0
    };
    let start = raw_start.clamp(0, total_len_i64) as usize;
    let end = (start as i64).saturating_add(len).clamp(0, total_len_i64) as usize;
    (start, end)
}

/// concat() function compatible with spark (returns null if any param is null)
/// concat('abcde', 2, 22) = 'abcde222
/// concat('abcde', 2, NULL, 22) = NULL
pub fn string_concat(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    // do not accept 0 arguments.
    if args.is_empty() {
        df_execution_err!(
            "concat was called with {} arguments. It requires at least 1.",
            args.len(),
        )?;
    }

    // first, decide whether to return a scalar or a vector.
    let mut return_array = args.iter().filter_map(|x| match x {
        ColumnarValue::Array(array) => Some(array.len()),
        _ => None,
    });
    if let Some(size) = return_array.next() {
        let result = (0..size)
            .map(|index| {
                let mut owned_string: String = "".to_owned();
                let mut is_not_null = true;
                for arg in args {
                    #[allow(clippy::collapsible_match)]
                    match arg {
                        ColumnarValue::Scalar(ScalarValue::Utf8(maybe_value)) => {
                            if let Some(value) = maybe_value {
                                owned_string.push_str(value);
                            } else {
                                is_not_null = false;
                                break;
                            }
                        }
                        ColumnarValue::Array(v) => {
                            if v.is_valid(index) {
                                let v = as_string_array(v).expect("Expected a StringArray");
                                owned_string.push_str(v.value(index));
                            } else {
                                is_not_null = false;
                                break;
                            }
                        }
                        _ => unreachable!(),
                    }
                }
                is_not_null.then_some(owned_string)
            })
            .collect::<StringArray>();

        Ok(ColumnarValue::Array(Arc::new(result)))
    } else {
        // short avenue with only scalars
        // returns null if args contains null
        let is_not_null = args
            .iter()
            .all(|arg| !matches!(arg, ColumnarValue::Scalar(scalar) if scalar.is_null()));
        if !is_not_null {
            return Ok(ColumnarValue::Scalar(ScalarValue::Utf8(None)));
        }

        // concat
        let initial = Some(String::new());
        let result = args.iter().fold(initial, |mut acc, rhs| {
            if let Some(ref mut inner) = acc {
                match rhs {
                    ColumnarValue::Scalar(ScalarValue::Utf8(Some(v))) => {
                        inner.push_str(v);
                    }
                    _ => unreachable!(""),
                };
            };
            acc
        });
        Ok(ColumnarValue::Scalar(ScalarValue::Utf8(result)))
    }
}

pub fn string_concat_ws(args: &[ColumnarValue]) -> Result<ColumnarValue> {
    let sep = match &args[0] {
        ColumnarValue::Scalar(ScalarValue::Utf8(Some(pat))) => pat,
        ColumnarValue::Scalar(ScalarValue::Utf8(None)) => {
            return Ok(ColumnarValue::Scalar(ScalarValue::Utf8(None)));
        }
        _ => df_execution_err!("string_concat_ws separator only supports literal string")?,
    };

    #[derive(Clone)]
    enum Arg<'a> {
        Literal(&'a str),
        LiteralList(Vec<&'a str>),
        Array(StringArray),
        List(ListArray),
        Ignore,
    }
    let mut args = args[1..]
        .iter()
        .map(|arg| {
            match arg {
                ColumnarValue::Array(array) => {
                    if let Ok(s) = as_string_array(&array).cloned() {
                        return Ok(Arg::Array(s));
                    }
                    if let Ok(l) = as_list_array(&array).cloned() {
                        if l.value_type() == DataType::Utf8 {
                            return Ok(Arg::List(l));
                        }
                    }
                }
                ColumnarValue::Scalar(scalar) => {
                    if scalar.is_null() {
                        return Ok(Arg::Ignore);
                    }
                    if let ScalarValue::Utf8(Some(s)) = scalar {
                        return Ok(Arg::Literal(s));
                    }
                    if let ScalarValue::List(l) = scalar
                        && l.data_type() == &DataType::Utf8
                    {
                        if l.is_null(0) {
                            return Ok(Arg::Ignore);
                        }
                        let list = l.value(0);
                        let str_list = list.as_string::<i32>();
                        let mut strs = vec![];
                        for s in str_list.iter() {
                            if let Some(s) = s {
                                strs.push(unsafe {
                                    // safety: bypass "returning temporary value" check
                                    std::mem::transmute::<_, &str>(s)
                                });
                            }
                        }
                        return Ok(Arg::LiteralList(strs));
                    }
                }
            }
            df_execution_err!("concat_ws args must be string or array<string>")
        })
        .collect::<Result<Vec<_>>>()?;

    args.retain(|arg| !matches!(arg, Arg::Ignore));

    // fast path when all args are literals
    if args
        .iter()
        .all(|arg| matches!(arg, Arg::Literal(_) | Arg::LiteralList(_)))
    {
        let mut segments = vec![];
        for arg in &args {
            match arg {
                Arg::Literal(s) => segments.push(*s),
                Arg::LiteralList(l) => segments.extend(l),
                _ => unreachable!(),
            }
        }
        return Ok(ColumnarValue::Scalar(ScalarValue::Utf8(Some(
            segments.join(sep),
        ))));
    }

    let mut array_len = 1;
    for arg in &args {
        match arg {
            Arg::Array(strings) => array_len = strings.len(),
            Arg::List(list) => array_len = list.len(),
            _ => continue,
        }
    }

    let mut segments = vec![];
    let concatenated_string_array: ArrayRef =
        Arc::new(StringArray::from_iter((0..array_len).map(|i| {
            segments.clear();
            for arg in &args {
                match &arg {
                    Arg::Literal(s) => segments.push(*s),
                    Arg::LiteralList(l) => segments.extend(l),
                    Arg::Array(strings) => {
                        if strings.is_valid(i) {
                            segments.push(strings.value(i));
                        }
                    }
                    Arg::List(list) => {
                        if list.is_valid(i) {
                            let strings =
                                as_string_array(list.values()).expect("Expected a StringArray");
                            let offsets = list.value_offsets();
                            let l = offsets[i] as usize;
                            let r = offsets[i + 1] as usize;
                            for i in l..r {
                                if strings.is_valid(i) {
                                    segments.push(strings.value(i));
                                }
                            }
                        }
                    }
                    _ => unreachable!(),
                }
            }
            Some(segments.join(sep))
        })));
    Ok(ColumnarValue::Array(concatenated_string_array))
}

#[cfg(test)]
mod test {
    use std::sync::Arc;

    use arrow::array::{BinaryArray, Int32Array, ListBuilder, StringArray, StringBuilder};
    use datafusion::{
        common::{
            Result, ScalarValue,
            cast::{as_binary_array, as_list_array, as_string_array},
        },
        physical_plan::ColumnarValue,
    };
    use datafusion_ext_commons::df_execution_err;

    use crate::spark_strings::{
        spark_substring, string_concat, string_concat_ws, string_lower, string_repeat,
        string_space, string_split, string_upper,
    };

    #[test]
    fn test_string_space() -> Result<()> {
        // positive case
        let r = string_space(&vec![ColumnarValue::Array(Arc::new(
            Int32Array::from_iter(vec![Some(3), Some(0), Some(-100), None]),
        ))])?;
        let s = r.into_array(4)?;
        assert_eq!(
            as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
            vec![Some("   "), Some(""), Some(""), None,]
        );
        Ok(())
    }

    #[test]
    fn test_string_upper() -> Result<()> {
        let r = string_upper(&vec![ColumnarValue::Array(Arc::new(
            StringArray::from_iter(vec![Some("{123}"), Some("A'asd'"), None]),
        ))])?;
        let s = r.into_array(3)?;
        assert_eq!(
            as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
            vec![Some("{123}"), Some("A'ASD'"), None,]
        );
        Ok(())
    }

    #[test]
    fn test_string_lower() -> Result<()> {
        let r = string_lower(&vec![ColumnarValue::Array(Arc::new(
            StringArray::from_iter(vec![Some("{123}"), Some("A'asd'"), None]),
        ))])?;
        let s = r.into_array(3)?;
        assert_eq!(
            as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
            vec![Some("{123}"), Some("a'asd'"), None,]
        );
        Ok(())
    }

    #[test]
    fn test_string_lower_null_scalar() -> Result<()> {
        let r = string_lower(&vec![ColumnarValue::Scalar(ScalarValue::Utf8(None))])?;
        match r {
            ColumnarValue::Scalar(ScalarValue::Utf8(None)) => Ok(()),
            other => df_execution_err!("Expected null Utf8 scalar, got: {:?}", other),
        }
    }

    #[test]
    fn test_string_upper_null_scalar() -> Result<()> {
        let r = string_upper(&vec![ColumnarValue::Scalar(ScalarValue::Utf8(None))])?;
        match r {
            ColumnarValue::Scalar(ScalarValue::Utf8(None)) => Ok(()),
            other => df_execution_err!("Expected null Utf8 scalar, got: {:?}", other),
        }
    }

    #[test]
    fn test_spark_substring_string_array() -> Result<()> {
        let r = spark_substring(&vec![
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some("abcdef".to_string()),
                Some("数据fusion".to_string()),
                None,
            ]))),
            ColumnarValue::Scalar(ScalarValue::from(2_i64)),
            ColumnarValue::Scalar(ScalarValue::from(3_i64)),
        ])?;
        let s = r.into_array(3)?;
        assert_eq!(
            as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
            vec![Some("bcd"), Some("据fu"), None]
        );
        Ok(())
    }

    #[test]
    fn test_spark_substring_binary_array() -> Result<()> {
        let r = spark_substring(&vec![
            ColumnarValue::Array(Arc::new(BinaryArray::from_iter(vec![
                Some(&[1_u8, 2, 3, 4, 5][..]),
                Some(&[9_u8, 8][..]),
                None,
            ]))),
            ColumnarValue::Scalar(ScalarValue::from(2_i64)),
            ColumnarValue::Scalar(ScalarValue::from(3_i64)),
        ])?;
        let b = r.into_array(3)?;
        assert_eq!(
            as_binary_array(&b)?.iter().collect::<Vec<_>>(),
            vec![Some(&[2_u8, 3, 4][..]), Some(&[8_u8][..]), None]
        );
        Ok(())
    }

    #[test]
    fn test_spark_substring_edge_cases() -> Result<()> {
        for (name, pos, len, expected) in [
            (
                "zero pos",
                0_i64,
                3_i64,
                vec![Some("abc"), Some("数据f"), None],
            ),
            (
                "negative pos",
                -3_i64,
                2_i64,
                vec![Some("de"), Some("io"), None],
            ),
            ("zero len", 2_i64, 0_i64, vec![Some(""), Some(""), None]),
            (
                "pos past end",
                10_i64,
                3_i64,
                vec![Some(""), Some(""), None],
            ),
            (
                "len past end",
                2_i64,
                100_i64,
                vec![Some("bcdef"), Some("据fusion"), None],
            ),
            (
                "i64 min pos",
                i64::MIN,
                3_i64,
                vec![Some("abc"), Some("数据f"), None],
            ),
            (
                "i64 max len",
                2_i64,
                i64::MAX,
                vec![Some("bcdef"), Some("据fusion"), None],
            ),
        ] {
            let r = spark_substring(&vec![
                ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                    Some("abcdef".to_string()),
                    Some("数据fusion".to_string()),
                    None,
                ]))),
                ColumnarValue::Scalar(ScalarValue::from(pos)),
                ColumnarValue::Scalar(ScalarValue::from(len)),
            ])?;
            let s = r.into_array(3)?;
            assert_eq!(
                as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
                expected,
                "string array case: {name}"
            );
        }

        for (name, pos, len, expected) in [
            (
                "zero len",
                2_i64,
                0_i64,
                vec![Some(&[][..]), Some(&[][..]), None],
            ),
            (
                "pos past end",
                10_i64,
                3_i64,
                vec![Some(&[][..]), Some(&[][..]), None],
            ),
            (
                "i64 max len",
                2_i64,
                i64::MAX,
                vec![Some(&[2_u8, 3, 4, 5][..]), Some(&[][..]), None],
            ),
        ] {
            let r = spark_substring(&vec![
                ColumnarValue::Array(Arc::new(BinaryArray::from_iter(vec![
                    Some(&[1_u8, 2, 3, 4, 5][..]),
                    Some(&[][..]),
                    None,
                ]))),
                ColumnarValue::Scalar(ScalarValue::from(pos)),
                ColumnarValue::Scalar(ScalarValue::from(len)),
            ])?;
            let b = r.into_array(3)?;
            assert_eq!(
                as_binary_array(&b)?.iter().collect::<Vec<_>>(),
                expected,
                "binary array case: {name}"
            );
        }

        for (name, input, pos, len, expected) in [
            ("i64 min pos", "abcdef", i64::MIN, 2_i64, "ab"),
            ("empty string", "", 2_i64, i64::MAX, ""),
        ] {
            let r = spark_substring(&vec![
                ColumnarValue::Scalar(ScalarValue::Utf8(Some(input.to_string()))),
                ColumnarValue::Scalar(ScalarValue::from(pos)),
                ColumnarValue::Scalar(ScalarValue::from(len)),
            ])?;
            match r {
                ColumnarValue::Scalar(ScalarValue::Utf8(Some(value))) => {
                    assert_eq!(value, expected, "scalar string case: {name}")
                }
                other => df_execution_err!("Expected scalar Utf8 substring, got: {:?}", other)?,
            }
        }

        let r = spark_substring(&vec![
            ColumnarValue::Scalar(ScalarValue::Binary(Some(vec![1_u8, 2, 3, 4, 5]))),
            ColumnarValue::Scalar(ScalarValue::from(2_i64)),
            ColumnarValue::Scalar(ScalarValue::from(i64::MAX)),
        ])?;
        match r {
            ColumnarValue::Scalar(ScalarValue::Binary(Some(value))) => {
                assert_eq!(value, vec![2_u8, 3, 4, 5], "scalar binary case")
            }
            other => df_execution_err!("Expected scalar Binary substring, got: {:?}", other)?,
        }
        Ok(())
    }

    #[test]
    fn test_string_repeat() -> Result<()> {
        // positive case
        let r = string_repeat(&vec![
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some("123".to_string()),
                Some("a".to_string()),
                None,
            ]))),
            ColumnarValue::Scalar(ScalarValue::from(3_i32)),
        ])?;
        let s = r.into_array(3)?;
        assert_eq!(
            as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
            vec![Some("123123123"), Some("aaa"), None,]
        );

        // repeat with n < 0
        let r = string_repeat(&vec![
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some("123".to_string()),
                Some("a".to_string()),
                None,
            ]))),
            ColumnarValue::Scalar(ScalarValue::from(-1_i32)),
        ])?;
        let s = r.into_array(3)?;
        assert_eq!(
            as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
            vec![Some(""), Some(""), None,]
        );

        // repeat with n = null
        let r = string_repeat(&vec![
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some("123".to_string()),
                Some("a".to_string()),
                None,
            ]))),
            ColumnarValue::Scalar(ScalarValue::Int32(None)),
        ])?;
        let s = r.into_array(3)?;
        assert_eq!(
            as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
            vec![None, None, None,]
        );
        Ok(())
    }

    #[test]
    fn test_string_split() -> Result<()> {
        // positive case
        let r = string_split(&vec![
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some("123,456,,,789,".to_string()),
                Some("123".to_string()),
                Some(String::new()),
                None,
            ]))),
            ColumnarValue::Scalar(ScalarValue::from(",")),
        ])?;
        let list = r.into_array(4)?;
        let list_offsets = as_list_array(&list)?.value_offsets();
        let list_values = as_list_array(&list)?.values();

        assert_eq!(list_offsets, vec![0, 6, 7, 8, 8]);
        assert_eq!(
            as_string_array(list_values)?
                .into_iter()
                .collect::<Vec<_>>(),
            vec![
                Some("123"),
                Some("456"),
                Some(""),
                Some(""),
                Some("789"),
                Some(""),
                Some("123"),
                Some(""),
            ]
        );
        Ok(())
    }

    #[test]
    fn test_string_concat() -> Result<()> {
        // positive case
        let r = string_concat(&vec![
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some("123".to_string()),
                None,
            ]))),
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some("444".to_string()),
                Some("456".to_string()),
            ]))),
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some(String::new()),
                Some(String::new()),
            ]))),
            ColumnarValue::Scalar(ScalarValue::from("SomeScalar")),
        ])?;
        let s = r.into_array(2)?;
        assert_eq!(
            as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
            vec![Some("123444SomeScalar"), None,]
        );
        Ok(())
    }
    #[test]
    fn test_string_concat_ws() -> Result<()> {
        // positive case
        let r = string_concat_ws(&vec![
            ColumnarValue::Scalar(ScalarValue::from("||")),
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some("123".to_string()),
                None,
            ]))),
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                None,
                Some("456".to_string()),
            ]))),
            ColumnarValue::Array(Arc::new(StringArray::from_iter(vec![
                Some(String::new()),
                Some(String::new()),
            ]))),
            ColumnarValue::Array(Arc::new({
                let mut list_builder = ListBuilder::new(StringBuilder::new());
                list_builder.values().append_value("XX");
                list_builder.values().append_value("YY");
                list_builder.values().append_null();
                list_builder.append(true);
                list_builder.append_null();
                list_builder.finish()
            })),
            ColumnarValue::Scalar(ScalarValue::from("SomeScalar")),
            ColumnarValue::Scalar(ScalarValue::Utf8(None)),
        ])?;
        let s = r.into_array(2)?;
        assert_eq!(
            as_string_array(&s)?.into_iter().collect::<Vec<_>>(),
            vec![Some("123||||XX||YY||SomeScalar"), Some("456||||SomeScalar"),]
        );
        Ok(())
    }
}
