// Threshold-based alert triggers

///|
pub(all) struct AlertRule {
  name : String
  min_level : Level
  max_count : Int
  window_size : Int
}

///|
pub fn AlertRule::new(
  name : String,
  min_level : Level,
  max_count : Int,
  window_size : Int,
) -> AlertRule {
  AlertRule::{ name, min_level, max_count, window_size }
}

///|
pub fn AlertRule::evaluate(self : AlertRule, logger : Logger) -> Bool {
  let count = count_recent(logger, self.min_level, self.window_size)
  count > self.max_count
}

///|
fn count_recent(logger : Logger, min_level : Level, window_size : Int) -> Int {
  let records = logger.records()
  let effective_window = if window_size > 0 { window_size } else { 0 }
  let start = if records.length() > effective_window {
    records.length() - effective_window
  } else {
    0
  }
  count_in_range(records, start, min_level, 0)
}

///|
fn count_in_range(
  records : Array[LogRecord],
  start : Int,
  min_level : Level,
  count : Int,
) -> Int {
  if start >= records.length() {
    count
  } else {
    let increment = if should_log(records[start].level, min_level) {
      1
    } else {
      0
    }
    count_in_range(records, start + 1, min_level, count + increment)
  }
}

// AlertManager — tracks multiple rules and triggers

///|
pub(all) struct AlertManager {
  rules : Array[AlertRule]
}

///|
pub fn AlertManager::new() -> AlertManager {
  AlertManager::{ rules: [] }
}

///|
pub fn AlertManager::add_rule(
  self : AlertManager,
  rule : AlertRule,
) -> AlertManager {
  let new_rules = self.rules.copy()
  new_rules.push(rule)
  AlertManager::{ rules: new_rules }
}

///|
pub fn AlertManager::check_all(
  self : AlertManager,
  logger : Logger,
) -> (AlertManager, Array[String]) {
  let triggered : Array[String] = []
  check_rules(self.rules, logger, 0, triggered)
}

///|
fn check_rules(
  rules : Array[AlertRule],
  logger : Logger,
  idx : Int,
  triggered : Array[String],
) -> (AlertManager, Array[String]) {
  if idx >= rules.length() {
    (AlertManager::{ rules, }, triggered)
  } else {
    if rules[idx].evaluate(logger) {
      triggered.push(rules[idx].name)
    }
    check_rules(rules, logger, idx + 1, triggered)
  }
}

// Log summary — quick overview of log activity

///|
pub(all) struct LogSummary {
  total : Int
  by_level : Array[Int] // [TRACE, DEBUG, INFO, WARN, ERROR]
}

///|
pub fn summarize(logger : Logger) -> LogSummary {
  compute_summary(logger.records(), 0, 0, 0, 0, 0, 0, 0)
}

///|
fn compute_summary(
  records : Array[LogRecord],
  idx : Int,
  t : Int,
  d : Int,
  i : Int,
  w : Int,
  e : Int,
  total : Int,
) -> LogSummary {
  if idx >= records.length() {
    LogSummary::{ total, by_level: [t, d, i, w, e] }
  } else {
    let (nt, nd, ni, nw, ne) = match records[idx].level {
      Level::TRACE => (t + 1, d, i, w, e)
      Level::DEBUG => (t, d + 1, i, w, e)
      Level::INFO => (t, d, i + 1, w, e)
      Level::WARN => (t, d, i, w + 1, e)
      Level::ERROR => (t, d, i, w, e + 1)
    }
    compute_summary(records, idx + 1, nt, nd, ni, nw, ne, total + 1)
  }
}

///|
pub fn LogSummary::error_rate(self : LogSummary) -> Int {
  if self.total == 0 {
    0
  } else {
    self.by_level[4] * 100 / self.total
  }
}

///|
pub fn LogSummary::warn_rate(self : LogSummary) -> Int {
  if self.total == 0 {
    0
  } else {
    self.by_level[3] * 100 / self.total
  }
}