//! String functions.

use crate::error::{ExecutionError, ExecutionResult};
use crate::result::CypherValue;

/// toString(value) - converts a value to a string
pub fn to_string(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "toString() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::String(s) => Ok(CypherValue::String(s.clone())),
        CypherValue::Integer(i) => Ok(CypherValue::String(i.to_string())),
        CypherValue::Float(f) => Ok(CypherValue::String(f.to_string())),
        CypherValue::Boolean(b) => Ok(CypherValue::String(b.to_string())),
        CypherValue::Null => Ok(CypherValue::Null),
        // Temporal types are convertible
        CypherValue::Date(d) => Ok(CypherValue::String(d.to_string())),
        CypherValue::Time(t) => Ok(CypherValue::String(t.to_string())),
        CypherValue::DateTime(dt) => Ok(CypherValue::String(dt.to_string())),
        CypherValue::LocalTime(lt) => Ok(CypherValue::String(lt.to_string())),
        CypherValue::LocalDateTime(ldt) => Ok(CypherValue::String(ldt.to_string())),
        CypherValue::Duration(dur) => Ok(CypherValue::String(dur.to_string())),
        CypherValue::ExtendedDate(d) => Ok(CypherValue::String(d.to_string())),
        CypherValue::ExtendedLocalDateTime(ldt) => Ok(CypherValue::String(ldt.to_string())),
        // Complex types cannot be converted
        CypherValue::List(_) | CypherValue::Map(_) | CypherValue::Node(_)
        | CypherValue::Relationship(_) | CypherValue::Path(_) => {
            Err(ExecutionError::Type(format!(
                "toString() cannot convert {} to string",
                args[0].type_name()
            )))
        }
    }
}

/// toUpper(string) - converts a string to uppercase
pub fn to_upper(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "toUpper() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::String(s) => Ok(CypherValue::String(s.to_uppercase())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "toUpper() requires a string, got {}",
            other.type_name()
        ))),
    }
}

/// toLower(string) - converts a string to lowercase
pub fn to_lower(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "toLower() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::String(s) => Ok(CypherValue::String(s.to_lowercase())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "toLower() requires a string, got {}",
            other.type_name()
        ))),
    }
}

/// trim(string) - removes leading and trailing whitespace
pub fn trim(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "trim() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::String(s) => Ok(CypherValue::String(s.trim().to_string())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "trim() requires a string, got {}",
            other.type_name()
        ))),
    }
}

/// ltrim(string) - removes leading whitespace
pub fn ltrim(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "ltrim() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::String(s) => Ok(CypherValue::String(s.trim_start().to_string())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "ltrim() requires a string, got {}",
            other.type_name()
        ))),
    }
}

/// rtrim(string) - removes trailing whitespace
pub fn rtrim(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "rtrim() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::String(s) => Ok(CypherValue::String(s.trim_end().to_string())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "rtrim() requires a string, got {}",
            other.type_name()
        ))),
    }
}

/// replace(original, search, replace) - replaces all occurrences
pub fn replace(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 3 {
        return Err(ExecutionError::InvalidArgument(
            "replace() requires exactly 3 arguments".to_string(),
        ));
    }

    match (&args[0], &args[1], &args[2]) {
        (CypherValue::String(original), CypherValue::String(search), CypherValue::String(replacement)) => {
            Ok(CypherValue::String(original.replace(search, replacement)))
        }
        (CypherValue::Null, _, _) | (_, CypherValue::Null, _) | (_, _, CypherValue::Null) => {
            Ok(CypherValue::Null)
        }
        _ => Err(ExecutionError::Type(
            "replace() requires string arguments".to_string(),
        )),
    }
}

/// substring(original, start, [length]) - returns a substring
pub fn substring(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() < 2 || args.len() > 3 {
        return Err(ExecutionError::InvalidArgument(
            "substring() requires 2 or 3 arguments".to_string(),
        ));
    }

    match (&args[0], &args[1], args.get(2)) {
        (CypherValue::String(s), CypherValue::Integer(start_val), length) => {
            // Handle negative start (convert to 0)
            let start = (*start_val).max(0) as usize;

            let chars: Vec<char> = s.chars().collect();

            // Handle out of bounds start
            if start >= chars.len() {
                return Ok(CypherValue::String(String::new()));
            }

            let result: String = if let Some(len_arg) = length {
                match len_arg {
                    CypherValue::Integer(len_val) => {
                        // Handle negative length (return empty)
                        let len = (*len_val).max(0) as usize;
                        chars.iter().skip(start).take(len).collect()
                    }
                    CypherValue::Null => return Ok(CypherValue::Null),
                    _ => {
                        return Err(ExecutionError::Type(
                            "substring() length must be an integer".to_string(),
                        ))
                    }
                }
            } else {
                chars.iter().skip(start).collect()
            };

            Ok(CypherValue::String(result))
        }
        (CypherValue::Null, _, _) | (_, CypherValue::Null, _) => Ok(CypherValue::Null),
        _ => Err(ExecutionError::Type(
            "substring() requires (string, integer, integer?)".to_string(),
        )),
    }
}

