use serde_json::Value;

pub fn is_integer(v: &Value) -> bool {
  match v {
    Value::Number(n) => {
      n.is_i64() || n.is_u64() || n.as_f64().filter(|n| n.fract() == 0.0).is_some()
    }
    _ => false,
  }
}

/// serde_json treats 0 and 0.0 not equal. so we cannot simply use v1==v2
pub fn equals(v1: &Value, v2: &Value) -> bool {
  match (v1, v2) {
    (Value::Null, Value::Null) => true,
    (Value::Bool(b1), Value::Bool(b2)) => b1 == b2,
    (Value::Number(n1), Value::Number(n2)) => {
      if let (Some(n1), Some(n2)) = (n1.as_u64(), n2.as_u64()) {
        return n1 == n2;
      }
      if let (Some(n1), Some(n2)) = (n1.as_i64(), n2.as_i64()) {
        return n1 == n2;
      }
      if let (Some(n1), Some(n2)) = (n1.as_f64(), n2.as_f64()) {
        return (n1 - n2).abs() < f64::EPSILON;
      }
      false
    }
    (Value::String(s1), Value::String(s2)) => s1 == s2,
    (Value::Array(arr1), Value::Array(arr2)) => {
      if arr1.len() != arr2.len() {
        return false;
      }
      arr1.iter().zip(arr2).all(|(e1, e2)| equals(e1, e2))
    }
    (Value::Object(obj1), Value::Object(obj2)) => {
      if obj1.len() != obj2.len() {
        return false;
      }
      for (k1, v1) in obj1 {
        if let Some(v2) = obj2.get(k1) {
          if !equals(v1, v2) {
            return false;
          }
        } else {
          return false;
        }
      }
      true
    }
    _ => false,
  }
}
