///|
/// RetryPolicy defines how the queue handles failed message processing.
pub struct RetryPolicy {
  max_attempts : Int
}

///|
/// new_policy creates a custom retry policy.
pub fn RetryPolicy::new(max_attempts : Int) -> RetryPolicy {
  { max_attempts, }
}

///|
/// default_policy provides a standard policy with 3 max attempts.
pub fn RetryPolicy::default() -> RetryPolicy {
  { max_attempts: 3 }
}

///|
/// Result of a negative acknowledgement.
pub enum NackResult {
  Requeued
  SentToDlq
}

///|
/// RelayQueue is the main entry point for interacting with the message queue.
/// It uses a closure-based backend abstraction to support multiple storage engines (Memory, Redis, etc.).
pub struct RelayQueue[T] {
  push_fn : async (T) -> Unit raise Error
  pop_fn : async () -> Message[T] raise Error
  ack_fn : async (String) -> Unit raise Error
  nack_fn : async (String) -> NackResult raise Error
  size_fn : () -> Int
}

///|
/// new creates a new RelayQueue with the provided backend functions.
pub fn[T] RelayQueue::new(
  push~ : async (T) -> Unit raise Error,
  pop~ : async () -> Message[T] raise Error,
  ack~ : async (String) -> Unit raise Error,
  nack~ : async (String) -> NackResult raise Error,
  size~ : () -> Int,
) -> RelayQueue[T] {
  { push_fn: push, pop_fn: pop, ack_fn: ack, nack_fn: nack, size_fn: size }
}

///|
/// push adds a value to the queue. Asynchronous and thread-safe via the underlying backend.
pub async fn[T] RelayQueue::push(
  self : RelayQueue[T],
  value : T,
) -> Unit raise Error {
  (self.push_fn)(value)
}

///|
/// pop removes and returns a value from the queue. Suspends if the queue is empty.
/// In V3, a popped message is considered "In Flight" until acknowledged.
pub async fn[T] RelayQueue::pop(self : RelayQueue[T]) -> Message[T] raise Error {
  (self.pop_fn)()
}

///|
/// ack confirms that a message has been processed successfully.
/// The backend will permanently remove the message from its tracking system.
pub async fn[T] RelayQueue::ack(
  self : RelayQueue[T],
  id : String,
) -> Unit raise Error {
  (self.ack_fn)(id)
}

///|
/// nack (negative acknowledgement) informs the queue that processing failed.
/// The backend will typically re-queue the message for another attempt, unless it exceeds the RetryPolicy.
pub async fn[T] RelayQueue::nack(
  self : RelayQueue[T],
  id : String,
) -> NackResult raise Error {
  (self.nack_fn)(id)
}

///|
/// size returns the current number of items in the queue.
pub fn[T] RelayQueue::size(self : RelayQueue[T]) -> Int {
  (self.size_fn)()
}