///|
pub(all) struct TimePoint {
  time : Double
  value : Double
}

///|
pub(all) struct RollingSummary {
  name : String
  window_size : Double
  priv points : Array[TimePoint]
}

///|
pub fn RollingSummary::new(
  name : String,
  window_size : Double,
) -> RollingSummary {
  if window_size <= 0.0 {
    abort("RollingSummary window_size must be positive")
  }
  { name, window_size, points: [] }
}

///|
pub fn RollingSummary::record(
  self : RollingSummary,
  time : Double,
  value : Double,
) -> Unit {
  self.points.push({ time, value })
}

///|
pub fn RollingSummary::rolling_mean(
  self : RollingSummary,
  now : Double,
) -> Double {
  if self.points.is_empty() {
    return 0.0
  }
  let cutoff = now - self.window_size
  let mut sum = 0.0
  let mut total_t = 0.0
  let mut prev_t = cutoff
  let mut prev_val = self.points[0].value

  for i = 0; i < self.points.length(); i = i + 1 {
    let pt = self.points[i]
    if pt.time <= cutoff {
      prev_val = pt.value
      prev_t = cutoff
    } else {
      let dt = pt.time - prev_t
      if dt > 0.0 {
        sum = sum + prev_val * dt
        total_t = total_t + dt
      }
      prev_t = pt.time
      prev_val = pt.value
    }
  }

  let dt_end = now - prev_t
  if dt_end > 0.0 {
    sum = sum + prev_val * dt_end
    total_t = total_t + dt_end
  }

  if total_t <= 0.0 {
    prev_val
  } else {
    sum / total_t
  }
}

///|
pub(all) struct PercentileTracker {
  name : String
  priv samples : Array[Double]
}

///|
pub fn PercentileTracker::new(name : String) -> PercentileTracker {
  { name, samples: [] }
}

///|
pub fn PercentileTracker::record(
  self : PercentileTracker,
  value : Double,
) -> Unit {
  self.samples.push(value)
}

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

///|
pub fn PercentileTracker::percentile(
  self : PercentileTracker,
  p : Double,
) -> Double {
  if self.samples.is_empty() {
    return 0.0
  }
  if p <= 0.0 {
    return self.samples[0]
  }
  let n = self.samples.length()
  // Create sorted copy
  let sorted : Array[Double] = []
  for s in self.samples {
    sorted.push(s)
  }
  for i = 0; i < n; i = i + 1 {
    for j = i + 1; j < n; j = j + 1 {
      if sorted[j] < sorted[i] {
        let tmp = sorted[i]
        sorted[i] = sorted[j]
        sorted[j] = tmp
      }
    }
  }
  let rank = p / 100.0 * Double::from_int(n - 1)
  let idx = rank.to_int()
  if idx >= n - 1 {
    sorted[n - 1]
  } else {
    let frac = rank - Double::from_int(idx)
    sorted[idx] + frac * (sorted[idx + 1] - sorted[idx])
  }
}

///|
pub fn PercentileTracker::p50(self : PercentileTracker) -> Double {
  self.percentile(50.0)
}

///|
pub fn PercentileTracker::p90(self : PercentileTracker) -> Double {
  self.percentile(90.0)
}

///|
pub fn PercentileTracker::p95(self : PercentileTracker) -> Double {
  self.percentile(95.0)
}

///|
pub fn PercentileTracker::p99(self : PercentileTracker) -> Double {
  self.percentile(99.0)
}