//! Hybrid Logical Clock (HLC).
//!
//! Carries a physical wall-clock estimate (`physical`, unix nanos) and a
//! logical counter (`logical`) so that events that share a physical instant
//! still have a total order. `tick(prev, now)` advances the clock so the
//! result is strictly greater than `prev`.
//!
//! Field declaration order is lexicographic (`logical` before `physical`) so
//! that ciborium emits canonical CBOR map keys in sorted order; the natural
//! ordering by `(physical, logical)` is restored by manual `Ord`/`PartialOrd`
//! impls.

use std::cmp::Ordering;

use serde::{Deserialize, Serialize};

/// HLC timestamp.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Hlc {
    /// Logical counter — incremented when physical did not advance.
    pub logical: u32,
    /// Physical wall-clock estimate (unix nanoseconds, opaque to the kernel).
    pub physical: u64,
}

impl Hlc {
    /// Construct an HLC directly.
    pub const fn new(physical: u64, logical: u32) -> Self {
        Self { logical, physical }
    }

    /// Genesis HLC (zero, zero).
    pub const fn genesis() -> Self {
        Self::new(0, 0)
    }

    /// Advance the clock. Given the previous-max HLC and a current physical
    /// reading, returns the next HLC. The result is strictly greater than
    /// `prev` under the `(physical, logical)` total order.
    pub fn tick(prev: Hlc, now_physical: u64) -> Self {
        if now_physical > prev.physical {
            Self::new(now_physical, 0)
        } else {
            Self::new(prev.physical, prev.logical.saturating_add(1))
        }
    }
}

impl PartialOrd for Hlc {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Hlc {
    fn cmp(&self, other: &Self) -> Ordering {
        (self.physical, self.logical).cmp(&(other.physical, other.logical))
    }
}
