use std::mem::MaybeUninit;

fn local_hhmm() -> (u32, u32) {
    let now = std::time::SystemTime::now()
        .duration_since(std::time::SystemTime::UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs() as i64;
    let mut tm = MaybeUninit::<libc::tm>::uninit();
    unsafe { libc::localtime_r(&now, tm.as_mut_ptr()) };
    let tm = unsafe { tm.assume_init() };
    (tm.tm_hour as u32, tm.tm_min as u32)
}

pub fn current_hhmm() -> (u32, u32) {
    local_hhmm()
}

pub fn current_yday() -> u32 {
    let now = std::time::SystemTime::now()
        .duration_since(std::time::SystemTime::UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs() as i64;
    let mut tm = MaybeUninit::<libc::tm>::uninit();
    unsafe { libc::localtime_r(&now, tm.as_mut_ptr()) };
    unsafe { tm.assume_init() }.tm_yday as u32
}

fn is_physical(name: &str) -> bool {
    if name == "lo" { return false; }
    for prefix in &["veth", "docker", "br-", "virbr", "vir", "tun", "tap", "dummy", "bond"] {
        if name.starts_with(prefix) { return false; }
    }
    true
}

fn read_sysfs(iface: &str, file: &str) -> Option<String> {
    std::fs::read_to_string(format!("/sys/class/net/{}/{}", iface, file))
        .ok()
        .map(|s| s.trim().to_string())
}

fn is_wifi(name: &str) -> bool {
    std::path::Path::new(&format!("/sys/class/net/{}/wireless", name)).exists()
}

fn wol_status(name: &str) -> Option<bool> {
    let out = std::process::Command::new("ethtool").arg(name).output().ok()?;
    let text = String::from_utf8_lossy(&out.stdout);
    for line in text.lines() {
        let t = line.trim();
        if t.starts_with("Wake-on:") {
            let val = t.split(':').nth(1)?.trim().to_string();
            return Some(val != "d" && !val.is_empty());
        }
    }
    None
}

fn iperf_mbps(server_ip: &str) -> Option<f64> {
    std::process::Command::new("which").arg("iperf3")
        .output().ok()
        .filter(|o| o.status.success())?;
    let out = std::process::Command::new("iperf3")
        .args(["-c", server_ip, "-J", "-t", "5", "-P", "1"])
        .output().ok()?;
    let json = String::from_utf8_lossy(&out.stdout);
    let v: serde_json::Value = serde_json::from_str(&json).ok()?;
    let bps = v["end"]["sum_received"]["bits_per_second"].as_f64()?;
    Some((bps / 1_000_000.0 * 10.0).round() / 10.0)
}

pub fn collect(server_ip: &str) -> Vec<crate::payload::NetworkInterface> {
    let entries = match std::fs::read_dir("/sys/class/net") {
        Ok(e) => e,
        Err(_) => return vec![],
    };
    let mut ifaces: Vec<String> = entries
        .flatten()
        .map(|e| e.file_name().into_string().unwrap_or_default())
        .filter(|n| is_physical(n))
        .collect();
    ifaces.sort();
    if ifaces.is_empty() { return vec![]; }

    let iperf = iperf_mbps(server_ip);

    ifaces.iter().map(|name| {
        let speed = read_sysfs(name, "speed")
            .and_then(|s| s.parse::<i64>().ok())
            .filter(|&v| v > 0);
        let mac = read_sysfs(name, "address").unwrap_or_default();
        let wifi = is_wifi(name);
        crate::payload::NetworkInterface {
            name: name.clone(),
            if_type: if wifi { "wifi".to_string() } else { "ethernet".to_string() },
            speed_mbps: speed,
            mac,
            wol: if wifi { None } else { wol_status(name) },
            iperf_mbps: iperf,
        }
    }).collect()
}
