// ============================================================
// Event queue router
//
// EventQueue stores ordered messages, while EventBus stores one-shot flags.
// EventRouter is the integration layer for applications that need a bounded
// amount of message work per frame. Handlers receive the original event,
// including its typed payload, and a Running handler remains active across
// frames without losing the message.
// ============================================================

///|
/// A named event handler used by EventRouter.
pub struct EventRoute {
  name : String
  handler : (Blackboard, EventRecord) -> Status
}

///|
/// Register a handler for an event name.
pub fn EventRoute::new(
  name : String,
  handler : (Blackboard, EventRecord) -> Status,
) -> EventRoute {
  { name, handler }
}

///|
/// Return the event name handled by this route.
pub fn EventRoute::name(self : EventRoute) -> String {
  self.name
}

///|
/// A bounded router tick report for monitoring and backpressure decisions.
pub struct EventRouterReport {
  processed : Int
  succeeded : Int
  failed : Int
  running : Bool
  pending : Int
  status : Status
}

///|
/// Number of events delivered during this router tick.
pub fn EventRouterReport::processed(self : EventRouterReport) -> Int {
  self.processed
}

///|
/// Number of events that completed successfully during this tick.
pub fn EventRouterReport::succeeded(self : EventRouterReport) -> Int {
  self.succeeded
}

///|
/// Number of events that failed during this tick.
pub fn EventRouterReport::failed(self : EventRouterReport) -> Int {
  self.failed
}

///|
/// Whether a handler is still running across frames.
pub fn EventRouterReport::is_running(self : EventRouterReport) -> Bool {
  self.running
}

///|
/// Number of queued or active events after this tick.
pub fn EventRouterReport::pending(self : EventRouterReport) -> Int {
  self.pending
}

///|
/// Overall status for the router tick.
pub fn EventRouterReport::status(self : EventRouterReport) -> Status {
  self.status
}

///|
/// Format the report for a log line or metrics sample.
pub fn EventRouterReport::to_line(self : EventRouterReport) -> String {
  "processed=\{self.processed};succeeded=\{self.succeeded};failed=\{self.failed};running=\{self.running};pending=\{self.pending};status=\{self.status.to_string()}"
}

///|
/// A bounded event-to-handler dispatcher.
pub struct EventRouter {
  queue : EventQueue
  routes : Array[EventRoute]
  fallback : Ref[EventRoute?]
  max_events : Ref[Int]
  active : Ref[EventRecord?]
}

///|
/// Create a router over an existing FIFO queue.
pub fn EventRouter::new(queue : EventQueue, max_events : Int) -> EventRouter {
  {
    queue,
    routes: [],
    fallback: Ref::new(None),
    max_events: Ref::new(if max_events > 0 { max_events } else { 1 }),
    active: Ref::new(None),
  }
}

///|
/// Add or replace a named route. Returns true when a route was inserted.
pub fn EventRouter::on(
  self : EventRouter,
  name : String,
  handler : (Blackboard, EventRecord) -> Status,
) -> Bool {
  let route = EventRoute::new(name, handler)
  let mut i = 0
  while i < self.routes.length() {
    if self.routes[i].name == route.name {
      self.routes[i] = route
      return false
    }
    i = i + 1
  }
  self.routes.push(route)
  true
}

///|
/// Remove one named route. Returns whether a route was removed.
pub fn EventRouter::remove(self : EventRouter, name : String) -> Bool {
  let mut i = 0
  while i < self.routes.length() {
    if self.routes[i].name == name {
      let _ = self.routes.remove(i)
      return true
    }
    i = i + 1
  }
  false
}

///|
/// Return whether a named route exists.
pub fn EventRouter::has_route(self : EventRouter, name : String) -> Bool {
  for route in self.routes {
    if route.name == name {
      return true
    }
  }
  false
}

///|
/// Number of registered routes.
pub fn EventRouter::route_count(self : EventRouter) -> Int {
  self.routes.length()
}

///|
/// Install a handler for events without a named route.
pub fn EventRouter::set_fallback(
  self : EventRouter,
  handler : (Blackboard, EventRecord) -> Status,
) -> Unit {
  self.fallback.set(Some(EventRoute::new("*", handler)))
}

