//! Pulse NIC Co-optimization Module (X-03)
//!
//! Provides integration points for hardware NIC offloading.
//! Currently detects and profiles standard OS interface speeds and stats.
//! Full DPDK/AF_XDP zero-copy is marked for future implementation.

use serde::{Deserialize, Serialize};
use std::fs;
use std::path::Path;
use tracing::{debug, info, warn};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum XdpMode {
    Native,
    Generic,
    Offloaded,
    None,
}

/// Configuration for the hardware acceleration layer.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NicConfig {
    pub interface: String,
    pub zero_copy_enabled: bool,
    pub xdp_mode: XdpMode,
}

impl NicConfig {
    pub fn probe_interface(interface_name: &str) -> Self {
        info!(
            "[NIC] Probing network interface `{}` for offload capabilities",
            interface_name
        );

        #[allow(unused_mut)]
        let mut xdp_mode = XdpMode::None;

        // On Linux, we would verify XDP capability via netlink or sysfs
        #[cfg(target_os = "linux")]
        {
            let sysfs_path = format!("/sys/class/net/{}/device", interface_name);
            if Path::new(&sysfs_path).exists() {
                debug!("Interface {} found in sysfs", interface_name);
                // Assume Generic/Native fallback if AF_XDP isn't bound yet
                xdp_mode = XdpMode::Generic;
            } else {
                warn!(
                    "Interface {} not found in sysfs or unsupported",
                    interface_name
                );
            }
        }

        #[cfg(not(target_os = "linux"))]
        {
            warn!(
                "NIC hardware offload probing is strictly supported on Linux. Defaulting to standard sockets."
            );
        }

        Self {
            interface: interface_name.to_string(),
            zero_copy_enabled: false, // honest: not implemented yet
            xdp_mode,
        }
    }
}

/// Tracks NIC-level statistics for the relay operator dashboard.
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct NicStats {
    pub rx_packets: u64,
    pub tx_packets: u64,
    pub rx_bytes: u64,
    pub tx_bytes: u64,
    pub rx_drops: u64,
    pub tx_drops: u64,
    pub rx_errors: u64,
}

impl NicStats {
    /// Read physical OS NIC statistics if on Linux.
    pub fn read_from_os(interface: &str) -> Result<Self, std::io::Error> {
        #[cfg(target_os = "linux")]
        {
            let base = format!("/sys/class/net/{}/statistics", interface);

            let read_u64 = |file: &str| -> u64 {
                let path = format!("{}/{}", base, file);
                fs::read_to_string(&path)
                    .unwrap_or_else(|_| "0".to_string())
                    .trim()
                    .parse()
                    .unwrap_or(0)
            };

            return Ok(Self {
                rx_packets: read_u64("rx_packets"),
                tx_packets: read_u64("tx_packets"),
                rx_bytes: read_u64("rx_bytes"),
                tx_bytes: read_u64("tx_bytes"),
                rx_drops: read_u64("rx_dropped"),
                tx_drops: read_u64("tx_dropped"),
                rx_errors: read_u64("rx_errors"),
            });
        }

        #[cfg(not(target_os = "linux"))]
        {
            let _ = interface; // Suppress unused var warning on non-Linux
            // Cannot reliably read generic interface stats across non-Linux without pcap/external deps
            Ok(Self::default())
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_nic_config_fallback() {
        let config = NicConfig::probe_interface("lo");
        assert_eq!(config.interface, "lo");
        assert!(!config.zero_copy_enabled);
    }

    #[test]
    fn test_nic_stats_non_existent() {
        let stats = NicStats::read_from_os("nonexistent0").expect("Should not panic");
        assert_eq!(stats.rx_packets, 0);
        assert_eq!(stats.tx_packets, 0);
    }
}
