///|
/// Durable topic configuration used by provisioning tools.
pub struct TopicSpec {
  name : String
  partitions : Int
  retention_days : Int
  max_message_bytes : Int
} derive(Debug)

///|
pub extend TopicSpec with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub fn TopicSpec::new(
  name : String,
  partitions? : Int = 1,
  retention_days? : Int = 7,
  max_message_bytes? : Int = 1_000_000,
) -> TopicSpec raise ProtocolError {
  validate_name(name)
  if partitions < 1 || partitions > 1_024 {
    raise InvalidOption("partitions must be between 1 and 1024")
  }
  if retention_days < 0 || retention_days > 3_650 {
    raise InvalidOption("retention_days must be between 0 and 3650")
  }
  if max_message_bytes < 1 || max_message_bytes > max_frame_size - 30 {
    raise InvalidOption("max_message_bytes exceeds NSQ frame capacity")
  }
  { name, partitions, retention_days, max_message_bytes, }
}

///|
pub fn TopicSpec::name(self : TopicSpec) -> String {
  self.name
}

///|
pub fn TopicSpec::partitions(self : TopicSpec) -> Int {
  self.partitions
}

///|
pub fn TopicSpec::retention_days(self : TopicSpec) -> Int {
  self.retention_days
}

///|
pub fn TopicSpec::max_message_bytes(self : TopicSpec) -> Int {
  self.max_message_bytes
}

///|
/// Channel configuration paired with a topic.
pub struct ChannelSpec {
  topic : String
  name : String
  max_in_flight : Int
  max_attempts : Int
} derive(Debug)

///|
pub extend ChannelSpec with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub fn ChannelSpec::new(
  topic : String,
  name : String,
  max_in_flight? : Int = 1,
  max_attempts? : Int = 5,
) -> ChannelSpec raise ProtocolError {
  validate_name(topic)
  validate_name(name)
  if max_in_flight < 1 || max_in_flight > 2_500 {
    raise InvalidOption("channel max_in_flight must be between 1 and 2500")
  }
  if max_attempts < 1 || max_attempts > 65_535 {
    raise InvalidOption("channel max_attempts must be between 1 and 65535")
  }
  { topic, name, max_in_flight, max_attempts, }
}

///|
pub fn ChannelSpec::topic(self : ChannelSpec) -> String {
  self.topic
}

///|
pub fn ChannelSpec::name(self : ChannelSpec) -> String {
  self.name
}

///|
pub fn ChannelSpec::max_in_flight(self : ChannelSpec) -> Int {
  self.max_in_flight
}

///|
pub fn ChannelSpec::max_attempts(self : ChannelSpec) -> Int {
  self.max_attempts
}

///|
/// A validated provisioning plan for one topic and its channels.
pub struct TopologyPlan {
  topic : TopicSpec
  channels : Array[ChannelSpec]
} derive(Debug)

///|
pub extend TopologyPlan with @moonbitlang/core/debug.Debug::{to_repr}

///|
pub fn TopologyPlan::new(
  topic : TopicSpec,
  channels : Array[ChannelSpec],
) -> TopologyPlan raise ProtocolError {
  if channels.length() == 0 {
    raise InvalidOption("topology plan needs at least one channel")
  }
  for channel in channels {
    if channel.topic() != topic.name() {
      raise InvalidOption("channel topic does not match topology topic")
    }
  }
  for i in 0.. TopicSpec {
  self.topic
}

///|
pub fn TopologyPlan::channels(self : TopologyPlan) -> Array[ChannelSpec] {
  self.channels.copy()
}

///|
pub fn TopologyPlan::contains_channel(
  self : TopologyPlan,
  name : String,
) -> Bool {
  for channel in self.channels {
    if channel.name() == name {
      return true
    }
  }
  false
}