// Copyright 2026 PingCAP, Inc.

use std::{
    sync::atomic::{AtomicBool, AtomicU64, Ordering},
    time::{Duration, Instant},
};

use crate::{AtomicDuration, AtomicTime, TimeUnit};

pub struct TotalCpuMonitor {
    ratio_bits: AtomicU64,
    available: AtomicBool,
    last_update: AtomicTime,
    max_staleness: AtomicDuration,
}

impl Default for TotalCpuMonitor {
    fn default() -> Self {
        Self {
            ratio_bits: AtomicU64::new(0.0f64.to_bits()),
            available: AtomicBool::new(false),
            last_update: AtomicTime::new(Instant::now(), TimeUnit::Millisecond),
            max_staleness: AtomicDuration::new(Duration::from_secs(1), TimeUnit::Millisecond),
        }
    }
}

impl TotalCpuMonitor {
    pub fn set_ratio(&self, ratio: f64) {
        self.ratio_bits.store(ratio.to_bits(), Ordering::Release);
        self.last_update.store(Instant::now());
        self.available.store(true, Ordering::Release);
    }

    pub fn clear(&self) {
        self.available.store(false, Ordering::Release);
    }

    pub(crate) fn set_max_staleness(&self, max_staleness: Duration) {
        self.max_staleness.store(max_staleness);
    }

    pub fn ratio(&self) -> Option<f64> {
        if !self.available.load(Ordering::Acquire) {
            return None;
        }
        if Instant::now().saturating_duration_since(self.last_update.load())
            > self.max_staleness.load()
        {
            return None;
        }
        Some(f64::from_bits(self.ratio_bits.load(Ordering::Acquire)))
    }
}
