///|
fn append_if_some(target : Array[Finding], item : Finding?) -> Unit {
  match item {
    Some(value) => target.push(value)
    None => ()
  }
}

///|
pub fn inspect(
  observations : Array[Observation],
  config : RuleConfig,
) -> QcSummary {
  let findings : Array[Finding] = []
  for i, observation in observations {
    append_if_some(findings, check_missing(observation, config))
    append_if_some(findings, check_range(observation, config))
    append_if_some(findings, check_climate(observation, config))
    if i > 0 {
      let previous = observations[i - 1]
      append_if_some(findings, check_delta(previous, observation, config))
      append_if_some(findings, check_interval(previous, observation, config))
      append_if_some(findings, check_spatial(previous, observation, config))
      append_if_some(findings, check_duplicate(previous, observation, config))
    }
  }
  let bad = findings.filter(fn(item) { item.flag == Bad }).length()
  let missing = findings.filter(fn(item) { item.flag == Missing }).length()
  let suspect = findings.filter(fn(item) { item.flag == Suspect }).length()
  {
    total: observations.length(),
    good: observations.length() - bad - missing - suspect,
    missing,
    suspect,
    bad,
    findings,
  }
}

///|
pub fn render_report(summary : QcSummary) -> String {
  let out = StringBuilder::new()
  out.write_string("MoonBit Ocean QC report\n")
  out <+
    "total=\{summary.total} good=\{summary.good} suspect=\{summary.suspect} missing=\{summary.missing} bad=\{summary.bad}\n"
  for item in summary.findings {
    out <+ "- \{item.observation_id} [\{Repr(item.rule)}] \{item.reason}\n"
  }
  out.to_string()
}