//! Miscellaneous operations: type, not, empty

use simd_json::OwnedValue as Value;
use simd_json::StaticNode;

pub fn eval_type(value: &Value) -> Value {
    let type_str = match value {
        Value::Static(StaticNode::Null) => "null",
        Value::Static(StaticNode::Bool(_)) => "boolean",
        Value::Static(StaticNode::I64(_) | StaticNode::U64(_) | StaticNode::F64(_)) => "number",
        Value::String(_) => "string",
        Value::Array(_) => "array",
        Value::Object(_) => "object",
    };
    Value::String(type_str.to_string())
}

pub fn eval_not(value: &Value) -> Value {
    // In jq, falsy values are: null and false
    let is_falsy = matches!(
        value,
        Value::Static(StaticNode::Null) | Value::Static(StaticNode::Bool(false))
    );
    Value::Static(StaticNode::Bool(is_falsy))
}

#[cfg(test)]
mod tests {
    use crate::filter::builtins::{Builtin, eval};
    use simd_json::{OwnedValue as Value, json};

    // type tests
    #[test]
    fn test_type_null() {
        assert_eq!(
            eval(&Builtin::Type, &json!(null)).unwrap(),
            vec![json!("null")]
        );
    }

    #[test]
    fn test_type_boolean() {
        assert_eq!(
            eval(&Builtin::Type, &json!(true)).unwrap(),
            vec![json!("boolean")]
        );
        assert_eq!(
            eval(&Builtin::Type, &json!(false)).unwrap(),
            vec![json!("boolean")]
        );
    }

    #[test]
    fn test_type_number() {
        assert_eq!(
            eval(&Builtin::Type, &json!(42)).unwrap(),
            vec![json!("number")]
        );
        assert_eq!(
            eval(&Builtin::Type, &json!(3.15)).unwrap(),
            vec![json!("number")]
        );
    }

    #[test]
    fn test_type_string() {
        assert_eq!(
            eval(&Builtin::Type, &json!("hello")).unwrap(),
            vec![json!("string")]
        );
    }

    #[test]
    fn test_type_array() {
        assert_eq!(
            eval(&Builtin::Type, &json!([1, 2, 3])).unwrap(),
            vec![json!("array")]
        );
    }

    #[test]
    fn test_type_object() {
        assert_eq!(
            eval(&Builtin::Type, &json!({"a": 1})).unwrap(),
            vec![json!("object")]
        );
    }

    // empty tests
    #[test]
    fn test_empty() {
        let empty: Vec<Value> = vec![];
        assert_eq!(
            eval(&Builtin::Empty, &json!({"anything": "here"})).unwrap(),
            empty
        );
        assert_eq!(eval(&Builtin::Empty, &json!(null)).unwrap(), empty);
    }

    // not tests
    #[test]
    fn test_not_null() {
        assert_eq!(
            eval(&Builtin::Not, &json!(null)).unwrap(),
            vec![json!(true)]
        );
    }

    #[test]
    fn test_not_false() {
        assert_eq!(
            eval(&Builtin::Not, &json!(false)).unwrap(),
            vec![json!(true)]
        );
    }

    #[test]
    fn test_not_true() {
        assert_eq!(
            eval(&Builtin::Not, &json!(true)).unwrap(),
            vec![json!(false)]
        );
    }

    #[test]
    fn test_not_truthy_values() {
        assert_eq!(eval(&Builtin::Not, &json!(0)).unwrap(), vec![json!(false)]);
        assert_eq!(eval(&Builtin::Not, &json!("")).unwrap(), vec![json!(false)]);
        assert_eq!(eval(&Builtin::Not, &json!([])).unwrap(), vec![json!(false)]);
        assert_eq!(eval(&Builtin::Not, &json!({})).unwrap(), vec![json!(false)]);
    }
}
