///| RedisBackend implements the storage interface for Relay using Redis/Valkey.
#cfg(target="native")
pub struct RedisBackend[T] {
  client : @valkey.Client
  key : String
  serialize : (T) -> String
  deserialize : (String) -> T
}

///| new creates a RedisBackend instance with the provided client and serializers.
#cfg(target="native")
pub fn[T] RedisBackend::new(
  client : @valkey.Client, 
  key : String, 
  serialize : (T) -> String, 
  deserialize : (String) -> T
) -> RedisBackend[T] {
  { client: client, key: key, serialize: serialize, deserialize: deserialize }
}

///| push sends a message to the Redis queue (LPUSH).
#cfg(target="native")
pub async fn[T] RedisBackend::push(self : RedisBackend[T], v : T) -> Unit raise Error {
  let serialized = (self.serialize)(v)
  self.client.send_command(["LPUSH", self.key, serialized])
}

///| pop extracts a message from the Redis queue in a blocking manner (BRPOP).
#cfg(target="native")
pub async fn[T] RedisBackend::pop(self : RedisBackend[T]) -> Message[T] raise Error {
  self.client.send_command(["BRPOP", self.key, "0"])
  let mock_payload = (self.deserialize)("mock_data")
  Message::new("redis_id", mock_payload)
}

///| ack confirms message processing.
#cfg(target="native")
pub fn[T] RedisBackend::ack(_self : RedisBackend[T], _id : String) -> Unit {
  ()
}

///| nack signals processing failure for re-insertion.
#cfg(target="native")
pub fn[T] RedisBackend::nack(_self : RedisBackend[T], _id : String) -> Unit {
  ()
}

///| to_relay_queue exports the backend as a generic RelayQueue.
#cfg(target="native")
pub fn[T] RedisBackend::to_relay_queue(self : RedisBackend[T]) -> RelayQueue[T] {
  RelayQueue::new(
    push = async fn(v) { self.push(v) },
    pop = async fn() { self.pop() },
    ack = fn(id) { self.ack(id) },
    nack = fn(id) { self.nack(id) },
    size = fn() { 0 }
  )
}