///|
pub(all) enum MutantOutcome {
  Killed
  Survived
  CompileError
  Timeout
  Skipped
  Equivalent
} derive(Debug, Eq, ToJson)

///|
pub(all) struct MutantResult {
  candidate : MutationCandidate
  outcome : MutantOutcome
  detail : String
} derive(Debug, Eq, ToJson)

///|
pub(all) struct MutationRunReport {
  file : String
  total : Int
  killed : Int
  survived : Int
  compile_error : Int
  timeout : Int
  skipped : Int
  equivalent : Int
  score_percent : Int
} derive(Debug, Eq, ToJson)

///|
pub fn outcome_label(outcome : MutantOutcome) -> String {
  match outcome {
    Killed => "killed"
    Survived => "survived"
    CompileError => "compile-error"
    Timeout => "timeout"
    Skipped => "skipped"
    Equivalent => "equivalent"
  }
}

///|
pub fn result(
  candidate : MutationCandidate,
  outcome : MutantOutcome,
  detail? : String = "",
) -> MutantResult {
  { candidate, outcome, detail }
}

///|
pub fn summarize_results(
  file : String,
  results : ArrayView[MutantResult],
) -> MutationRunReport {
  let mut killed = 0
  let mut survived = 0
  let mut compile_error = 0
  let mut timeout = 0
  let mut skipped = 0
  let mut equivalent = 0
  for item in results {
    match item.outcome {
      Killed => killed += 1
      Survived => survived += 1
      CompileError => compile_error += 1
      Timeout => timeout += 1
      Skipped => skipped += 1
      Equivalent => equivalent += 1
    }
  }
  let scored = killed + survived
  let score_percent = if scored == 0 { 0 } else { killed * 100 / scored }
  {
    file,
    total: results.length(),
    killed,
    survived,
    compile_error,
    timeout,
    skipped,
    equivalent,
    score_percent,
  }
}

///|
pub fn format_run_report(report : MutationRunReport) -> String {
  [
    "Mutation report for \{report.file}",
    "total: \{report.total}",
    "killed: \{report.killed}",
    "survived: \{report.survived}",
    "compile-error: \{report.compile_error}",
    "timeout: \{report.timeout}",
    "skipped: \{report.skipped}",
    "equivalent: \{report.equivalent}",
    "score: \{report.score_percent}%",
  ].join("\n")
}

///|
pub fn format_result(result : MutantResult) -> String {
  let candidate = result.candidate
  let base = "#\{candidate.id} \{outcome_label(result.outcome)} " +
    "\{candidate.span.file}:\{candidate.span.line}:\{candidate.span.column} " +
    "\{candidate.rule.label} \{candidate.original} -> \{candidate.replacement}"
  if result.detail == "" {
    base
  } else {
    base + " -- " + result.detail
  }
}