// submodule needed to get metrics such as
// cpu load, ram/rom load and net activity

// use std::sync::Mutex;
use std::sync::Arc;
use crate::options::structs::TrackingProcess;
use sysinfo::{Process, System};
use tokio::join;
use crate::options::structs::{ProcessMetrics, ContainerMetrics};
use crate::utils::get_container_id;
// use pcap::{Device, Capture, Active};
// use std::net::Ipv4Addr;
// use anyhow::{Result, Ok};

// type PacketBuffer = Arc<Mutex<Vec<PacketInfo>>>;

/// # Fn `init_metrics_grubber`
/// ## for initializing process of unstoppable grubbing metrics.
/// 
/// *input*      : `Arc<Mutex<UnixSocket>>` ??
///
/// *output*     : `Err` if it cant create grubbers | `Ok` on finish
///
/// *initiator*  : main thread ?? 
///
/// *managing*   : object of unix-socket reader 
///
/// *depends on* : -  
/// 
#[allow(dead_code)]
pub async fn init_metrics_grubber() { 
    let mut system = System::new();
    // let mut buffer: Vec<PacketInfo> = vec![];
    // let shared_buf: PacketBuffer = Arc::new(Mutex::new(buffer));

    system.refresh_all();
    // let temp =  String::from_utf8(get_pid("systemd").await.unwrap().stdout).unwrap();
    // let prc = system.process(Pid::from_str(&temp).unwrap()).unwrap();
    // prc.
    // let _ = capture_packets(shared_buf.clone()).await;
}

#[allow(dead_code)]
#[allow(unused_variables)]
async fn gather_metrics(proc: Arc<Process>) {
    
}

// DEPRECATED : for net monitoring  
// async fn capture_packets(buffer: PacketBuffer) -> Result<()> {
//     let mut cap = Capture::from_device(Device::lookup()?.unwrap())?
//         .promisc(true)
//         .open()?;

//     cap.filter("not broadcast and not multicast", true)?;

//     while let core::result::Result::Ok(packet) = cap.next_packet() {
//         if let Some((src, dst, prot)) = get_packet_info(&packet.data).await {
//             let packet_info = PacketInfo::new(String::from(prot), dst, src, packet.header.len as usize);
//             let mut locked_buffer = buffer.lock().unwrap();
//             println!("{:?}", &packet_info);
//             locked_buffer.push(packet_info);
//         }
//     }
//     Ok(())
// }
// async fn get_packet_info(data: &[u8]) -> Option<(Ipv4Addr, Ipv4Addr, &str)> {
//     if data.len() >= 20 {
//         let src_ip = Ipv4Addr::new(data[12], data[13], data[14], data[15]);
//         let dst_ip = Ipv4Addr::new(data[16], data[17], data[18], data[19]);
//         let protocol = match data[9] {
//             1 => "ICMP",
//             6 => "TCP",
//             17 => "UDP",
//             _ => "Unknown",
//         };
        
//         Some((src_ip, dst_ip, protocol))
//     } else {
//         None
//     }
// }


/// # Fn `get_all_container_metrics`
/// ## for gathering all container (whole system metrics)
/// 
/// *input*      : `Arc<System>`, `Arc<Vec<TrackingProcess>>`
///
/// *output*     : `ContainerMetrics`
///
/// *initiator*  : main thread ?? 
///
/// *managing*   : ref counter to `System` object, ref counter to list of processes
///
/// *depends on* : `TrackingProcess`
/// 
#[allow(dead_code)]
async fn get_all_container_metrics(sys: Arc<System>, prcs: Arc<Vec<TrackingProcess>>) -> ContainerMetrics {
    let metrics = join!(
        get_cpu_metrics_container(sys.clone()),
        get_ram_metrics_container(sys.clone()),
        get_subsystems(prcs.clone())
    );
    ContainerMetrics::new(
        &get_container_id().unwrap_or(String::from("unknown")),
        metrics.0, 
        metrics.1, 
        metrics.2
    )
}

