//! System profile detection

use super::error::ProfilingError;
use serde::{Deserialize, Serialize};
use sysinfo;

/// System identification and topology information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemProfile {
    pub hostname: String,
    pub os_name: String,
    pub os_version: String,
    pub kernel_version: String,
    pub cpu_model: String,
    pub cpu_cores: u32,
    pub cpu_threads: u32,
    pub memory_total_bytes: u64,
    pub numa_nodes: Option<u32>,
}

impl SystemProfile {
    /// Detect system profile using cross-platform sysinfo crate
    pub fn detect() -> Result<Self, ProfilingError> {
        let mut system = sysinfo::System::new_all();
        system.refresh_all();

        let hostname = Self::get_hostname()?;
        let (os_name, os_version) = Self::get_os_info(&system);
        let kernel_version = Self::get_kernel_version();
        let (cpu_model, cpu_cores, cpu_threads) = Self::get_cpu_info(&system);
        let memory_total_bytes = Self::get_memory_info(&system);
        let numa_nodes = Self::detect_numa();

        Ok(Self {
            hostname,
            os_name,
            os_version,
            kernel_version,
            cpu_model,
            cpu_cores,
            cpu_threads,
            memory_total_bytes,
            numa_nodes,
        })
    }

    fn get_hostname() -> Result<String, ProfilingError> {
        std::env::var("HOSTNAME")
            .or_else(|_: std::env::VarError| {
                // Use sysinfo for cross-platform hostname detection
                sysinfo::System::host_name()
                    .map(|hostname| hostname.to_string())
                    .ok_or_else(|| {
                        std::io::Error::new(std::io::ErrorKind::NotFound, "Hostname not available")
                    })
            })
            .or_else(|_: std::io::Error| Ok("localhost".to_string()))
            .map_err(|e: std::env::VarError| {
                ProfilingError::IoError(format!("Failed to get hostname: {}", e))
            })
    }

    fn get_os_info(_system: &sysinfo::System) -> (String, String) {
        let os_name = sysinfo::System::name().unwrap_or_else(|| "Unknown".to_string());
        let os_version = sysinfo::System::os_version().unwrap_or_else(|| "Unknown".to_string());
        (os_name, os_version)
    }

    fn get_kernel_version() -> String {
        // Use sysinfo for cross-platform kernel version detection
        sysinfo::System::kernel_version().unwrap_or_else(|| "Unknown".to_string())
    }

    fn get_cpu_info(system: &sysinfo::System) -> (String, u32, u32) {
        let cpu_model = system
            .cpus()
            .first()
            .map(|cpu| cpu.brand().to_string())
            .unwrap_or_else(|| "Unknown".to_string());

        let physical_cores = system.physical_core_count().unwrap_or(1) as u32;
        let logical_cores = num_cpus::get() as u32;

        (cpu_model, physical_cores, logical_cores)
    }

    fn get_memory_info(system: &sysinfo::System) -> u64 {
        system.total_memory()
    }

    fn detect_numa() -> Option<u32> {
        #[cfg(target_os = "linux")]
        {
            std::fs::read_dir("/sys/devices/system/node")
                .ok()?
                .filter_map(Result::ok)
                .filter(|entry| entry.file_name().to_string_lossy().starts_with("node"))
                .count()
                .checked_sub(1)
                .filter(|&count| count > 0)
                .map(|count| count as u32 + 1)
        }

        #[cfg(target_os = "macos")]
        {
            // macOS doesn't expose NUMA architecture
            None
        }

        #[cfg(target_os = "windows")]
        {
            use windows::Win32::System::SystemInformation::GetNumaHighestNodeNumber;

            unsafe {
                let mut highest_node: u32 = 0;
                if GetNumaHighestNodeNumber(&mut highest_node).is_ok() && highest_node > 0 {
                    Some(highest_node + 1) // Return count, not index
                } else {
                    None
                }
            }
        }

        #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))]
        {
            None
        }
    }
}
