///|
/// Coverage dimensions supported by a threshold gate.
pub(all) enum CoverageMetric {
  Lines
  Branches
  Functions
} derive(Debug, Eq)

///|
/// Optional minimum percentages for each coverage dimension.
pub(all) struct CoverageThresholds {
  lines : Double?
  branches : Double?
  functions : Double?
} derive(Debug)

///|
/// One failed coverage threshold.
pub(all) struct ThresholdViolation {
  metric : CoverageMetric
  actual : Double
  required : Double
} derive(Debug)

///|
/// The complete result of evaluating thresholds.
pub(all) struct GateResult {
  passed : Bool
  summary : CoverageSummary
  violations : Array[ThresholdViolation]
} derive(Debug)

///|
/// Construct optional percentage thresholds.
pub fn CoverageThresholds::new(
  lines? : Double,
  branches? : Double,
  functions? : Double,
) -> CoverageThresholds {
  { lines, branches, functions }
}

///|
fn validate_threshold(
  metric : String,
  value : Double?,
) -> Unit raise CoverageError {
  match value {
    Some(number) =>
      if number.is_nan() || number.is_inf() || number < 0.0 || number > 100.0 {
        raise InvalidThreshold(metric, number)
      }
    None => ()
  }
}

///|
fn add_violation(
  violations : Array[ThresholdViolation],
  metric : CoverageMetric,
  actual : Double,
  required : Double?,
) -> Unit {
  match required {
    Some(minimum) =>
      if actual < minimum {
        violations.push({ metric, actual, required: minimum })
      }
    None => ()
  }
}

///|
/// Evaluate report-wide minimum coverage percentages.
///
/// Equality passes: a 75% result satisfies a 75% threshold. Metrics without
/// instrumented items evaluate to 100%; see `CoverageCount::percentage`.
pub fn check_thresholds(
  report : CoverageReport,
  thresholds : CoverageThresholds,
) -> GateResult raise CoverageError {
  validate_threshold("lines", thresholds.lines)
  validate_threshold("branches", thresholds.branches)
  validate_threshold("functions", thresholds.functions)
  let summary = summarize(report)
  let violations : Array[ThresholdViolation] = []
  add_violation(violations, Lines, summary.lines.percentage(), thresholds.lines)
  add_violation(
    violations,
    Branches,
    summary.branches.percentage(),
    thresholds.branches,
  )
  add_violation(
    violations,
    Functions,
    summary.functions.percentage(),
    thresholds.functions,
  )
  { passed: violations.is_empty(), summary, violations }
}