///|
/// Review result produced before an incident response is handed over.
pub(all) struct RunbookReview {
  required_total : Int
  required_completed : Int
  missing_required : Array[String]
  next_owner : String?
  complete : Bool
  escalation_required : Bool
}

///|
/// Returns the event names in operator-facing runbook order.
pub fn runbook_events(runbook : IncidentRunbook) -> Array[String] {
  let events = []
  for step in runbook.steps {
    events.push(step.event)
  }
  events
}

///|
/// Returns unique owners in the order they first appear in the runbook.
pub fn runbook_owners(runbook : IncidentRunbook) -> Array[String] {
  let owners = []
  let seen = Map([])
  for step in runbook.steps {
    if !seen.contains(step.owner) {
      seen.set(step.owner, true)
      owners.push(step.owner)
    }
  }
  owners
}

///|
/// Checks whether an event is part of a runbook.
pub fn runbook_has_event(runbook : IncidentRunbook, event : String) -> Bool {
  for step in runbook.steps {
    if step.event == event {
      return true
    }
  }
  false
}

///|
/// Counts required runbook steps without inspecting execution history.
pub fn required_runbook_steps(runbook : IncidentRunbook) -> Int {
  let mut count = 0
  for step in runbook.steps {
    if step.required {
      count += 1
    }
  }
  count
}

///|
/// Builds a list of required events absent from a successful history.
pub fn missing_runbook_events(
  runbook : IncidentRunbook,
  history : Array[@fsm.TransitionRecord[String, String]],
) -> Array[String] {
  let missing = []
  for step in runbook.steps {
    if !step.required {
      continue
    }
    let mut found = false
    for record in history {
      if record.event == step.event {
        found = true
      }
    }
    if !found {
      missing.push(step.event)
    }
  }
  missing
}

///|
/// Returns the owner responsible for the first incomplete required step.
pub fn next_runbook_owner(
  runbook : IncidentRunbook,
  history : Array[@fsm.TransitionRecord[String, String]],
) -> String? {
  match next_runbook_step(runbook, history) {
    Some(step) => Some(step.owner)
    None => None
  }
}

///|
/// Reviews completion and escalation readiness for an incident handover.
pub fn review_runbook(
  runbook : IncidentRunbook,
  history : Array[@fsm.TransitionRecord[String, String]],
  severity : Int,
) -> RunbookReview {
  let total = required_runbook_steps(runbook)
  let completed = completed_required_steps(runbook, history)
  let missing = missing_runbook_events(runbook, history)
  {
    required_total: total,
    required_completed: completed,
    missing_required: missing,
    next_owner: next_runbook_owner(runbook, history),
    complete: missing.length() == 0,
    escalation_required: severity >= 3 && completed < total,
  }
}

///|
/// Computes an integer completion percentage for dashboards.
pub fn runbook_completion_percent(
  runbook : IncidentRunbook,
  history : Array[@fsm.TransitionRecord[String, String]],
) -> Int {
  let total = required_runbook_steps(runbook)
  if total == 0 {
    return 100
  }
  completed_required_steps(runbook, history) * 100 / total
}

///|
/// Returns a stable handover message with the next responsible owner.
pub fn handover_message(review : RunbookReview) -> String {
  if review.complete {
    "Runbook complete; response may enter closure review"
  } else {
    match review.next_owner {
      Some(owner) => "Pending required action owned by " + owner
      None => "Pending required action without an assigned owner"
    }
  }
}

///|
/// Lists every step owned by one operational role.
pub fn steps_for_owner(
  runbook : IncidentRunbook,
  owner : String,
) -> Array[RunbookStep] {
  let result = []
  for step in runbook.steps {
    if step.owner == owner {
      result.push(step)
    }
  }
  result
}

///|
/// Counts the required steps still assigned to one role.
pub fn pending_steps_for_owner(
  runbook : IncidentRunbook,
  history : Array[@fsm.TransitionRecord[String, String]],
  owner : String,
) -> Int {
  let mut pending = 0
  for step in steps_for_owner(runbook, owner) {
    if !step.required {
      continue
    }
    let mut completed = false
    for record in history {
      if record.event == step.event {
        completed = true
      }
    }
    if !completed {
      pending += 1
    }
  }
  pending
}

///|
/// A closure gate requires all mandatory steps and no unacknowledged incidents.
pub fn safe_to_close(
  review : RunbookReview,
  journal : @fsm.WorkflowJournal,
) -> Bool {
  review.complete && journal.unacknowledged_incidents() == 0
}

///|
/// Produces a compact review record for a handover document.
pub fn review_line(review : RunbookReview) -> String {
  let state = if review.complete { "COMPLETE" } else { "PENDING" }
  let escalation = if review.escalation_required {
    "ESCALATE"
  } else {
    "NORMAL"
  }
  "\{state} \{review.required_completed}/\{review.required_total} \{escalation}"
}

///|
/// Finds the first event assigned to a role, if the role is in the runbook.
pub fn first_event_for_owner(
  runbook : IncidentRunbook,
  owner : String,
) -> String? {
  for step in runbook.steps {
    if step.owner == owner {
      return Some(step.event)
    }
  }
  None
}

///|
/// Returns all required owners, preserving their runbook order.
pub fn required_runbook_owners(runbook : IncidentRunbook) -> Array[String] {
  let result = []
  let seen = Map([])
  for step in runbook.steps {
    if step.required && !seen.contains(step.owner) {
      seen.set(step.owner, true)
      result.push(step.owner)
    }
  }
  result
}

///|
/// Returns the zero-based position of an event in the runbook.
pub fn runbook_event_index(runbook : IncidentRunbook, event : String) -> Int? {
  for index in 0.. Int {
  let review = review_runbook(runbook, history, 0)
  review.missing_required.length()
}

///|
/// Labels a severity for operator-facing handover records.
pub fn severity_label(severity : Int) -> String {
  if severity >= 3 {
    "SEV-1"
  } else if severity == 2 {
    "SEV-2"
  } else {
    "SEV-3"
  }
}

///|
/// Returns whether an operator still owns mandatory work.
pub fn owner_has_pending(
  runbook : IncidentRunbook,
  history : Array[@fsm.TransitionRecord[String, String]],
  owner : String,
) -> Bool {
  pending_steps_for_owner(runbook, history, owner) > 0
}

///|
/// Converts missing events into an operator-friendly handover sentence.
pub fn missing_steps_message(review : RunbookReview) -> String {
  if review.complete {
    "No required runbook steps remain"
  } else {
    "Missing required steps: " + review.missing_required.join(", ")
  }
}

///|
/// Applies the critical-response escalation rule used by the example.
pub fn requires_commander(severity : Int, review : RunbookReview) -> Bool {
  severity >= 3 && !review.complete
}