/// left(string, length) - returns the leftmost n characters
pub fn left(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 2 {
        return Err(ExecutionError::InvalidArgument(
            "left() requires exactly 2 arguments".to_string(),
        ));
    }

    match (&args[0], &args[1]) {
        (CypherValue::String(s), CypherValue::Integer(n)) => {
            // Handle negative or zero count (return empty string)
            if *n <= 0 {
                return Ok(CypherValue::String(String::new()));
            }
            let n = *n as usize;
            let result: String = s.chars().take(n).collect();
            Ok(CypherValue::String(result))
        }
        (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
        _ => Err(ExecutionError::Type(
            "left() requires a string and an integer".to_string(),
        )),
    }
}

/// right(string, length) - returns the rightmost n characters
pub fn right(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 2 {
        return Err(ExecutionError::InvalidArgument(
            "right() requires exactly 2 arguments".to_string(),
        ));
    }

    match (&args[0], &args[1]) {
        (CypherValue::String(s), CypherValue::Integer(n)) => {
            // Handle negative or zero count (return empty string)
            if *n <= 0 {
                return Ok(CypherValue::String(String::new()));
            }
            let n = *n as usize;
            let chars: Vec<char> = s.chars().collect();
            let start = chars.len().saturating_sub(n);
            let result: String = chars.iter().skip(start).collect();
            Ok(CypherValue::String(result))
        }
        (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
        _ => Err(ExecutionError::Type(
            "right() requires a string and an integer".to_string(),
        )),
    }
}

/// split(string, delimiter) - splits a string into a list
pub fn split(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 2 {
        return Err(ExecutionError::InvalidArgument(
            "split() requires exactly 2 arguments".to_string(),
        ));
    }

    match (&args[0], &args[1]) {
        (CypherValue::String(s), CypherValue::String(delim)) => {
            let parts: Vec<CypherValue> = s
                .split(delim.as_str())
                .map(|p| CypherValue::String(p.to_string()))
                .collect();
            Ok(CypherValue::List(parts))
        }
        (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
        _ => Err(ExecutionError::Type(
            "split() requires string arguments".to_string(),
        )),
    }
}

/// reverse(string) - reverses a string
pub fn reverse_string(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "reverse() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::String(s) => Ok(CypherValue::String(s.chars().rev().collect())),
        CypherValue::List(l) => Ok(CypherValue::List(l.iter().rev().cloned().collect())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "reverse() requires a string or list, got {}",
            other.type_name()
        ))),
    }
}

/// startsWith(string, prefix) - checks if string starts with prefix
pub fn starts_with(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 2 {
        return Err(ExecutionError::InvalidArgument(
            "startsWith() requires exactly 2 arguments".to_string(),
        ));
    }

    match (&args[0], &args[1]) {
        (CypherValue::String(s), CypherValue::String(prefix)) => {
            Ok(CypherValue::Boolean(s.starts_with(prefix)))
        }
        (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
        _ => Err(ExecutionError::Type(
            "startsWith() requires string arguments".to_string(),
        )),
    }
}

/// endsWith(string, suffix) - checks if string ends with suffix
pub fn ends_with(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 2 {
        return Err(ExecutionError::InvalidArgument(
            "endsWith() requires exactly 2 arguments".to_string(),
        ));
    }

    match (&args[0], &args[1]) {
        (CypherValue::String(s), CypherValue::String(suffix)) => {
            Ok(CypherValue::Boolean(s.ends_with(suffix)))
        }
        (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
        _ => Err(ExecutionError::Type(
            "endsWith() requires string arguments".to_string(),
        )),
    }
}

/// contains(string, substring) - checks if string contains substring
pub fn contains_string(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 2 {
        return Err(ExecutionError::InvalidArgument(
            "contains() requires exactly 2 arguments".to_string(),
        ));
    }

    match (&args[0], &args[1]) {
        (CypherValue::String(s), CypherValue::String(substring)) => {
            Ok(CypherValue::Boolean(s.contains(substring.as_str())))
        }
        (CypherValue::Null, _) | (_, CypherValue::Null) => Ok(CypherValue::Null),
        _ => Err(ExecutionError::Type(
            "contains() requires string arguments".to_string(),
        )),
    }
}

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

    #[test]
    fn test_to_upper() {
        assert_eq!(
            to_upper(vec![CypherValue::String("hello".to_string())]).unwrap(),
            CypherValue::String("HELLO".to_string())
        );
    }

    #[test]
    fn test_to_lower() {
        assert_eq!(
            to_lower(vec![CypherValue::String("HELLO".to_string())]).unwrap(),
            CypherValue::String("hello".to_string())
        );
    }

    #[test]
    fn test_trim() {
        assert_eq!(
            trim(vec![CypherValue::String("  hello  ".to_string())]).unwrap(),
            CypherValue::String("hello".to_string())
        );
    }

    #[test]
    fn test_substring() {
        assert_eq!(
            substring(vec![
                CypherValue::String("hello".to_string()),
                CypherValue::Integer(1),
                CypherValue::Integer(3)
            ])
            .unwrap(),
            CypherValue::String("ell".to_string())
        );
    }

    #[test]
    fn test_split() {
        assert_eq!(
            split(vec![
                CypherValue::String("a,b,c".to_string()),
                CypherValue::String(",".to_string())
            ])
            .unwrap(),
            CypherValue::List(vec![
                CypherValue::String("a".to_string()),
                CypherValue::String("b".to_string()),
                CypherValue::String("c".to_string()),
            ])
        );
    }

    #[test]
    fn test_reverse_string() {
        // Test string reversal
        assert_eq!(
            reverse_string(vec![CypherValue::String("hello".to_string())]).unwrap(),
            CypherValue::String("olleh".to_string())
        );
    }

    #[test]
    fn test_reverse_list() {
        // Test list reversal
        assert_eq!(
            reverse_string(vec![CypherValue::List(vec![
                CypherValue::Integer(1),
                CypherValue::Integer(2),
                CypherValue::Integer(3),
            ])])
            .unwrap(),
            CypherValue::List(vec![
                CypherValue::Integer(3),
                CypherValue::Integer(2),
                CypherValue::Integer(1),
            ])
        );
    }

    #[test]
    fn test_reverse_null() {
        // Test null handling
        assert_eq!(
            reverse_string(vec![CypherValue::Null]).unwrap(),
            CypherValue::Null
        );
    }
}