///|
/// Remove the fallback handler.
pub fn EventRouter::clear_fallback(self : EventRouter) -> Unit {
  self.fallback.set(None)
}

///|
/// Set the maximum number of new events processed in one tick.
pub fn EventRouter::set_max_events(
  self : EventRouter,
  max_events : Int,
) -> Unit {
  self.max_events.set(if max_events > 0 { max_events } else { 1 })
}

///|
/// Current per-tick event budget.
pub fn EventRouter::max_events(self : EventRouter) -> Int {
  self.max_events.get()
}

///|
/// Return the queue used by this router.
pub fn EventRouter::queue(self : EventRouter) -> EventQueue {
  self.queue
}

///|
/// Return the active event being resumed, if a handler returned Running.
pub fn EventRouter::active_event(self : EventRouter) -> EventRecord? {
  self.active.get()
}

///|
/// Return whether a handler is waiting for another frame.
pub fn EventRouter::is_running(self : EventRouter) -> Bool {
  match self.active.get() {
    Some(_) => true
    None => false
  }
}

///|
/// Find a named route without changing the registration order.
fn find_event_route(routes : Array[EventRoute], name : String) -> EventRoute? {
  for route in routes {
    if route.name == name {
      return Some(route)
    }
  }
  None
}

///|
/// Process at most the configured number of events.
///
/// A Running handler keeps the current EventRecord active. The event is not
/// polled from the queue again, so handlers can safely perform multi-frame
/// animation, network retry, or resource loading work.
pub fn EventRouter::tick(
  self : EventRouter,
  bb : Blackboard,
) -> EventRouterReport {
  let mut processed = 0
  let mut succeeded = 0
  let mut failed = 0
  let limit = self.max_events.get()
  while processed < limit {
    let event = match self.active.get() {
      Some(active) => active
      None =>
        match self.queue.poll() {
          None => break
          Some(next) => {
            self.active.set(Some(next))
            next
          }
        }
    }
    let status = match find_event_route(self.routes, event.name()) {
      Some(route) => (route.handler)(bb, event)
      None =>
        match self.fallback.get() {
          Some(route) => (route.handler)(bb, event)
          None => Status::BTFailure
        }
    }
    processed = processed + 1
    match status {
      Status::BTRunning => break
      Status::BTSuccess => {
        succeeded = succeeded + 1
        self.active.set(None)
      }
      Status::BTFailure => {
        failed = failed + 1
        self.active.set(None)
      }
    }
  }
  let running = self.is_running()
  let active_count = if running { 1 } else { 0 }
  let pending = self.queue.size() + active_count
  let overall = if running || pending > 0 {
    Status::BTRunning
  } else if failed > 0 {
    Status::BTFailure
  } else {
    Status::BTSuccess
  }
  { processed, succeeded, failed, running, pending, status: overall }
}

///|
/// Cancel the active handler without consuming queued events.
pub fn EventRouter::cancel_active(self : EventRouter) -> Unit {
  self.active.set(None)
}

///|
/// Clear queued and active work, preserving route registrations.
pub fn EventRouter::reset(self : EventRouter) -> Unit {
  self.queue.clear()
  self.active.set(None)
}

///|
/// Return the number of queued events, excluding an active event.
pub fn EventRouter::queued(self : EventRouter) -> Int {
  self.queue.size()
}

///|
/// Return all registered route names in declaration order.
pub fn EventRouter::route_names(self : EventRouter) -> Array[String] {
  let names : Array[String] = []
  for route in self.routes {
    names.push(route.name)
  }
  names
}

///|
/// Deliver one event immediately through a named route without queueing it.
pub fn EventRouter::deliver(
  self : EventRouter,
  name : String,
  bb : Blackboard,
  payload : Value?,
) -> Status {
  let event = EventRecord::new(-1, name, payload)
  match find_event_route(self.routes, event.name()) {
    Some(route) => (route.handler)(bb, event)
    None =>
      match self.fallback.get() {
        Some(route) => (route.handler)(bb, event)
        None => Status::BTFailure
      }
  }
}