// Copyright 2026 Leo Cheng
// SPDX-License-Identifier: Apache-2.0

///|
/// The same forty calls offered to a bucket, a sliding window and a period, all
/// with a quota of twenty a second.
///
/// `moon run src/examples/limiters`
///
/// The traffic is bursty on purpose: one burst every two seconds, forty calls
/// from 950 ms past the second to 1050 ms past it, so the burst straddles a
/// period boundary and nothing else happens in between. That is the shape that
/// tells the three apart:
///
/// - the bucket spends its saved burst, then holds the rate;
/// - the sliding window refuses the twenty-first call in any one-second stretch,
///   wherever that stretch starts;
/// - the period starts a fresh twenty when the second turns over, so the same
///   burst is counted as twenty on each side and all forty pass.
fn main {
  let second = @moondate.Span::new(seconds=1L)
  let bucket = @moonward.Bucket::new(20.0, burst=20.0)
  let sliding = @moonward.Sliding::new(20, second)
  let period = @moonward.Period::new(20, second, align=true)
  println("burst  bucket  sliding  period")
  for b in 0L..<4L {
    let mut by_bucket = 0
    let mut by_sliding = 0
    let mut by_period = 0
    for k in 0L..<40L {
      // 950 ms past the second, 2.5 ms apart: 20 calls either side of the line.
      let now = b * 2_000_000_000L + 950_000_000L + k * 2_500_000L
      if bucket.take(now) {
        by_bucket += 1
      }
      if sliding.take(now) {
        by_sliding += 1
      }
      match period.take(now) {
        @moonward.Over => ()
        _ => by_period += 1
      }
    }
    println(
      "\{pad(b.to_int(), 5)}  \{pad(by_bucket, 6)}  \{pad(by_sliding, 7)}  \{pad(by_period, 6)}",
    )
  }
  // What a caller does with a refusal: ask how long until there is room again,
  // which is the number a Retry-After header carries.
  let wait = bucket.next(7_050_000_000L, n=5.0)
  println("\nfive tokens in \{(wait.nanos + 999_999L) / 1_000_000L} ms")
}

///|
/// Right-align a number in `width` columns, so the table can be read.
fn pad(n : Int32, width : Int32) -> String {
  let mut out = "\{n}"
  while out.length() < width {
    out = " " + out
  }
  out
}