/// # Fn `get_cpu_metrics_container`
/// ## for gathering container cpu metrics
/// 
/// *input*      : `Arc<System>`
///
/// *output*     : `f32`
///
/// *initiator*  : main thread ?? 
///
/// *managing*   : ref counter to `System` object
///
/// *depends on* : -  
/// 
#[allow(dead_code)]
async fn get_cpu_metrics_container(sys: Arc<System>) -> f32 {
    sys.global_cpu_usage()
}

/// # Fn `get_ram_metrics_container`
/// ## for gathering container ram metrics
/// 
/// *input*      : `Arc<System>`
///
/// *output*     : `f32`
///
/// *initiator*  : main thread ?? 
///
/// *managing*   : ref counter to `System` object
///
/// *depends on* : -  
/// 
#[allow(dead_code)]
async fn get_ram_metrics_container(sys: Arc<System>) -> f32 {
    (sys.used_memory() / sys.total_memory()) as f32 * 100.0 
}
// async fn get_mem_metrics_container(sys: Arc<System>) -> f32 {
//     sys.
// }

/// # Fn `get_subsystems`
/// ## for gathering info about container subsystems (processes)
/// 
/// *input*      : `Arc<Vec<TrackingProcess>>`
///
/// *output*     : `Vec<String>`
///
/// *initiator*  : main thread ?? 
///
/// *managing*   : ref counter to list of `TrackingProcess`
///
/// *depends on* : `TrackingProcess`  
/// 
#[allow(dead_code)]
async fn get_subsystems(prcs: Arc<Vec<TrackingProcess>>) -> Vec<String> {
    prcs.iter().map(|process| process.name.clone()).collect()
}

/// # Fn `get_all_metrics_process`
/// ## for gathering all process' metrics
/// 
/// *input*      : `Arc<Process>`, `Arc<System>`
///
/// *output*     : `ProcessMetrics`
///
/// *initiator*  : main thread ?? 
///
/// *managing*   : two ref counters to `Process` and `System`
///
/// *depends on* : -
/// 
#[allow(dead_code)]
async fn get_all_metrics_process(proc: Arc<Process>, sys: Arc<System>) -> ProcessMetrics {
    let metrics = join!(
        get_cpu_metrics_process(proc.clone()),
        get_ram_metrics_process(proc.clone(), sys.clone())
    );
    ProcessMetrics::new(
        proc.name().to_str().unwrap_or("unknown"), 
        metrics.0, 
        metrics.1
    )
}

/// # Fn `get_cpu_metrics_process`
/// ## for gathering process cpu metrics
/// 
/// *input*      : `Arc<Process>`
///
/// *output*     : `f32`
///
/// *initiator*  : main thread ?? 
///
/// *managing*   : ref counter to `Process` object
///
/// *depends on* : -  
/// 
async fn get_cpu_metrics_process(proc: Arc<Process>) -> f32 {
    proc.cpu_usage()
}

/// # Fn `get_ram_metrics_process`
/// ## for gathering process ram metrics
/// 
/// *input*      : `Arc<Process>`
///
/// *output*     : `f32`
///
/// *initiator*  : main thread ?? 
///
/// *managing*   : ref counter to `Process` object
///
/// *depends on* : -  
/// 
async fn get_ram_metrics_process(proc: Arc<Process>, sys: Arc<System>) -> f32 {
    (proc.memory() as f64 / sys.total_memory() as f64) as f32 * 100.0 as f32
}

#[cfg(test)]
mod metrics_unittets {
    #[tokio::test]
    // Option<String> output
    // echo func
    async fn get_cpu_load() {
        assert!(true);
    }
    #[tokio::test]
    // Option<String> output
    // echo func
    async fn get_ram_load() {
        assert!(true);
    }
    #[tokio::test]
    // Option<String> output
    // echo func
    async fn get_mem_load() {
        assert!(true);
    }
    #[tokio::test]
    // can't be tested this way because of 0-0 loop of checking buffer
    // async func with loop inside
    async fn get_net_stat() {
        assert!(true);
    }
    #[tokio::test]
    async fn get_metrics_grubber() {
        assert!(true);
    }
    // Option<Vec<TrackingProcess OR String>> output
    // echo func
    #[tokio::test]
    async fn get_info_about_subsystems() {
        assert!(true);
    }
}
