///|
/// Convert JsonValue to JSON string
pub fn stringify(value : JsonValue) -> String {
  let buf = @buffer.new()
  write_value(buf, value)
  buf.to_string()
}

///|
/// Write JSON value to buffer
fn write_value(buf : @buffer.Buffer, value : JsonValue) -> Unit {
  match value {
    Null => buf.write_string("null")
    Bool(true) => buf.write_string("true")
    Bool(false) => buf.write_string("false")
    Number(n) =>
      // Handle special cases
      if n.is_nan() {
        buf.write_string("null")
      } else if n.is_inf() {
        buf.write_string("null")
      } else {
        buf.write_string(n.to_string())
      }
    String(s) => write_string(buf, s)
    Array(arr) => write_array(buf, arr)
    Object(obj) => write_object(buf, obj)
  }
}

///|
/// Write JSON string with escaping
fn write_string(buf : @buffer.Buffer, s : String) -> Unit {
  buf.write_string("\"")
  for ch in s {
    match ch {
      '"' => buf.write_string("\\\"")
      '\\' => buf.write_string("\\\\")
      '\b' => buf.write_string("\\b")
      '\u000C' => buf.write_string("\\f")
      '\n' => buf.write_string("\\n")
      '\r' => buf.write_string("\\r")
      '\t' => buf.write_string("\\t")
      ch if ch >= '\u0000' && ch <= '\u001F' => {
        // Control characters as unicode escape
        buf.write_string("\\u00")
        let code = ch.to_int()
        let hex = "0123456789abcdef"
        buf.write_char(hex.get_char(code / 16).unwrap())
        buf.write_char(hex.get_char(code % 16).unwrap())
      }
      ch => buf.write_char(ch)
    }
  }
  buf.write_string("\"")
}

///|
/// Write JSON array
fn write_array(buf : @buffer.Buffer, arr : Array[JsonValue]) -> Unit {
  buf.write_string("[")
  for i, value in arr {
    if i > 0 {
      buf.write_string(",")
    }
    write_value(buf, value)
  }
  buf.write_string("]")
}

///|
/// Write JSON object
fn write_object(buf : @buffer.Buffer, obj : Map[String, JsonValue]) -> Unit {
  buf.write_string("{")
  let mut first = true
  for key, value in obj {
    if not(first) {
      buf.write_string(",")
    }
    first = false
    write_string(buf, key)
    buf.write_string(":")
    write_value(buf, value)
  }
  buf.write_string("}")
}

///|
/// Pretty print with indentation
pub fn stringify_pretty(value : JsonValue, indent : Int) -> String {
  let buf = @buffer.new()
  write_value_pretty(buf, value, 0, indent)
  buf.to_string()
}

///|
/// Write JSON value with pretty printing
fn write_value_pretty(
  buf : @buffer.Buffer,
  value : JsonValue,
  depth : Int,
  indent : Int,
) -> Unit {
  match value {
    Null => buf.write_string("null")
    Bool(true) => buf.write_string("true")
    Bool(false) => buf.write_string("false")
    Number(n) =>
      if n.is_nan() || n.is_inf() {
        buf.write_string("null")
      } else {
        buf.write_string(n.to_string())
      }
    String(s) => write_string(buf, s)
    Array(arr) => write_array_pretty(buf, arr, depth, indent)
    Object(obj) => write_object_pretty(buf, obj, depth, indent)
  }
}

///|
/// Write indentation
fn write_indent(buf : @buffer.Buffer, depth : Int, indent : Int) -> Unit {
  for _i = 0; _i < depth * indent; _i = _i + 1 {
    buf.write_string(" ")
  }
}

///|
/// Write JSON array with pretty printing
fn write_array_pretty(
  buf : @buffer.Buffer,
  arr : Array[JsonValue],
  depth : Int,
  indent : Int,
) -> Unit {
  buf.write_string("[")
  if arr.is_empty() {
    buf.write_string("]")
    return
  }
  buf.write_string("\n")
  for i, value in arr {
    write_indent(buf, depth + 1, indent)
    write_value_pretty(buf, value, depth + 1, indent)
    if i < arr.length() - 1 {
      buf.write_string(",")
    }
    buf.write_string("\n")
  }
  write_indent(buf, depth, indent)
  buf.write_string("]")
}

///|
/// Write JSON object with pretty printing
fn write_object_pretty(
  buf : @buffer.Buffer,
  obj : Map[String, JsonValue],
  depth : Int,
  indent : Int,
) -> Unit {
  buf.write_string("{")
  let keys : Array[String] = []
  for key, _ in obj {
    keys.push(key)
  }
  if keys.is_empty() {
    buf.write_string("}")
    return
  }
  buf.write_string("\n")
  for i, key in keys {
    write_indent(buf, depth + 1, indent)
    write_string(buf, key)
    buf.write_string(": ")
    write_value_pretty(buf, obj[key], depth + 1, indent)
    if i < keys.length() - 1 {
      buf.write_string(",")
    }
    buf.write_string("\n")
  }
  write_indent(buf, depth, indent)
  buf.write_string("}")
}