//! Monotonic and wall-clock binding.
//!
//! `MonoClock` is authoritative for tick budgets, telemetry-skew compensation,
//! and inter-frame correlation. `WallClock` is GPS-bound when locked and NTP at
//! boot. Drift > 200 ms surfaces as yellow health on the affected component.

use std::time::Instant;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClockSource {
    Gnss,
    Host,
    Coast,
}

/// Process-monotonic clock — never goes backwards, immune to NTP adjustments.
#[derive(Debug, Clone, Copy)]
pub struct MonoClock {
    boot: Instant,
}

impl MonoClock {
    pub fn new() -> Self {
        Self {
            boot: Instant::now(),
        }
    }

    /// Nanoseconds since this clock was constructed.
    pub fn elapsed_ns(&self) -> u64 {
        self.boot.elapsed().as_nanos() as u64
    }
}

impl Default for MonoClock {
    fn default() -> Self {
        Self::new()
    }
}

/// Wall-clock binding — produced from `MonoClock` via the active `ClockSource`.
/// Drift beyond the threshold MUST be surfaced as a yellow health detail.
#[derive(Debug, Clone)]
pub struct WallClock {
    pub source: ClockSource,
}

impl WallClock {
    pub fn new(source: ClockSource) -> Self {
        Self { source }
    }

    pub fn now(&self) -> chrono::DateTime<chrono::Utc> {
        chrono::Utc::now()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn mono_clock_is_monotonic() {
        let clock = MonoClock::new();
        let t1 = clock.elapsed_ns();
        let t2 = clock.elapsed_ns();

        assert!(t2 >= t1, "monotonic clock went backwards: {t1} -> {t2}");
    }
}
