///|
/// Resilient Mailbox State Machine (RMSM) per ETG.1000.4.
/// Tracks per-slave mailbox state, manages counter correlation,
/// and provides retry logic for lost/stale frames.

///|
/// RMSM state for a single slave's mailbox channel.
pub(all) enum RmsmState {
  /// No pending transaction; ready to send.
  Idle
  /// Waiting for a response with the given counter value.
  WaitingForResponse(Byte)
  /// Transaction completed with an error; can be reset.
  Error(String)
} derive(Eq, Debug)

///|
/// Per-slave Resilient Mailbox State Machine.
/// Manages counter correlation and repeat detection.
pub(all) struct Rmsm {
  /// Mailbox counter (cycles 1-7).
  counter : MailboxCounter
  /// Current state.
  mut state : RmsmState
  /// Maximum retries for a single transaction.
  max_retries : Int
  /// Current retry count within the active transaction.
  mut retries : Int
  /// Last received counter value for repeat detection.
  mut last_recv_counter : Byte
  /// Current Repeat Request toggle value tracked for mailbox-read replay.
  mut repeat_request : Bool
  /// True while waiting for the SubDevice to mirror RepeatAck.
  mut awaiting_repeat_ack : Bool
} derive(Debug)

///|
pub fn Rmsm::new(max_retries? : Int = 3) -> Rmsm {
  {
    counter: MailboxCounter::new(),
    state: Idle,
    max_retries,
    retries: 0,
    last_recv_counter: b'\x00',
    repeat_request: false,
    awaiting_repeat_ack: false,
  }
}

///|
/// Begin a new mailbox transaction. Returns the counter value to embed.
/// Transitions from Idle → WaitingForResponse.
pub fn Rmsm::begin_transaction(self : Rmsm) -> Byte raise @hal.EcError {
  guard self.state is Idle else {
    raise @hal.EcError::MailboxProtocol(
      "RMSM: cannot begin transaction in state " + @debug.to_string(self.state),
    )
  }
  let cnt = self.counter.next()
  self.state = WaitingForResponse(cnt)
  self.retries = 0
  self.awaiting_repeat_ack = false
  cnt
}

///|
/// Begin a timeout retry transaction without clearing the accumulated retry
/// budget for the surrounding operation.
pub fn Rmsm::begin_retry_transaction(self : Rmsm) -> Byte raise @hal.EcError {
  guard self.state is Idle else {
    raise @hal.EcError::MailboxProtocol(
      "RMSM: cannot begin retry transaction in state " +
      @debug.to_string(self.state),
    )
  }
  let cnt = self.counter.next()
  self.state = WaitingForResponse(cnt)
  self.awaiting_repeat_ack = false
  cnt
}

///|
/// Validate a received mailbox response counter.
/// Returns true if this is a valid (new) response; false if it's a stale or
/// repeat frame that the caller should discard and continue polling.
pub fn Rmsm::validate_response(
  self : Rmsm,
  recv_counter : Byte,
) -> Bool raise @hal.EcError {
  guard self.state is WaitingForResponse(expected) else {
    raise @hal.EcError::MailboxProtocol(
      "RMSM: unexpected response in state " + @debug.to_string(self.state),
    )
  }
  // Check for repeat (same counter as last received)
  if recv_counter == self.last_recv_counter && recv_counter != b'\x00' {
    return false // Repeat frame, discard
  }
  // Validate counter matches expectation
  if recv_counter != expected && recv_counter != b'\x00' {
    // A non-zero unexpected counter is most often a stale frame from an
    // earlier transaction that was still queued in the mailbox. Keep waiting
    // for the current transaction's response and let the caller discard it.
    self.last_recv_counter = recv_counter
    return false
  }
  self.last_recv_counter = recv_counter
  self.state = Idle
  self.awaiting_repeat_ack = false
  true
}

///|
/// Toggle Repeat Request in the SyncManager activate byte to recover a lost
/// mailbox read frame. The returned byte is ready to write back to SMx+0x06.
pub fn Rmsm::begin_read_repeat(
  self : Rmsm,
  activate : Byte,
) -> Byte raise @hal.EcError {
  guard self.state is WaitingForResponse(_) else {
    raise @hal.EcError::MailboxProtocol(
      "RMSM: cannot request mailbox repeat in state " +
      @debug.to_string(self.state),
    )
  }
  self.repeat_request = !self.repeat_request
  self.awaiting_repeat_ack = true
  { request: self.repeat_request, acknowledge: false }.apply_to_activate_byte(
    activate,
  )
}

///|
/// Check whether the SubDevice has mirrored the RepeatAck bit for the current
/// read-repeat sequence. Returns true exactly when the expected toggle is seen.
pub fn Rmsm::observe_repeat_ack(self : Rmsm, pdi_control : Byte) -> Bool {
  let repeat = MailboxRepeatState::from_sync_manager_bytes(b'\x00', pdi_control)
  if self.awaiting_repeat_ack && repeat.acknowledge == self.repeat_request {
    self.awaiting_repeat_ack = false
    true
  } else {
    false
  }
}

///|
/// Report whether a mailbox-read repeat is currently waiting for RepeatAck.
pub fn Rmsm::awaits_repeat_ack(self : Rmsm) -> Bool {
  self.awaiting_repeat_ack
}

///|
/// Record a timeout and check if we should retry.
/// Returns true if a retry is allowed, false if retries exhausted.
pub fn Rmsm::record_timeout(self : Rmsm) -> Bool {
  self.retries = self.retries + 1
  if self.retries > self.max_retries {
    self.state = Error(
      "RMSM: retries exhausted (" + self.max_retries.to_string() + ")",
    )
    false
  } else {
    // Stay in WaitingForResponse; caller should resend
    true
  }
}

///|
/// Reset the RMSM to Idle between retry attempts while preserving the retry
/// budget already spent by the caller.
pub fn Rmsm::reset_for_retry(self : Rmsm) -> Unit {
  self.state = Idle
  self.awaiting_repeat_ack = false
}

///|
/// Reset the RMSM to Idle state, discarding any pending transaction.
pub fn Rmsm::reset(self : Rmsm) -> Unit {
  self.state = Idle
  self.retries = 0
  self.awaiting_repeat_ack = false
}

///|
/// Check if the RMSM is ready for a new transaction.
pub fn Rmsm::is_idle(self : Rmsm) -> Bool {
  self.state is Idle
}

///|
/// Get the current counter value that would be used for the next send
/// (without actually beginning a transaction). For repeat sends.
pub fn Rmsm::current_counter(self : Rmsm) -> Byte {
  guard self.state is WaitingForResponse(cnt) else { return b'\x00' }
  cnt
}