use crate::backends::base::TracingBackend;
use crate::profile_result::ProfileResult;
use crate::profiler::Profiler;
use std::time::{Instant, SystemTime, UNIX_EPOCH};

pub struct ProfilerTimer {
    name: String,
    start_point: Instant,
    start_timestamp: u64,
    frame_index: usize,
    thread_name: String,
    is_stopped: bool,
}

impl ProfilerTimer {
    pub fn new(name: &str) -> Self {
        let frame_index = Profiler::get().lock().frame_count();
        let thread_name = std::thread::current()
            .name()
            .unwrap_or("unnamed")
            .to_string();
        Self {
            name: name.to_string(),
            start_point: Instant::now(),
            start_timestamp: timestamp_micros(),
            frame_index,
            thread_name,
            is_stopped: false,
        }
    }

    pub fn stop(&mut self) {
        if self.is_stopped {
            return;
        }
        self.is_stopped = true;
        let dur = self.start_point.elapsed().as_micros() as u64;
        let mut result = ProfileResult::complete(
            self.name.clone(),
            self.start_timestamp,
            dur,
            thread_id_u64(),
            process_id_u64(),
        );
        result.args = Some(serde_json::json!({
            "frame": self.frame_index,
            "thread_name": self.thread_name,
        }));
        Profiler::get().lock().record_event(&result);
    }
}

impl Drop for ProfilerTimer {
    fn drop(&mut self) {
        if !self.is_stopped {
            self.stop();
        }
    }
}

fn timestamp_micros() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap()
        .as_micros() as u64
}

fn thread_id_u64() -> u64 {
    let tid = format!("{:?}", std::thread::current().id());
    tid.trim_start_matches("ThreadId(")
        .trim_end_matches(')')
        .parse()
        .unwrap_or(0)
}

fn process_id_u64() -> u64 {
    std::process::id() as u64
}
