///|
/// Encode a graph as stable, human-readable JSON using MoonBit's standard
/// JSON representation. The result can be stored or exchanged with tools.
pub fn graph_to_json(graph : Graph) -> String {
  ToJson::to_json(graph).stringify(indent=2)
}

///|
/// Decode a graph from JSON. Parse and schema errors are propagated to callers.
pub fn graph_from_json(source : String) -> Graph raise {
  @json.from_json(@json.parse(source))
}

///|
/// Return all validation findings instead of only a boolean result.
pub fn validate_graph(graph : Graph) -> Array[String] {
  let findings : Array[String] = []
  if graph.nodes.length() == 0 {
    findings.push("graph must contain at least one node")
  }
  let mut node_index = 0
  while node_index < graph.nodes.length() {
    let node = graph.nodes[node_index]
    if node.id != node_index {
      findings.push("node ids must be contiguous and match array indexes")
    }
    if node.label.length() == 0 {
      findings.push("node labels must not be empty")
    }
    node_index = node_index + 1
  }
  let mut edge_index = 0
  while edge_index < graph.edges.length() {
    let edge = graph.edges[edge_index]
    if edge.from < 0 || edge.from >= graph.nodes.length() {
      findings.push("edge source is outside the node array")
    }
    if edge.to < 0 || edge.to >= graph.nodes.length() {
      findings.push("edge destination is outside the node array")
    }
    if edge.meters <= 0 {
      findings.push("edge distance must be positive")
    }
    if edge.width_cm <= 0 {
      findings.push("edge width must be positive")
    }
    if edge.lighting < 0 || edge.lighting > 5 {
      findings.push("edge lighting must be between 0 and 5")
    }
    if edge.slope_percent < 0 {
      findings.push("edge slope must not be negative")
    }
    if edge.steps < 0 || edge.crossing < 0 {
      findings.push("edge steps and crossing counts must not be negative")
    }
    edge_index = edge_index + 1
  }
  findings
}

///|
pub fn graph_json_is_valid(source : String) -> Bool {
  try {
    let graph = graph_from_json(source)
    validate_graph(graph).length() == 0
  } catch {
    _ => false
  }
}