///|
// Per-backend traffic metrics. The counters are the single source of truth for
// how many requests were dispatched, succeeded and failed, so an operator can
// see the balancer's view of a pool. Like the health functions these are pure
// and compile on every target; the native runtime calls them around each
// forward attempt, and `snapshot` makes them observable in tests and logs.

///|
/// An immutable point-in-time view of one backend's counters.
pub struct BackendStats {
  address : String
  healthy : Bool
  weight : Int
  active_conns : Int
  total_requests : Int
  success_count : Int
  fail_count : Int
}

///|
/// Count one request dispatched to backend `idx`.
pub fn count_request(backends : Array[Backend], idx : Int) -> Unit {
  let b = backends[idx]
  b.total_requests = b.total_requests + 1
  backends[idx] = b
}

///|
/// Count one successful exchange with backend `idx`.
pub fn count_success(backends : Array[Backend], idx : Int) -> Unit {
  let b = backends[idx]
  b.success_count = b.success_count + 1
  backends[idx] = b
}

///|
/// Count one failed exchange with backend `idx`.
pub fn count_failure(backends : Array[Backend], idx : Int) -> Unit {
  let b = backends[idx]
  b.fail_count = b.fail_count + 1
  backends[idx] = b
}

///|
/// Snapshot every backend's counters in array order.
pub fn snapshot(backends : Array[Backend]) -> Array[BackendStats] {
  let out : Array[BackendStats] = []
  for b in backends {
    out.push({
      address: b.address(),
      healthy: b.healthy,
      weight: b.weight,
      active_conns: b.active_conns,
      total_requests: b.total_requests,
      success_count: b.success_count,
      fail_count: b.fail_count,
    })
  }
  out
}

///|
/// The stats entry's `address:port`.
pub fn BackendStats::address(self : BackendStats) -> String {
  self.address
}

///|
/// Whether the backend was healthy at the snapshot.
pub fn BackendStats::is_healthy(self : BackendStats) -> Bool {
  self.healthy
}

///|
/// Requests dispatched at the snapshot.
pub fn BackendStats::total_requests(self : BackendStats) -> Int {
  self.total_requests
}

///|
/// Successful exchanges at the snapshot.
pub fn BackendStats::success_count(self : BackendStats) -> Int {
  self.success_count
}

///|
/// Failed exchanges at the snapshot.
pub fn BackendStats::fail_count(self : BackendStats) -> Int {
  self.fail_count
}

///|
/// In-flight requests at the snapshot.
pub fn BackendStats::active_conns(self : BackendStats) -> Int {
  self.active_conns
}