///|
/// Render validation diagnostics as newline separated text.
pub fn render_diagnostics(diags : Array[Diagnostic]) -> String {
  if diags.is_empty() {
    return "No validation findings.\n"
  }
  let mut out = ""
  for i = 0; i < diags.length(); i = i + 1 {
    let d = diags[i]
    out = out +
      severity_text(d.severity) +
      " " +
      d.code +
      " " +
      d.path +
      " - " +
      d.message +
      "\n"
  }
  out
}

///|
pub fn severity_text(severity : Severity) -> String {
  match severity {
    Info => "INFO"
    Warning => "WARN"
    Error => "ERROR"
  }
}

///|
/// Render a compact human readable summary.
pub fn render_summary(summary : HarSummary) -> String {
  let mut out = ""
  out = out + "MoonHAR summary\n"
  out = out + "entries: " + summary.entry_count.to_string() + "\n"
  out = out + "pages: " + summary.page_count.to_string() + "\n"
  out = out + "failed: " + summary.failed_count.to_string() + "\n"
  out = out + "redirected: " + summary.redirected_count.to_string() + "\n"
  out = out + "cached: " + summary.cached_count.to_string() + "\n"
  out = out + "bytes: " + summary.total_bytes.to_string() + "\n"
  out = out + "total_ms: " + summary.total_time.to_string() + "\n"
  out = out + "avg_ms: " + summary.average_time.to_string() + "\n"
  out = out + "longest_ms: " + summary.longest_time.to_string() + "\n"
  out = out + "\nDomains\n"
  for i = 0; i < summary.domains.length(); i = i + 1 {
    let d = summary.domains[i]
    out = out +
      "- " +
      d.domain +
      ": count=" +
      d.count.to_string() +
      " bytes=" +
      d.bytes.to_string() +
      " failures=" +
      d.failures.to_string() +
      "\n"
  }
  out = out + "\nStatus groups\n"
  for i = 0; i < summary.statuses.length(); i = i + 1 {
    let s = summary.statuses[i]
    out = out + "- " + s.group + ": " + s.count.to_string() + "\n"
  }
  out = out + "\nResources\n"
  for i = 0; i < summary.resources.length(); i = i + 1 {
    let r = summary.resources[i]
    out = out +
      "- " +
      r.kind +
      ": count=" +
      r.count.to_string() +
      " bytes=" +
      r.bytes.to_string() +
      "\n"
  }
  out = out + "\nSlow entries\n"
  for i = 0; i < summary.slow_entries.length(); i = i + 1 {
    let e = summary.slow_entries[i]
    out = out +
      "- " +
      e.time.to_string() +
      "ms " +
      e.http_method +
      " " +
      e.url +
      " status=" +
      e.status.to_string() +
      "\n"
  }
  out
}

///|
/// Render a compact JSON-like report without depending on a JSON encoder.
pub fn render_summary_json(summary : HarSummary) -> String {
  let mut out = "{"
  out = out + "\"entryCount\":" + summary.entry_count.to_string() + ","
  out = out + "\"pageCount\":" + summary.page_count.to_string() + ","
  out = out + "\"failedCount\":" + summary.failed_count.to_string() + ","
  out = out +
    "\"redirectedCount\":" +
    summary.redirected_count.to_string() +
    ","
  out = out + "\"cachedCount\":" + summary.cached_count.to_string() + ","
  out = out + "\"totalBytes\":" + summary.total_bytes.to_string() + ","
  out = out + "\"averageTime\":" + summary.average_time.to_string() + ","
  out = out + "\"domains\":["
  for i = 0; i < summary.domains.length(); i = i + 1 {
    if i > 0 {
      out = out + ","
    }
    let d = summary.domains[i]
    out = out +
      "{\"domain\":\"" +
      escape_json(d.domain) +
      "\",\"count\":" +
      d.count.to_string() +
      ",\"bytes\":" +
      d.bytes.to_string() +
      ",\"failures\":" +
      d.failures.to_string() +
      "}"
  }
  out = out + "],\"resources\":["
  for i = 0; i < summary.resources.length(); i = i + 1 {
    if i > 0 {
      out = out + ","
    }
    let r = summary.resources[i]
    out = out +
      "{\"kind\":\"" +
      escape_json(r.kind) +
      "\",\"count\":" +
      r.count.to_string() +
      ",\"bytes\":" +
      r.bytes.to_string() +
      "}"
  }
  out = out + "]}"
  out
}

///|
pub fn escape_json(text : String) -> String {
  let mut out = ""
  for i = 0; i < text.length(); i = i + 1 {
    match text.get_char(i) {
      Some('"') => out = out + "\\\""
      Some('\\') => out = out + "\\\\"
      Some('\n') => out = out + "\\n"
      Some('\r') => out = out + "\\r"
      Some('\t') => out = out + "\\t"
      Some(ch) => out = out + ch.to_string()
      None => ()
    }
  }
  out
}