///|
/// Counts of operational risk levels in a result collection.
pub(all) struct RiskMatrix {
  low_count : Int
  medium_count : Int
  high_count : Int
  total_count : Int
}

///|
/// Convert risk to an ordered score.
pub fn risk_level_score(risk : RiskLevel) -> Int {
  match risk {
    Low => 1
    Medium => 2
    High => 3
  }
}

///|
/// Return whether a risk level requires action.
pub fn risk_is_actionable(risk : RiskLevel) -> Bool {
  risk is RiskLevel::High || risk is RiskLevel::Medium
}

///|
/// Return a stable risk label.
pub fn risk_level_text(risk : RiskLevel) -> String {
  match risk {
    Low => "low"
    Medium => "medium"
    High => "high"
  }
}

///|
/// Aggregate risk levels from feature results.
pub fn build_risk_matrix(collection : ResultCollection) -> RiskMatrix {
  let mut low_count = 0
  let mut medium_count = 0
  let mut high_count = 0
  for result in collection.results {
    match result.risk {
      Low => low_count += 1
      Medium => medium_count += 1
      High => high_count += 1
    }
  }
  {
    low_count,
    medium_count,
    high_count,
    total_count: collection.results.length(),
  }
}

///|
/// Return whether any matrix item needs action.
pub fn risk_matrix_needs_action(matrix : RiskMatrix) -> Bool {
  matrix.medium_count + matrix.high_count > 0
}

///|
/// Return a stable matrix status.
pub fn risk_matrix_status(matrix : RiskMatrix) -> String {
  if matrix.high_count > 0 {
    "action"
  } else if matrix.medium_count > 0 {
    "review"
  } else {
    "clear"
  }
}

///|
/// Return the fraction of low-risk results.
pub fn low_risk_rate(matrix : RiskMatrix) -> Double {
  if matrix.total_count == 0 {
    0.0
  } else {
    matrix.low_count.to_double() / matrix.total_count.to_double()
  }
}

///|
/// Return the fraction of high-risk results.
pub fn high_risk_rate(matrix : RiskMatrix) -> Double {
  if matrix.total_count == 0 {
    0.0
  } else {
    matrix.high_count.to_double() / matrix.total_count.to_double()
  }
}

///|
/// Render a stable matrix summary.
pub fn render_risk_matrix(matrix : RiskMatrix) -> String {
  "status=" +
  risk_matrix_status(matrix) +
  " low=" +
  matrix.low_count.to_string() +
  " medium=" +
  matrix.medium_count.to_string() +
  " high=" +
  matrix.high_count.to_string()
}

///|
/// Return the largest risk level in a matrix.
pub fn highest_risk(matrix : RiskMatrix) -> RiskLevel {
  if matrix.high_count > 0 {
    High
  } else if matrix.medium_count > 0 {
    Medium
  } else {
    Low
  }
}

///|
/// Return whether all results are low risk.
pub fn risk_matrix_is_clear(matrix : RiskMatrix) -> Bool {
  matrix.high_count == 0 && matrix.medium_count == 0
}