use std::fmt::Write;
use std::sync::Mutex;
use std::time::Instant;

use crate::app_config::DevOrProd;

#[derive(Debug)]
pub struct ServerTiming {
    enabled: bool,
    created_at: Instant,
    events: Mutex<Vec<PerfEvent>>,
}

#[derive(Debug)]
struct PerfEvent {
    time: Instant,
    name: &'static str,
}

impl Default for ServerTiming {
    fn default() -> Self {
        Self {
            enabled: false,
            created_at: Instant::now(),
            events: Mutex::new(Vec::new()),
        }
    }
}

impl ServerTiming {
    #[must_use]
    pub fn enabled(enabled: bool) -> Self {
        Self {
            enabled,
            ..Default::default()
        }
    }

    #[must_use]
    pub fn for_env(env: DevOrProd) -> Self {
        Self::enabled(!env.is_prod())
    }

    pub fn record(&self, name: &'static str) {
        if self.enabled {
            self.events.lock().unwrap().push(PerfEvent {
                time: Instant::now(),
                name,
            });
        }
    }

    pub fn header_value(&self) -> Option<String> {
        if !self.enabled {
            return None;
        }
        let evts = self.events.lock().unwrap();
        let mut s = String::with_capacity(evts.len() * 16);
        let mut last = self.created_at;
        for (i, PerfEvent { name, time }) in evts.iter().enumerate() {
            if i > 0 {
                s.push_str(", ");
            }
            let micros = time.saturating_duration_since(last).as_micros();
            let millis = micros / 1000;
            let micros = micros % 1000;
            write!(&mut s, "{name};dur={millis}.{micros:03}").ok()?;
            last = *time;
        }
        Some(s)
    }
}
