///|
/// Daily aggregate used for lightweight operational monitoring.
pub struct DailyValue {
  day : Int
  orders : Int
  customers : Int
  value : Int
  average_order : Int
}

///|
pub enum TrendDirection {
  Rising
  Falling
  Flat
} derive(Debug, Eq)

///|
pub struct TrendSummary {
  direction : TrendDirection
  first_value : Int
  last_value : Int
  change_percent : Int
}

///|
pub fn daily_values(events : Array[Event]) -> Array[DailyValue] {
  let days : Array[Int] = []
  for event in events {
    if !days.contains(event.day) {
      days.push(event.day)
    }
  }
  days.sort()
  days.map(day => {
    let ids : Array[String] = []
    let mut orders = 0
    let mut value = 0
    for event in events {
      if event.day == day {
        orders += 1
        value += event.amount
        if !ids.contains(event.customer_id) {
          ids.push(event.customer_id)
        }
      }
    }
    {
      day,
      orders,
      customers: ids.length(),
      value,
      average_order: if orders == 0 {
        0
      } else {
        value / orders
      },
    }
  })
}

///|
pub fn summarize_trend(values : Array[DailyValue]) -> TrendSummary {
  if values.is_empty() {
    return { direction: Flat, first_value: 0, last_value: 0, change_percent: 0 }
  }
  let first = values[0].value
  let last = values[values.length() - 1].value
  let direction = if last > first {
    Rising
  } else if last < first {
    Falling
  } else {
    Flat
  }
  let change = if first == 0 { 0 } else { (last - first) * 100 / first }
  { direction, first_value: first, last_value: last, change_percent: change }
}

///|
/// Stable predicates for consumers that do not pattern-match the enum.
pub fn TrendSummary::is_rising(summary : TrendSummary) -> Bool {
  summary.direction == Rising
}

///|
pub fn TrendSummary::is_falling(summary : TrendSummary) -> Bool {
  summary.direction == Falling
}