//! Process CPU usage profiling

use std::fs;
use std::path::Path;

use super::super::error::ProfilingError;

/// CPU usage profile for a process
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CPUUsageProfile {
    pub pid: u32,
    pub cpu_percent: f64,
    pub user_time: std::time::Duration,
    pub system_time: std::time::Duration,
    pub duration: std::time::Duration,
}

/// Profile CPU usage of a process
pub fn profile_cpu_usage(
    pid: u32,
    duration: std::time::Duration,
) -> Result<CPUUsageProfile, ProfilingError> {
    let stat_path_str = format!("/proc/{}/stat", pid);
    let stat_path = Path::new(&stat_path_str);

    if !stat_path.exists() {
        return Err(ProfilingError::IoError(format!(
            "Process {} not found",
            pid
        )));
    }

    let start_time = std::time::Instant::now();
    let start_stat = read_proc_stat(pid)?;

    std::thread::sleep(duration);

    let end_time = std::time::Instant::now();
    let end_stat = read_proc_stat(pid)?;

    let elapsed = end_time.duration_since(start_time);
    let user_time_delta = end_stat.user_time - start_stat.user_time;
    let system_time_delta = end_stat.system_time - start_stat.system_time;
    let total_cpu_time = user_time_delta + system_time_delta;

    let cpu_percent = if elapsed.as_nanos() > 0 {
        (total_cpu_time.as_nanos() as f64 / elapsed.as_nanos() as f64)
            * 100.0
            * num_cpus::get() as f64
    } else {
        0.0
    };

    Ok(CPUUsageProfile {
        pid,
        cpu_percent: cpu_percent.min(100.0),
        user_time: user_time_delta,
        system_time: system_time_delta,
        duration: elapsed,
    })
}

struct ProcStat {
    user_time: std::time::Duration,
    system_time: std::time::Duration,
}

fn read_proc_stat(pid: u32) -> Result<ProcStat, ProfilingError> {
    let stat_path_str = format!("/proc/{}/stat", pid);
    let stat_path = Path::new(&stat_path_str);
    let content = fs::read_to_string(stat_path)
        .map_err(|e| ProfilingError::IoError(format!("Failed to read stat: {}", e)))?;

    let parts: Vec<&str> = content.split_whitespace().collect();
    if parts.len() < 17 {
        return Err(ProfilingError::ParseError(
            "Invalid /proc/pid/stat format".to_string(),
        ));
    }

    let utime: u64 = parts[13].parse().unwrap_or(0);
    let stime: u64 = parts[14].parse().unwrap_or(0);

    let user_time = std::time::Duration::from_nanos(utime * 1_000_000); // jiffies to nanoseconds
    let system_time = std::time::Duration::from_nanos(stime * 1_000_000);

    Ok(ProcStat {
        user_time,
        system_time,
    })
}
