// ============================================================
// Runtime health checks for integration boundaries
// ============================================================

///|
/// A compact health record for a tree runner.
pub struct RunnerHealth {
  frame : Int
  terminal : Bool
  status : String
}

///|
/// Inspect a runner without changing its execution state.
pub fn inspect_runner(runner : TreeRunner) -> RunnerHealth {
  let status = match runner.last_status() {
    None => "NotStarted"
    Some(value) => value.to_string()
  }
  { frame: runner.frame(), terminal: runner.is_terminal(), status }
}

///|
/// Return a stable line suitable for a readiness endpoint.
pub fn RunnerHealth::to_line(self : RunnerHealth) -> String {
  "frame=\{self.frame};terminal=\{self.terminal};status=\{self.status}"
}

///|
/// Return the inspected frame number.
pub fn RunnerHealth::frame(self : RunnerHealth) -> Int {
  self.frame
}

///|
/// Return whether the inspected runner is terminal.
pub fn RunnerHealth::is_terminal(self : RunnerHealth) -> Bool {
  self.terminal
}

///|
/// Return the status label without formatting.
pub fn RunnerHealth::status(self : RunnerHealth) -> String {
  self.status
}

///|
/// Return the type name stored under a key, or None when absent.
pub fn blackboard_value_type(bb : Blackboard, key : String) -> String? {
  match bb.get_value(key) {
    Some(value) => Some(value.type_name())
    None => None
  }
}

///|
/// Check that a blackboard contains all required keys.
pub fn has_required_keys(bb : Blackboard, required : Array[String]) -> Bool {
  for key in required {
    if !bb.has(key) {
      return false
    }
  }
  true
}

///|
/// Count how many required keys are present.
pub fn count_present_keys(bb : Blackboard, required : Array[String]) -> Int {
  let mut count = 0
  for key in required {
    if bb.has(key) {
      count = count + 1
    }
  }
  count
}

///|
/// Execute a tree only when its required context is complete.
pub fn context_guard_node(required : Array[String], child : Node) -> Node {
  let tick = fn(bb : Blackboard) {
    if has_required_keys(bb, required) {
      child.tick(bb)
    } else {
      child.reset()
      Status::BTFailure
    }
  }
  let reset = fn() { child.reset() }
  Node::new(tick, reset)
}