#![deny(warnings)]
#![allow(dead_code)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-harmonic -- Cross-service Tempo Synchronization
//! Distributed clock sync, heartbeat coordination, event ordering

use std::collections::HashMap;
use std::sync::{Arc, Mutex};

#[derive(Debug)]
pub enum HarmonicError { ClockDrift(String), SyncFailed(String), NodeLost(String) }
impl std::fmt::Display for HarmonicError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self { Self::ClockDrift(e) => write!(f, "Drift: {e}"), Self::SyncFailed(e) => write!(f, "Sync: {e}"), Self::NodeLost(e) => write!(f, "Lost: {e}") }
    }
}
impl std::error::Error for HarmonicError {}

/// Hybrid Logical Clock (HLC) — combination of physical + logical time
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct HybridTimestamp { pub physical: u64, pub logical: u32, pub node_id: u16 }

impl HybridTimestamp {
    pub fn new(physical: u64, node_id: u16) -> Self { Self { physical, logical: 0, node_id } }
    /// Update on local event
    pub fn tick(&mut self, now: u64) {
        if now > self.physical { self.physical = now; self.logical = 0; }
        else { self.logical += 1; }
    }
    /// Merge with received timestamp (Lamport-style)
    pub fn merge(&mut self, other: &HybridTimestamp, now: u64) {
        if now > self.physical && now > other.physical { self.physical = now; self.logical = 0; }
        else if self.physical == other.physical { self.logical = self.logical.max(other.logical) + 1; }
        else if other.physical > self.physical { self.physical = other.physical; self.logical = other.logical + 1; }
        else { self.logical += 1; }
    }
    pub fn to_bytes(&self) -> [u8; 14] {
        let mut buf = [0u8; 14];
        buf[..8].copy_from_slice(&self.physical.to_be_bytes());
        buf[8..12].copy_from_slice(&self.logical.to_be_bytes());
        buf[12..14].copy_from_slice(&self.node_id.to_be_bytes());
        buf
    }
}

#[derive(Debug, Clone)]
pub struct HeartbeatRecord { pub node_id: String, pub timestamp: HybridTimestamp, pub drift_ms: i64, pub is_alive: bool }

pub struct Harmonic {
    clock: Arc<Mutex<HybridTimestamp>>,
    node_id: u16,
    heartbeats: Arc<Mutex<HashMap<String, HeartbeatRecord>>>,
    max_drift_ms: i64,
}

impl Harmonic {
    pub fn new(node_id: u16, max_drift_ms: i64) -> Self {
        Self {
            clock: Arc::new(Mutex::new(HybridTimestamp::new(0, node_id))),
            node_id, max_drift_ms,
            heartbeats: Arc::new(Mutex::new(HashMap::new())),
        }
    }
    pub fn now(&self, physical_now: u64) -> Result<HybridTimestamp, HarmonicError> {
        let mut clock = self.clock.lock().map_err(|_| HarmonicError::SyncFailed("Lock".into()))?;
        clock.tick(physical_now);
        Ok(*clock)
    }
    pub fn receive(&self, remote: &HybridTimestamp, physical_now: u64) -> Result<HybridTimestamp, HarmonicError> {
        let mut clock = self.clock.lock().map_err(|_| HarmonicError::SyncFailed("Lock".into()))?;
        let drift = physical_now as i64 - remote.physical as i64;
        if drift.abs() > self.max_drift_ms {
            return Err(HarmonicError::ClockDrift(format!("{}ms exceeds {}ms", drift, self.max_drift_ms)));
        }
        clock.merge(remote, physical_now);
        Ok(*clock)
    }
    pub fn record_heartbeat(&self, node_name: &str, remote_ts: HybridTimestamp, local_now: u64) -> Result<(), HarmonicError> {
        let drift = local_now as i64 - remote_ts.physical as i64;
        let record = HeartbeatRecord { node_id: node_name.into(), timestamp: remote_ts, drift_ms: drift, is_alive: drift.abs() < self.max_drift_ms };
        if let Ok(mut hb) = self.heartbeats.lock() { hb.insert(node_name.into(), record); }
        Ok(())
    }
    pub fn alive_nodes(&self) -> Vec<String> {
        self.heartbeats.lock().map(|hb| hb.values().filter(|r| r.is_alive).map(|r| r.node_id.clone()).collect()).unwrap_or_default()
    }
    pub fn max_drift(&self) -> Result<i64, HarmonicError> {
        let hb = self.heartbeats.lock().map_err(|_| HarmonicError::SyncFailed("Lock".into()))?;
        Ok(hb.values().map(|r| r.drift_ms.abs()).max().unwrap_or(0))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_hlc_tick() {
        let h = Harmonic::new(1, 5000);
        let t1 = h.now(1000).unwrap();
        let t2 = h.now(1000).unwrap();
        assert!(t2 > t1); // logical incremented
    }
    #[test]
    fn test_hlc_merge() {
        let h = Harmonic::new(1, 5000);
        let remote = HybridTimestamp { physical: 2000, logical: 5, node_id: 2 };
        let t = h.receive(&remote, 1999).unwrap();
        assert_eq!(t.physical, 2000);
        assert!(t.logical > 5);
    }
    #[test]
    fn test_drift_detection() {
        let h = Harmonic::new(1, 100);
        let remote = HybridTimestamp { physical: 1000, logical: 0, node_id: 2 };
        assert!(h.receive(&remote, 2000).is_err()); // 1000ms drift > 100ms limit
    }
}
