///|
/// Render a concise report for an analyst or CI log.
pub fn render_summary(report : AuditReport) -> String {
  let builder = StringBuilder()
  builder.write_string("MoonMIME security audit\n")
  builder.write_string(
    "risk-score: " + report.risk_score.to_string() + "/100\n",
  )
  builder.write_string("high: " + report.high.to_string() + "\n")
  builder.write_string("medium: " + report.medium.to_string() + "\n")
  builder.write_string("low: " + report.low.to_string() + "\n")
  builder.write_string("info: " + report.info.to_string() + "\n")
  for finding in report.findings {
    builder.write_string(
      "[" +
      finding.severity.to_string() +
      "] " +
      finding.code.to_code() +
      " entity=" +
      render_path(finding.entity_path) +
      " bytes=" +
      finding.evidence_range.start.to_string() +
      ".." +
      finding.evidence_range.end.to_string() +
      " " +
      finding.message +
      "\n",
    )
  }
  builder.to_string()
}

///|
/// Render the stable `moonmime.audit.v1` exchange format.
pub fn render_json(report : AuditReport) -> String {
  let builder = StringBuilder()
  builder.write_string("{\"schema\":\"moonmime.audit.v1\",\"risk_score\":")
  builder.write_string(report.risk_score.to_string())
  builder.write_string(",\"counts\":{\"high\":")
  builder.write_string(report.high.to_string())
  builder.write_string(",\"medium\":")
  builder.write_string(report.medium.to_string())
  builder.write_string(",\"low\":")
  builder.write_string(report.low.to_string())
  builder.write_string(",\"info\":")
  builder.write_string(report.info.to_string())
  builder.write_string("},\"findings\":[")
  for index in 0.. 0 {
      builder.write_char(',')
    }
    let finding = report.findings[index]
    builder.write_string("{\"code\":\"")
    builder.write_string(finding.code.to_code())
    builder.write_string("\",\"severity\":\"")
    builder.write_string(finding.severity.to_string())
    builder.write_string("\",\"entity_path\":[")
    for component_index in 0.. 0 {
        builder.write_char(',')
      }
      builder.write_string(finding.entity_path[component_index].to_string())
    }
    builder.write_string("],\"start\":")
    builder.write_string(finding.evidence_range.start.to_string())
    builder.write_string(",\"end\":")
    builder.write_string(finding.evidence_range.end.to_string())
    builder.write_string(",\"message\":\"")
    write_json_text(builder, finding.message)
    builder.write_string("\"}")
  }
  builder.write_string("]}")
  builder.to_string()
}

///|
fn render_path(path : Array[Int]) -> String {
  if path.is_empty() {
    return "."
  }
  let builder = StringBuilder()
  for index in 0.. 0 {
      builder.write_char('.')
    }
    builder.write_string(path[index].to_string())
  }
  builder.to_string()
}

///|
fn write_json_text(builder : StringBuilder, value : String) -> Unit {
  for byte in @utf8.encode(value) {
    match byte {
      b'"' => builder.write_string("\\\"")
      b'\\' => builder.write_string("\\\\")
      b'\n' => builder.write_string("\\n")
      b'\r' => builder.write_string("\\r")
      b'\t' => builder.write_string("\\t")
      _ => builder.write_char(byte.to_int().unsafe_to_char())
    }
  }
}