// MoonBit Stream Quality Engine - Core Value Module
// Copyright 2026 zmjknn
// Apache-2.0 License

///|
/// Dynamic Value representation for stream record attributes.
pub(all) enum Value {
  Null
  Bool(Bool)
  Int(Int64)
  Double(Double)
  String(String)
  Bytes(Bytes)
  Timestamp(Int64) // Unix timestamp in milliseconds
  Array(Array[Value])
  Map(Map[String, Value])
} derive(Eq, Debug)

///|
/// Returns true if the Value is Null.
pub fn Value::is_null(self : Value) -> Bool {
  match self {
    Null => true
    _ => false
  }
}

///|
/// Returns true if the Value is a boolean.
pub fn Value::is_bool(self : Value) -> Bool {
  match self {
    Bool(_) => true
    _ => false
  }
}

///|
/// Returns true if the Value is an integer.
pub fn Value::is_int(self : Value) -> Bool {
  match self {
    Int(_) => true
    _ => false
  }
}

///|
/// Returns true if the Value is a double/float.
pub fn Value::is_double(self : Value) -> Bool {
  match self {
    Double(_) => true
    _ => false
  }
}

///|
/// Returns true if the Value is a numeric type (Int or Double).
pub fn Value::is_number(self : Value) -> Bool {
  match self {
    Int(_) | Double(_) => true
    _ => false
  }
}

///|
/// Returns true if the Value is a string.
pub fn Value::is_string(self : Value) -> Bool {
  match self {
    String(_) => true
    _ => false
  }
}

///|
/// Returns true if the Value is a timestamp.
pub fn Value::is_timestamp(self : Value) -> Bool {
  match self {
    Timestamp(_) => true
    _ => false
  }
}

///|
/// Returns true if the Value is an array.
pub fn Value::is_array(self : Value) -> Bool {
  match self {
    Array(_) => true
    _ => false
  }
}

///|
/// Returns true if the Value is a map.
pub fn Value::is_map(self : Value) -> Bool {
  match self {
    Map(_) => true
    _ => false
  }
}

///|
/// Converts the value to a Double representation if possible.
pub fn Value::as_double(self : Value) -> Double? {
  match self {
    Int(i) => Some(i.to_double())
    Double(d) => Some(d)
    String(s) => Some(@string.parse_double(s)) catch { _ => None }
    _ => None
  }
}

///|
/// Converts the value to Int64 if possible.
pub fn Value::as_int(self : Value) -> Int64? {
  match self {
    Int(i) => Some(i)
    Double(d) => Some(d.to_int64())
    String(s) => Some(@string.parse_int64(s)) catch { _ => None }
    Timestamp(ts) => Some(ts)
    _ => None
  }
}

///|
/// Converts the value to String representation.
pub fn Value::as_string(self : Value) -> String? {
  match self {
    String(s) => Some(s)
    Int(i) => Some(i.to_string())
    Double(d) => Some(d.to_string())
    Bool(b) => Some(b.to_string())
    Timestamp(ts) => Some(ts.to_string())
    _ => None
  }
}

///|
/// Converts the value to a boolean if possible.
pub fn Value::as_bool(self : Value) -> Bool? {
  match self {
    Bool(b) => Some(b)
    Int(i) => Some(i != 0L)
    String(s) =>
      if s == "true" || s == "1" || s == "TRUE" {
        Some(true)
      } else if s == "false" || s == "0" || s == "FALSE" {
        Some(false)
      } else {
        None
      }
    _ => None
  }
}

///|
/// Human-readable type name of the value.
pub fn Value::type_name(self : Value) -> String {
  match self {
    Null => "Null"
    Bool(_) => "Bool"
    Int(_) => "Int"
    Double(_) => "Double"
    String(_) => "String"
    Bytes(_) => "Bytes"
    Timestamp(_) => "Timestamp"
    Array(_) => "Array"
    Map(_) => "Map"
  }
}

///|
/// Formats the Value into string representation.
pub fn Value::to_string(self : Value) -> String {
  match self {
    Null => "null"
    Bool(b) => b.to_string()
    Int(i) => i.to_string()
    Double(d) => d.to_string()
    String(s) => "\"" + s + "\""
    Bytes(_) => ""
    Timestamp(ts) => ts.to_string()
    Array(arr) => {
      let mut res = "["
      for i = 0; i < arr.length(); i = i + 1 {
        if i > 0 {
          res = res + ", "
        }
        res = res + arr[i].to_string()
      }
      res + "]"
    }
    Map(m) => {
      let mut res = "{"
      let mut first = true
      m.each(fn(k, v) {
        if !first {
          res = res + ", "
        }
        res = res + "\"" + k + "\": " + v.to_string()
        first = false
      })
      res + "}"
    }
  }
}

///|
/// Compares two values for ordering when supported.
pub fn Value::compare(self : Value, other : Value) -> Int? {
  match (self, other) {
    (Int(a), Int(b)) =>
      if a < b {
        Some(-1)
      } else if a > b {
        Some(1)
      } else {
        Some(0)
      }
    (Double(a), Double(b)) =>
      if a < b {
        Some(-1)
      } else if a > b {
        Some(1)
      } else {
        Some(0)
      }
    (Int(a), Double(b)) => {
      let ad = a.to_double()
      if ad < b {
        Some(-1)
      } else if ad > b {
        Some(1)
      } else {
        Some(0)
      }
    }
    (Double(a), Int(b)) => {
      let bd = b.to_double()
      if a < bd {
        Some(-1)
      } else if a > bd {
        Some(1)
      } else {
        Some(0)
      }
    }
    (String(a), String(b)) =>
      if a < b {
        Some(-1)
      } else if a > b {
        Some(1)
      } else {
        Some(0)
      }
    (Timestamp(a), Timestamp(b)) =>
      if a < b {
        Some(-1)
      } else if a > b {
        Some(1)
      } else {
        Some(0)
      }
    _ => None
  }
}