///|
/// Runs once per decoded gateway event, around the handler fan-out.
/// `next(event)` spawns the typed and raw handlers for (a possibly
/// transformed) event and returns once they are spawned, not completed.
/// Not calling `next` drops the event for handlers only: cache updates,
/// decode-error observation, collectors, and interaction routing are
/// wire-level machinery that runs before this chain.
pub type EventMiddleware = async (
  GatewayCtx,
  @model.Event,
  async (@model.Event) -> Unit,
) -> Unit

///|
/// Install gateway event middleware. The first installed middleware is
/// outermost. The chain runs serially in the shard dispatch loop, so
/// middleware must return promptly. A dropped event emits no EventDispatched
/// telemetry. Middleware cannot widen intents or the gateway event filter.
pub fn Bot::middleware(self : Bot, middleware : EventMiddleware) -> Unit {
  self.event_middleware_.push(middleware)
}

///|
async fn Bot::run_event_middleware(
  self : Bot,
  spawner : @app.Spawner,
  ctx : GatewayCtx,
  index : Int,
  event : @model.Event,
) -> Unit {
  if index < self.event_middleware_.length() {
    self.event_middleware_[index](ctx, event, next_event => {
      self.run_event_middleware(spawner, ctx, index + 1, next_event)
    })
  } else {
    self.spawn_event_handlers(spawner, ctx, event)
  }
}

///|
async fn Bot::dispatch_event(
  self : Bot,
  spawner : @app.Spawner,
  ctx : GatewayCtx,
  event : @model.Event,
) -> Unit {
  self.run_event_middleware(spawner, ctx, 0, event) catch {
    error if @async.is_being_cancelled() || @async.is_cancellation_error(error) =>
      raise error
    error => self.app_.warn("event middleware failed: \{Repr(error)}")
  }
}