///|
fn count_key(kind : PhiKind) -> String {
  "\{kind}"
}

///|
fn add_count(counts : Map[String, Int], kind : PhiKind) -> Unit {
  let key = count_key(kind)
  counts[key] = match counts.get(key) {
    Some(v) => v + 1
    None => 1
  }
}

///|
fn apply_findings(
  input : String,
  findings : Array[Finding],
) -> (String, Array[OffsetMap]) {
  let output = StringBuilder()
  let offsets = []
  let mut cursor = 0
  for finding in findings {
    if finding.start >= cursor && finding.end <= input.length() {
      output.write_string(input[cursor:finding.start].to_owned())
      let replacement_start = output.to_string().length()
      output.write_string(finding.replacement)
      let replacement_end = output.to_string().length()
      offsets.push({
        original_start: finding.start,
        original_end: finding.end,
        replacement_start,
        replacement_end,
        finding_id: finding.id,
      })
      cursor = finding.end
    }
  }
  output.write_string(input[cursor:].to_owned())
  (output.to_string(), offsets)
}

///|
fn build_audit(
  input : String,
  output : String,
  findings : Array[Finding],
  offsets : Array[OffsetMap],
) -> AuditReport {
  let counts : Map[String, Int] = Map([])
  for finding in findings {
    add_count(counts, finding.kind)
  }
  {
    input_length: input.length(),
    output_length: output.length(),
    finding_count: findings.length(),
    applied_count: offsets.length(),
    counts,
    findings,
    offsets,
  }
}

///|
pub fn redact(
  input : String,
  rules? : Array[Rule] = comprehensive_rules(),
  options? : DeidOptions = DeidOptions::default(),
) -> DeidResult raise DeidError {
  let findings = scan(input, rules~, options~)
  let (text, offsets) = apply_findings(input, findings)
  {
    text,
    findings,
    offsets,
    audit: build_audit(input, text, findings, offsets),
  }
}

///|
pub fn audit_markdown(report : AuditReport) -> String {
  let lines = [
    "# moonbit-deid audit",
    "",
    "- input_length: \{report.input_length}",
    "- output_length: \{report.output_length}",
    "- findings: \{report.finding_count}",
    "- applied: \{report.applied_count}",
    "",
    "## counts",
  ]
  for k, v in report.counts {
    lines.push("- \{k}: \{v}")
  }
  lines.push("")
  lines.push("## findings")
  for f in report.findings {
    lines.push(
      "- \{f.id} \{f.kind} \{f.start}..\{f.end} via \{f.rule_id} confidence=\{f.confidence}",
    )
  }
  lines.join("\n")
}