///|
/// Structured hazard identification and risk-ranking helpers.
pub enum RiskLikelihood {
  Rare
  Unlikely
  Possible
  Likely
  Frequent
}

///|
pub fn rare() -> RiskLikelihood {
  Rare
}

///|
pub fn unlikely() -> RiskLikelihood {
  Unlikely
}

///|
pub fn possible() -> RiskLikelihood {
  Possible
}

///|
pub fn likely() -> RiskLikelihood {
  Likely
}

///|
pub fn frequent() -> RiskLikelihood {
  Frequent
}

///|
pub enum RiskSeverity {
  Negligible
  Minor
  Serious
  Major
  Catastrophic
}

///|
pub fn negligible() -> RiskSeverity {
  Negligible
}

///|
pub fn minor() -> RiskSeverity {
  Minor
}

///|
pub fn serious() -> RiskSeverity {
  Serious
}

///|
pub fn major() -> RiskSeverity {
  Major
}

///|
pub fn catastrophic() -> RiskSeverity {
  Catastrophic
}

///|
fn likelihood_score(value : RiskLikelihood) -> Int {
  match value {
    Rare => 1
    Unlikely => 2
    Possible => 3
    Likely => 4
    Frequent => 5
  }
}

///|
fn severity_score(value : RiskSeverity) -> Int {
  match value {
    Negligible => 1
    Minor => 2
    Serious => 3
    Major => 4
    Catastrophic => 5
  }
}

///|
pub struct Hazard {
  id : String
  node : String
  deviation : String
  cause : String
  consequence : String
  likelihood : RiskLikelihood
  severity : RiskSeverity
  safeguards : Array[String]
}

///|
pub fn hazard(
  id : String,
  node : String,
  deviation : String,
  cause : String,
  consequence : String,
  likelihood : RiskLikelihood,
  severity : RiskSeverity,
  safeguards : Array[String],
) -> Hazard {
  { id, node, deviation, cause, consequence, likelihood, severity, safeguards }
}

///|
pub fn Hazard::score(self : Hazard) -> Int {
  likelihood_score(self.likelihood) * severity_score(self.severity)
}

///|
pub fn Hazard::level(self : Hazard) -> String {
  if self.score() >= 15 {
    "high"
  } else if self.score() >= 8 {
    "medium"
  } else {
    "low"
  }
}

///|
pub fn Hazard::adequately_safeguarded(self : Hazard) -> Bool {
  self.safeguards.length() > 0
}

///|
pub struct RiskRegister {
  hazards : Array[Hazard]
}

///|
pub fn risk_register(hazards : Array[Hazard]) -> RiskRegister {
  { hazards, }
}

///|
pub fn RiskRegister::high(self : RiskRegister) -> Array[Hazard] {
  self.hazards.filter(fn(h) { h.level() == "high" })
}

///|
pub fn RiskRegister::score(self : RiskRegister) -> Int {
  self.hazards.fold(init=0, fn(total, h) { total + h.score() })
}

///|
pub fn RiskRegister::uncontrolled(self : RiskRegister) -> Array[Hazard] {
  self.hazards.filter(fn(h) { !h.adequately_safeguarded() })
}

///|
pub fn RiskRegister::acceptable(self : RiskRegister, max_high : Int) -> Bool {
  self.high().length() <= max_high && self.uncontrolled().length() == 0
}

///|
pub struct Safeguard {
  name : String
  kind : String
  independent : Bool
  test_interval_hours : Float
  enabled : Bool
}

///|
pub fn safeguard(
  name : String,
  kind : String,
  independent : Bool,
  test_interval_hours : Float,
  enabled : Bool,
) -> Safeguard {
  { name, kind, independent, test_interval_hours, enabled }
}

///|
pub fn Safeguard::valid(self : Safeguard) -> Bool {
  self.name != "" && self.test_interval_hours > 0.0
}

///|
pub fn Safeguard::reliable(self : Safeguard) -> Bool {
  self.valid() && self.enabled && self.independent
}

///|
pub struct BowTie {
  hazard : String
  threats : Array[String]
  preventive : Array[Safeguard]
  mitigative : Array[Safeguard]
}

///|
pub fn bow_tie(
  hazard : String,
  threats : Array[String],
  preventive : Array[Safeguard],
  mitigative : Array[Safeguard],
) -> BowTie {
  { hazard, threats, preventive, mitigative }
}

///|
pub fn BowTie::prevention_count(self : BowTie) -> Int {
  self.preventive.filter(fn(s) { s.reliable() }).length()
}

///|
pub fn BowTie::mitigation_count(self : BowTie) -> Int {
  self.mitigative.filter(fn(s) { s.reliable() }).length()
}

///|
pub fn BowTie::defended(self : BowTie) -> Bool {
  self.prevention_count() > 0 && self.mitigation_count() > 0
}

///|
pub fn risk_register_table(register : RiskRegister) -> ReportTable {
  let rows : Array[Array[String]] = []
  for h in register.hazards {
    rows.push([
      h.id,
      h.node,
      h.deviation,
      h.cause,
      h.consequence,
      "\{h.score()}",
      h.level(),
      "\{h.safeguards.length()}",
    ])
  }
  table(
    [
      "id", "node", "deviation", "cause", "consequence", "score", "level", "safeguards",
    ],
    rows,
  )
}

///|
pub fn risk_priority_order(hazards : Array[Hazard]) -> Array[Hazard] {
  let result = hazards.copy()
  result.sort_by(fn(a, b) { b.score() - a.score() })
  result
}

///|
pub fn risk_reduction(before : Hazard, after : Hazard) -> Int {
  before.score() - after.score()
}

///|
pub fn risk_summary(register : RiskRegister) -> String {
  "hazards=\{register.hazards.length()},high=\{register.high().length()},uncontrolled=\{register.uncontrolled().length()}"
}