///|
/// EventRecorder is a small in-memory probe for tests.
pub(all) struct EventRecorder {
  name : String
  messages : Array[Message]
} derive(@debug.Debug)

///|
pub fn EventRecorder::EventRecorder(
  name? : StringView = "recorder",
) -> EventRecorder {
  { name: name.to_owned(), messages: [] }
}

///|
pub fn EventRecorder::handle(
  self : EventRecorder,
  message : Message,
) -> DeliveryStatus {
  self.messages.push(message)
  Ack
}

///|
pub fn EventRecorder::handler(
  self : EventRecorder,
) -> (Message) -> DeliveryStatus {
  fn(message) { self.handle(message) }
}

///|
pub fn EventRecorder::count(self : EventRecorder) -> Int {
  self.messages.length()
}

///|
pub fn EventRecorder::clear(self : EventRecorder) -> Unit {
  self.messages.clear()
}

///|
pub fn EventRecorder::last(self : EventRecorder) -> Message? {
  if self.messages.length() == 0 {
    None
  } else {
    Some(self.messages[self.messages.length() - 1])
  }
}

///|
pub fn EventRecorder::count_topic(
  self : EventRecorder,
  topic : StringView,
) -> Int {
  let mut count = 0
  for msg in self.messages {
    if msg.topic == topic.to_owned() {
      count = count + 1
    }
  }
  count
}

///|
pub fn EventRecorder::contains_topic(
  self : EventRecorder,
  topic : StringView,
) -> Bool {
  self.count_topic(topic) > 0
}

///|
pub(all) struct FakePublisher {
  bus : EventBus
  source : String
} derive(@debug.Debug)

///|
pub fn FakePublisher::FakePublisher(
  bus : EventBus,
  source? : StringView = "fake",
) -> FakePublisher {
  { bus, source: source.to_owned() }
}

///|
pub fn FakePublisher::emit(
  self : FakePublisher,
  topic : StringView,
) -> PublishReport {
  self.bus.publish(topic, source=self.source)
}

///|
pub fn FakePublisher::emit_payload(
  self : FakePublisher,
  topic : StringView,
  payload : StringView,
) -> PublishReport {
  self.bus.publish(topic, payload~, source=self.source)
}

///|
pub fn assert_delivered(report : PublishReport, expected : Int) -> Unit {
  if report.delivered != expected {
    panic()
  }
}

///|
pub fn assert_rejected(report : PublishReport, expected : Int) -> Unit {
  if report.rejected != expected {
    panic()
  }
}

///|
pub fn assert_matched(report : PublishReport, expected : Int) -> Unit {
  if report.matched != expected {
    panic()
  }
}

///|
pub fn collect_topics(messages : ArrayView[Message]) -> Array[String] {
  let topics = Array::new(capacity=messages.length())
  for msg in messages {
    topics.push(msg.topic)
  }
  topics
}