//! Math functions.

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

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Integer(i.abs())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.abs())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "abs() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Integer(*i)),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.ceil())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "ceil() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Integer(*i)),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.floor())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "floor() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Integer(*i)),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.round())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "round() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => {
            let s = if *i > 0 { 1 } else if *i < 0 { -1 } else { 0 };
            Ok(CypherValue::Integer(s))
        }
        CypherValue::Float(f) => {
            let s = if *f > 0.0 {
                1.0
            } else if *f < 0.0 {
                -1.0
            } else {
                0.0
            };
            Ok(CypherValue::Float(s))
        }
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "sign() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).sqrt())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.sqrt())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "sqrt() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).ln())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.ln())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "log() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).log10())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.log10())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "log10() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).exp())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.exp())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "exp() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).sin())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.sin())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "sin() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).cos())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.cos())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "cos() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).tan())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.tan())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "tan() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).asin())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.asin())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "asin() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).acos())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.acos())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "acos() requires a number, got {}",
            other.type_name()
        ))),
    }
}

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

    match &args[0] {
        CypherValue::Integer(i) => Ok(CypherValue::Float((*i as f64).atan())),
        CypherValue::Float(f) => Ok(CypherValue::Float(f.atan())),
        CypherValue::Null => Ok(CypherValue::Null),
        other => Err(ExecutionError::Type(format!(
            "atan() requires a number, got {}",
            other.type_name()
        ))),
    }
}

/// atan2(y, x) - returns the arctangent of y/x
pub fn atan2(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if args.len() != 2 {
        return Err(ExecutionError::InvalidArgument(
            "atan2() requires exactly 2 arguments".to_string(),
        ));
    }

    let y = match &args[0] {
        CypherValue::Integer(i) => *i as f64,
        CypherValue::Float(f) => *f,
        CypherValue::Null => return Ok(CypherValue::Null),
        other => {
            return Err(ExecutionError::Type(format!(
                "atan2() requires numbers, got {}",
                other.type_name()
            )))
        }
    };

    let x = match &args[1] {
        CypherValue::Integer(i) => *i as f64,
        CypherValue::Float(f) => *f,
        CypherValue::Null => return Ok(CypherValue::Null),
        other => {
            return Err(ExecutionError::Type(format!(
                "atan2() requires numbers, got {}",
                other.type_name()
            )))
        }
    };

    Ok(CypherValue::Float(y.atan2(x)))
}

/// pi() - returns pi
pub fn pi(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if !args.is_empty() {
        return Err(ExecutionError::InvalidArgument(
            "pi() takes no arguments".to_string(),
        ));
    }
    Ok(CypherValue::Float(std::f64::consts::PI))
}

/// e() - returns e (Euler's number)
pub fn e(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if !args.is_empty() {
        return Err(ExecutionError::InvalidArgument(
            "e() takes no arguments".to_string(),
        ));
    }
    Ok(CypherValue::Float(std::f64::consts::E))
}

/// rand() - returns a random number between 0 and 1
pub fn rand(args: Vec<CypherValue>) -> ExecutionResult<CypherValue> {
    if !args.is_empty() {
        return Err(ExecutionError::InvalidArgument(
            "rand() takes no arguments".to_string(),
        ));
    }
    // Simple pseudo-random using system time
    use std::time::{SystemTime, UNIX_EPOCH};
    let seed = SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_nanos())
        .unwrap_or(0);
    let random = ((seed * 1103515245 + 12345) % (1 << 31)) as f64 / (1u64 << 31) as f64;
    Ok(CypherValue::Float(random))
}

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

    #[test]
    fn test_abs() {
        assert_eq!(
            abs(vec![CypherValue::Integer(-5)]).unwrap(),
            CypherValue::Integer(5)
        );
        assert_eq!(
            abs(vec![CypherValue::Float(-3.5)]).unwrap(),
            CypherValue::Float(3.5)
        );
    }

    #[test]
    fn test_ceil_floor_round() {
        assert_eq!(
            ceil(vec![CypherValue::Float(3.2)]).unwrap(),
            CypherValue::Float(4.0)
        );
        assert_eq!(
            floor(vec![CypherValue::Float(3.8)]).unwrap(),
            CypherValue::Float(3.0)
        );
        assert_eq!(
            round(vec![CypherValue::Float(3.5)]).unwrap(),
            CypherValue::Float(4.0)
        );
    }

    #[test]
    fn test_sqrt() {
        assert_eq!(
            sqrt(vec![CypherValue::Integer(16)]).unwrap(),
            CypherValue::Float(4.0)
        );
    }

    #[test]
    fn test_pi_e() {
        if let CypherValue::Float(p) = pi(vec![]).unwrap() {
            assert!((p - std::f64::consts::PI).abs() < 1e-10);
        }
        if let CypherValue::Float(e_val) = e(vec![]).unwrap() {
            assert!((e_val - std::f64::consts::E).abs() < 1e-10);
        }
    }
}
