// Event meter — tracks event rates over time windows

///|
pub(all) struct EventMeter {
  name : String
  events : Array[Int] // timestamps of events
  window_size : Int // size of sliding window
}

///|
pub fn EventMeter::new(name : String, window_size : Int) -> EventMeter {
  EventMeter::{ name, events: [], window_size }
}

///|
pub fn EventMeter::record(self : EventMeter, ts : Int) -> EventMeter {
  let new_events = self.events.copy()
  new_events.push(ts)
  // Trim events outside window
  let cutoff = ts - self.window_size
  let trimmed = trim_events(new_events, cutoff, 0, [])
  EventMeter::{ ..self, events: trimmed }
}

///|
fn trim_events(
  src : Array[Int],
  cutoff : Int,
  idx : Int,
  result : Array[Int],
) -> Array[Int] {
  if idx >= src.length() {
    result
  } else if src[idx] >= cutoff {
    // All remaining are within window
    copy_remaining(src, idx, result)
  } else {
    trim_events(src, cutoff, idx + 1, result)
  }
}

///|
fn copy_remaining(
  src : Array[Int],
  idx : Int,
  result : Array[Int],
) -> Array[Int] {
  if idx >= src.length() {
    result
  } else {
    result.push(src[idx])
    copy_remaining(src, idx + 1, result)
  }
}

///|
pub fn EventMeter::count(self : EventMeter) -> Int {
  self.events.length()
}

///|
pub fn EventMeter::rate(self : EventMeter) -> Int {
  // Events per window_size ticks
  self.events.length()
}

// Rate limiter — allows up to N events per window

///|
pub(all) struct RateLimiter {
  max_per_window : Int
  meter : EventMeter
}

///|
pub fn RateLimiter::new(max_per_window : Int, window_size : Int) -> RateLimiter {
  RateLimiter::{
    max_per_window,
    meter: EventMeter::new("limiter", window_size),
  }
}

///|
pub fn RateLimiter::allow(self : RateLimiter, ts : Int) -> (RateLimiter, Bool) {
  let new_meter = self.meter.record(ts)
  let allowed = new_meter.count() <= self.max_per_window
  (RateLimiter::{ ..self, meter: new_meter }, allowed)
}

// Throughput counter — counts total events and computes average rate

///|
pub(all) struct ThroughputCounter {
  name : String
  total : Int
  first_ts : Int
  last_ts : Int
}

///|
pub fn ThroughputCounter::new(name : String) -> ThroughputCounter {
  ThroughputCounter::{ name, total: 0, first_ts: 0, last_ts: 0 }
}

///|
pub fn ThroughputCounter::record(
  self : ThroughputCounter,
  ts : Int,
) -> ThroughputCounter {
  if self.total == 0 {
    ThroughputCounter::{ name: self.name, total: 1, first_ts: ts, last_ts: ts }
  } else {
    ThroughputCounter::{ ..self, total: self.total + 1, last_ts: ts }
  }
}

///|
pub fn ThroughputCounter::avg_rate(self : ThroughputCounter) -> Int {
  let duration = self.last_ts - self.first_ts
  if duration <= 0 {
    0
  } else {
    self.total / duration
  }
}

///|
pub fn ThroughputCounter::total_count(self : ThroughputCounter) -> Int {
  self.total
}