//! List functions.

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

/// size(list) or size(string) - returns the size
pub fn size(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "size() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::List(l) => Ok(CypherValue::Integer(l.len() as i64)),
        CypherValue::String(s) => Ok(CypherValue::Integer(s.chars().count() as i64)),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "size() requires a list or string, got {}",
            other.type_name()
        ))),
    }
}

/// length(path) - returns the length of a path (number of relationships)
pub fn length(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "length() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::Path(p) => Ok(CypherValue::Integer(p.length() as i64)),
        CypherValue::String(s) => Ok(CypherValue::Integer(s.chars().count() as i64)),
        CypherValue::List(l) => Ok(CypherValue::Integer(l.len() as i64)),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "length() requires a path, string, or list, got {}",
            other.type_name()
        ))),
    }
}

/// head(list) - returns the first element
pub fn head(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "head() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::List(l) => Ok(l.first().cloned().unwrap_or(CypherValue::Null)),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "head() requires a list, got {}",
            other.type_name()
        ))),
    }
}

/// tail(list) - returns all but the first element
pub fn tail(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "tail() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::List(l) => {
            if l.is_empty() {
                Ok(CypherValue::List(vec![]))
            } else {
                Ok(CypherValue::List(l[1..].to_vec()))
            }
        }
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "tail() requires a list, got {}",
            other.type_name()
        ))),
    }
}

/// last(list) - returns the last element
pub fn last(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "last() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::List(l) => Ok(l.last().cloned().unwrap_or(CypherValue::Null)),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "last() requires a list, got {}",
            other.type_name()
        ))),
    }
}

/// range(start, end, [step]) - creates a list of integers
pub fn range(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() < 2 || args.len() > 3 {
        return Err(ExecutionError::InvalidArgument(
            "range() requires 2 or 3 arguments".to_string(),
        ));
    }

    let start = match &args[0] {
        CypherValue::Integer(i) => *i,
        CypherValue::Null => return Ok(CypherValue::Null),
        other => {
            return Err(ExecutionError::Type(format!(
                "range() requires integers, got {}",
                other.type_name()
            )))
        }
    };

    let end = match &args[1] {
        CypherValue::Integer(i) => *i,
        CypherValue::Null => return Ok(CypherValue::Null),
        other => {
            return Err(ExecutionError::Type(format!(
                "range() requires integers, got {}",
                other.type_name()
            )))
        }
    };

    let step = if args.len() == 3 {
        match &args[2] {
            CypherValue::Integer(i) => *i,
            CypherValue::Null => return Ok(CypherValue::Null),
            other => {
                return Err(ExecutionError::Type(format!(
                    "range() requires integers, got {}",
                    other.type_name()
                )))
            }
        }
    } else {
        1
    };

    if step == 0 {
        return Err(ExecutionError::InvalidArgument(
            "range() step cannot be 0".to_string(),
        ));
    }

    let mut result = Vec::new();
    let mut current = start;

    if step > 0 {
        while current <= end {
            result.push(CypherValue::Integer(current));
            current += step;
        }
    } else {
        while current >= end {
            result.push(CypherValue::Integer(current));
            current += step;
        }
    }

    Ok(CypherValue::List(result))
}

/// nodes(path) - returns the nodes in a path
pub fn nodes(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "nodes() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::Path(p) => Ok(CypherValue::List(
            p.nodes.iter().cloned().map(CypherValue::Node).collect(),
        )),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "nodes() requires a path, got {}",
            other.type_name()
        ))),
    }
}

/// relationships(path) - returns the relationships in a path
pub fn relationships(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 1 {
        return Err(ExecutionError::InvalidArgument(
            "relationships() requires exactly 1 argument".to_string(),
        ));
    }

    match &args[0] {
        CypherValue::Path(p) => Ok(CypherValue::List(
            p.relationships
                .iter()
                .cloned()
                .map(CypherValue::Relationship)
                .collect(),
        )),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "relationships() requires a path, got {}",
            other.type_name()
        ))),
    }
}

/// extract(variable IN list | expression) - extract values from list
/// Note: This is implemented via list comprehension syntax in the parser/evaluator
pub fn extract(_args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    Err(ExecutionError::InvalidArgument(
        "extract() is implemented via list comprehension syntax: [x IN list | expression]".to_string()
    ))
}

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

    match &args[0] {
        CypherValue::List(items) => {
            let mut reversed = items.clone();
            reversed.reverse();
            Ok(CypherValue::List(reversed))
        }
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "reverse() requires a list, got {}",
            other.type_name()
        ))),
    }
}

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

    #[test]
    fn test_size() {
        assert_eq!(
            size(vec![CypherValue::List(vec![
                CypherValue::Integer(1),
                CypherValue::Integer(2),
                CypherValue::Integer(3),
            ])])
            .unwrap(),
            CypherValue::Integer(3)
        );

        assert_eq!(
            size(vec![CypherValue::String("hello".to_string())]).unwrap(),
            CypherValue::Integer(5)
        );
    }

    #[test]
    fn test_head_tail_last() {
        let list = vec![CypherValue::List(vec![
            CypherValue::Integer(1),
            CypherValue::Integer(2),
            CypherValue::Integer(3),
        ])];

        assert_eq!(head(list.clone()).unwrap(), CypherValue::Integer(1));
        assert_eq!(last(list.clone()).unwrap(), CypherValue::Integer(3));
        assert_eq!(
            tail(list).unwrap(),
            CypherValue::List(vec![CypherValue::Integer(2), CypherValue::Integer(3)])
        );
    }

    #[test]
    fn test_range() {
        assert_eq!(
            range(vec![CypherValue::Integer(1), CypherValue::Integer(5)]).unwrap(),
            CypherValue::List(vec![
                CypherValue::Integer(1),
                CypherValue::Integer(2),
                CypherValue::Integer(3),
                CypherValue::Integer(4),
                CypherValue::Integer(5),
            ])
        );

        assert_eq!(
            range(vec![
                CypherValue::Integer(0),
                CypherValue::Integer(10),
                CypherValue::Integer(2)
            ])
            .unwrap(),
            CypherValue::List(vec![
                CypherValue::Integer(0),
                CypherValue::Integer(2),
                CypherValue::Integer(4),
                CypherValue::Integer(6),
                CypherValue::Integer(8),
                CypherValue::Integer(10),
            ])
        );
    }

    #[test]
    fn test_extract_returns_error() {
        // extract() should return an error directing users to list comprehension syntax
        let result = extract(vec![]);
        assert!(result.is_err());
        assert!(result.unwrap_err().to_string().contains("list comprehension"));
    }
}
