///|
/// Sliding-window log limiter for gateway commands: Discord allows 120 sends
/// per 60 seconds per connection; a few slots are reserved for heartbeats
/// (which bypass this limiter), so user commands default to 115.
pub struct CommandLimiter {
  priv timestamps : Array[Int64]
  priv limit : Int
  priv sleeper_ : async (Int) -> Unit
}

///|
/// Create a limiter allowing `limit` sends per 60-second sliding window.
pub fn CommandLimiter::CommandLimiter(
  limit? : Int = 115,
  sleeper? : async (Int) -> Unit = @async.sleep,
) -> CommandLimiter {
  { timestamps: [], limit, sleeper_: sleeper, }
}

///|
/// Wait until a send slot is available within the window, then consume it.
pub async fn CommandLimiter::acquire(self : CommandLimiter) -> Unit {
  for ;; {
    let now = @clock.now_ms()
    while self.timestamps.length() > 0 && now - self.timestamps[0] >= 60000L {
      self.timestamps.remove(0) |> ignore
    }
    if self.timestamps.length() < self.limit {
      self.timestamps.push(now)
      break
    }
    let wait = self.timestamps[0] + 60000L - now
    (self.sleeper_)(wait.to_int() + 1)
  }
}