///|
/// Composable monitor registration and user-defined monitor trait (B-11).
///
/// Extends the built-in monitor system with:
///   - MonitorFn type for lightweight function-based monitors
///   - MonitorRegistry for composable monitor registration
///   - Support for user-defined monitors via the same interface

///|
/// MonitorFn — the function signature for a runtime monitor.
/// Each monitor takes a Runtime snapshot and returns a MonitorResult.
pub(all) struct MonitorFn {
  run : (Runtime) -> MonitorResult
}

///|
/// MonitorRegistry manages a composable set of monitors.
/// Both built-in and user-defined monitors are registered uniformly.
pub(all) struct MonitorRegistry {
  monitors : Array[(String, MonitorFn)]
}

///|
pub fn MonitorRegistry::new() -> MonitorRegistry {
  { monitors: [] }
}

///|
/// Create a registry pre-loaded with all built-in monitors.
pub fn MonitorRegistry::with_defaults() -> MonitorRegistry {
  let reg = MonitorRegistry::new()
  reg.register("consecutive_timeouts", {
    run: fn(rt) { monitor_consecutive_timeouts(rt) },
  })
  reg.register("wkc_errors", { run: fn(rt) { monitor_wkc_errors(rt) } })
  reg.register("dc_drift", { run: fn(rt) { monitor_dc_drift(rt) } })
  reg.register("startup_state", { run: fn(rt) { monitor_startup_state(rt) } })
  reg.register("pdo_configured", { run: fn(rt) { monitor_pdo_configured(rt) } })
  reg.register("cycle_success_rate", {
    run: fn(rt) { monitor_cycle_success_rate(rt, 80) },
  })
  reg
}

///|
/// Register a named monitor function.
pub fn MonitorRegistry::register(
  self : MonitorRegistry,
  name : String,
  f : MonitorFn,
) -> Unit {
  self.monitors.push((name, f))
}

///|
/// Remove a monitor by name (for overriding defaults).
pub fn MonitorRegistry::unregister(
  self : MonitorRegistry,
  name : String,
) -> Unit {
  let filtered : Array[(String, MonitorFn)] = []
  for entry in self.monitors {
    if entry.0 != name {
      filtered.push(entry)
    }
  }
  // Replace contents
  self.monitors.clear()
  for entry in filtered {
    self.monitors.push(entry)
  }
}

///|
/// Run all registered monitors and return results with aggregate worst verdict.
pub fn MonitorRegistry::run(
  self : MonitorRegistry,
  rt : Runtime,
) -> (Array[MonitorResult], Verdict) {
  let results : Array[MonitorResult] = []
  for entry in self.monitors {
    let (_, f) = entry
    results.push((f.run)(rt))
  }
  let worst = worst_verdict(results)
  (results, worst)
}

///|
/// Count of registered monitors.
pub fn MonitorRegistry::len(self : MonitorRegistry) -> Int {
  self.monitors.length()
}