#[cfg(target_os = "windows")]
pub fn get_process_name_from_port(port: u16) -> Option<String> {
    use winapi::shared::minwindef::ULONG;
    use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
    use winapi::um::iphlpapi::GetExtendedTcpTable;
    use winapi::shared::tcpmib::{MIB_TCPTABLE_OWNER_PID, MIB_TCPROW_OWNER_PID};

    let mut size: ULONG = 0;
    let table_class = 5; // TCP_TABLE_OWNER_PID_ALL
    let mut table = vec![0u8; 1024];

    unsafe {
        let mut ret = GetExtendedTcpTable(
            table.as_mut_ptr() as *mut _,
            &mut size,
            0,
            2, // AF_INET
            table_class,
            0,
        );

        if ret == ERROR_INSUFFICIENT_BUFFER {
            table.resize(size as usize, 0);
            ret = GetExtendedTcpTable(
                table.as_mut_ptr() as *mut _,
                &mut size,
                0,
                2, // AF_INET
                table_class,
                0,
            );
        }

        if ret == 0 {
            let tcp_table = &*(table.as_ptr() as *const MIB_TCPTABLE_OWNER_PID);
            let row_ptr = &tcp_table.table[0] as *const MIB_TCPROW_OWNER_PID;
            for i in 0..tcp_table.dwNumEntries {
                let row = &*row_ptr.add(i as usize);
                // Local port is in network byte order
                let local_port = u16::from_be(row.dwLocalPort as u16);
                if local_port == port {
                    return get_process_name_from_pid(row.dwOwningPid);
                }
            }
        }
    }
    None
}

#[cfg(target_os = "windows")]
pub fn get_process_name_from_port_udp(port: u16) -> Option<String> {
    use winapi::shared::minwindef::ULONG;
    use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
    use winapi::um::iphlpapi::GetExtendedUdpTable;
    use winapi::shared::udpmib::{MIB_UDPTABLE_OWNER_PID, MIB_UDPROW_OWNER_PID};

    let mut size: ULONG = 0;
    let table_class = 1; // UDP_TABLE_OWNER_PID
    let mut table = vec![0u8; 1024];

    unsafe {
        let mut ret = GetExtendedUdpTable(
            table.as_mut_ptr() as *mut _,
            &mut size,
            0,
            2, // AF_INET
            table_class,
            0,
        );

        if ret == ERROR_INSUFFICIENT_BUFFER {
            table.resize(size as usize, 0);
            ret = GetExtendedUdpTable(
                table.as_mut_ptr() as *mut _,
                &mut size,
                0,
                2, // AF_INET
                table_class,
                0,
            );
        }

        if ret == 0 {
            let udp_table = &*(table.as_ptr() as *const MIB_UDPTABLE_OWNER_PID);
            let row_ptr = &udp_table.table[0] as *const MIB_UDPROW_OWNER_PID;
            for i in 0..udp_table.dwNumEntries {
                let row = &*row_ptr.add(i as usize);
                let local_port = u16::from_be(row.dwLocalPort as u16);
                if local_port == port {
                    return get_process_name_from_pid(row.dwOwningPid);
                }
            }
        }
    }
    None
}

#[cfg(target_os = "windows")]
fn get_process_name_from_pid(pid: u32) -> Option<String> {
    use winapi::um::processthreadsapi::OpenProcess;
    use winapi::um::psapi::GetModuleBaseNameW;
    use winapi::um::winnt::{PROCESS_QUERY_INFORMATION, PROCESS_VM_READ};
    use winapi::um::handleapi::CloseHandle;
    use std::os::windows::ffi::OsStringExt;

    unsafe {
        let handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, 0, pid);
        if handle.is_null() {
            return None;
        }

        let mut buffer = [0u16; 1024];
        let len = GetModuleBaseNameW(handle, std::ptr::null_mut(), buffer.as_mut_ptr(), buffer.len() as u32);
        CloseHandle(handle);

        if len > 0 {
            let name = std::ffi::OsString::from_wide(&buffer[..len as usize]);
            return Some(name.to_string_lossy().into_owned());
        }
    }
    None
}

#[cfg(target_os = "linux")]
pub fn get_process_name_from_port(port: u16) -> Option<String> {
    use std::fs;
    use std::io::{BufRead, BufReader};

    let hex_port = format!("{:04X}", port);

    let check_net_file = |path: &str| -> Option<u64> {
        let file = fs::File::open(path).ok()?;
        let reader = BufReader::new(file);
        for line in reader.lines().skip(1).filter_map(Result::ok) {
            let parts: Vec<&str> = line.split_whitespace().collect();
            if parts.len() >= 10 {
                let local_addr = parts[1];
                if local_addr.ends_with(&format!(":{}", hex_port)) {
                    if let Ok(inode) = parts[9].parse::<u64>() {
                        return Some(inode);
                    }
                }
            }
        }
        None
    };

    let target_inode = check_net_file("/proc/net/tcp")
        .or_else(|| check_net_file("/proc/net/tcp6"))
        .or_else(|| check_net_file("/proc/net/udp"))
        .or_else(|| check_net_file("/proc/net/udp6"))?;

    let socket_str = format!("socket:[{}]", target_inode);

    for entry in fs::read_dir("/proc").ok()?.filter_map(Result::ok) {
        let file_name = entry.file_name();
        let pid_str = file_name.to_string_lossy();
        if !pid_str.chars().all(char::is_numeric) {
            continue;
        }

        let fd_dir = entry.path().join("fd");
        if let Ok(fd_entries) = fs::read_dir(fd_dir) {
            for fd_entry in fd_entries.filter_map(Result::ok) {
                if let Ok(target) = fs::read_link(fd_entry.path()) {
                    if target.to_string_lossy() == socket_str {
                        let exe_path = entry.path().join("exe");
                        if let Ok(exe_link) = fs::read_link(exe_path) {
                            if let Some(name) = exe_link.file_name() {
                                return Some(name.to_string_lossy().into_owned());
                            }
                        }
                        if let Ok(comm) = fs::read_to_string(entry.path().join("comm")) {
                            return Some(comm.trim().to_string());
                        }
                    }
                }
            }
        }
    }

    None
}

#[cfg(not(any(target_os = "windows", target_os = "linux")))]
pub fn get_process_name_from_port(_port: u16) -> Option<String> {
    None
}

#[cfg(not(target_os = "windows"))]
pub fn get_process_name_from_port_udp(port: u16) -> Option<String> {
    get_process_name_from_port(port)
}
