///|
pub(all) struct FindingSummary {
  total : Int
  critical : Int
  high : Int
  medium : Int
  low : Int
  info : Int
} derive(Eq, Debug)

///|
pub fn summarize(findings : Array[Finding]) -> FindingSummary {
  let mut critical = 0
  let mut high = 0
  let mut medium = 0
  let mut low = 0
  let mut info = 0
  for finding in findings {
    match finding.severity {
      Critical => critical = critical + 1
      High => high = high + 1
      Medium => medium = medium + 1
      Low => low = low + 1
      Info => info = info + 1
    }
  }
  { total: findings.length(), critical, high, medium, low, info }
}

///|
fn json_escape(text : String) -> String {
  let out = StringBuilder::new()
  for c in text {
    match c {
      '"' => out.write_string("\\\"")
      '\\' => out.write_string("\\\\")
      '\n' => out.write_string("\\n")
      '\r' => out.write_string("\\r")
      '\t' => out.write_string("\\t")
      _ => out.write_char(c)
    }
  }
  out.to_string()
}

///|
pub fn findings_to_json(findings : Array[Finding]) -> String {
  let out = StringBuilder::new()
  out.write_string("{\"findings\":[")
  for i = 0; i < findings.length(); i = i + 1 {
    if i > 0 {
      out.write_char(',')
    }
    let finding = findings[i]
    out.write_string("{\"detector\":\"")
    out.write_string(json_escape(finding.detector_id))
    out.write_string("\",\"kind\":\"")
    out.write_string(finding.kind.name())
    out.write_string("\",\"severity\":\"")
    out.write_string(finding.severity.name())
    out.write_string("\",\"start\":")
    out.write_string(finding.span.start.to_string())
    out.write_string(",\"end\":")
    out.write_string(finding.span.end.to_string())
    out.write_string(",\"confidence\":")
    out.write_string(finding.confidence.to_string())
    out.write_string(",\"preview\":\"")
    out.write_string(json_escape(finding.preview))
    out.write_string("\",\"message\":\"")
    out.write_string(json_escape(finding.message))
    out.write_string("\"}")
  }
  out.write_string("]}")
  out.to_string()
}