///|
/// Mutable Hybrid Logical Clock state for one replica.
pub(all) struct HlcClock {
  node : String
  mut last : HlcTimestamp
} derive(Debug)

///| Create a clock with an initial physical time. The first generated event at

///|
/// the same physical time receives logical counter one.
pub fn HlcClock::new(
  node : String,
  initial_physical : Int,
) -> Result[HlcClock, TimestampError] {
  match HlcTimestamp::new(initial_physical, 0, node) {
    Ok(last) => Ok({ node: last.node, last })
    Err(error) => Err(error)
  }
}

///|
/// Return the last generated timestamp without advancing the clock.
pub fn HlcClock::last(self : HlcClock) -> HlcTimestamp {
  self.last
}

///| Advance for a local event. A physical clock moving backwards is safe: the

///|
/// clock keeps the known physical component and advances its logical counter.
pub fn HlcClock::tick(self : HlcClock, physical_now : Int) -> HlcTimestamp {
  let physical = max_int(self.last.physical, physical_now)
  let logical = if physical_now > self.last.physical {
    0
  } else {
    self.last.logical + 1
  }
  let next = { physical, logical, node: self.node }
  self.last = next
  next
}

///| Merge a remote HLC timestamp and advance for the receive event. This is the

///| standard HLC receive rule: the resulting timestamp is strictly later than

///|
/// both the local state and the received timestamp.
pub fn HlcClock::receive(
  self : HlcClock,
  remote : HlcTimestamp,
  physical_now : Int,
) -> HlcTimestamp {
  let current = self.last
  let physical = max_int(
    max_int(current.physical, remote.physical),
    physical_now,
  )
  let logical = if physical == current.physical && physical == remote.physical {
    max_int(current.logical, remote.logical) + 1
  } else if physical == current.physical {
    current.logical + 1
  } else if physical == remote.physical {
    remote.logical + 1
  } else {
    0
  }
  let next = { physical, logical, node: self.node }
  self.last = next
  next
}

///| Restore a persisted state after validating that the timestamp belongs to

///| the same replica. This prevents accidentally continuing another node's

///|
/// sequence under a new identity.
pub fn HlcClock::restore(
  node : String,
  last : HlcTimestamp,
) -> Result[HlcClock, ClockError] {
  if node == last.node {
    Ok({ node, last })
  } else {
    Err(NodeMismatch(expected=node, found=last.node))
  }
}

///|
/// Clock-specific domain errors.
pub(all) enum ClockError {
  NodeMismatch(expected~ : String, found~ : String)
} derive(Eq, Debug)

///|
/// Keep arithmetic local instead of adding a dependency for one operation.
fn max_int(left : Int, right : Int) -> Int {
  if left > right {
    left
  } else {
    right
  }
}