///|
/// Milliseconds throughout. max_retries excludes the initial request.
pub(all) struct Settings {
  max_retries : Int
  base_delay_ms : Int64
  max_delay_ms : Int64
  total_budget_ms : Int64
  request_reserve_ms : Int64
} derive(Debug, Eq)

///|
pub fn defaults() -> Settings {
  {
    max_retries: 3,
    base_delay_ms: 200L,
    max_delay_ms: 30000L,
    total_budget_ms: 120000L,
    request_reserve_ms: 1000L,
  }
}

///|
pub(all) enum StopReason {
  InvalidSettings
  InvalidInput
  NotRetryable
  NotReplayable
  AttemptsExhausted
  BudgetExhausted
  ServerDelayExceedsCap
  InvalidRetryAfter
} derive(Debug, Eq)

///|
pub(all) enum Decision {
  Stop(StopReason)
  Wait(Int64)
} derive(Debug, Eq)

///|
/// Pure settings validation, also performed by each decision.
pub fn valid_settings(settings : Settings) -> Bool {
  settings.max_retries >= 0 &&
  settings.max_retries <= 1000 &&
  settings.base_delay_ms >= 0L &&
  settings.max_delay_ms >= settings.base_delay_ms &&
  settings.total_budget_ms >= 0L &&
  settings.request_reserve_ms >= 0L &&
  settings.request_reserve_ms <= settings.total_budget_ms
}

///|
pub extend Settings with @debug.Debug::{to_repr}

///|
pub extend Settings with Eq::{equal, not_equal}

///|
pub extend StopReason with @debug.Debug::{to_repr}

///|
pub extend StopReason with Eq::{equal, not_equal}

///|
pub extend Decision with @debug.Debug::{to_repr}

///|
pub extend Decision with Eq::{equal, not_equal}