use netstat2::{AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo};
use sysinfo::{System, SystemExt, Pid, PidExt, ProcessExt};
use serde::{Serialize, Deserialize};

use crate::errors::*;
use crate::get_state;


#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Hash)]
pub struct Client {
    pub pid: u32,
    pub exe: String,
}


fn get_associated_pids(local_port: u16) -> Result<Vec<u32>, netstat2::error::Error> {
    let sockets_iter = netstat2::iterate_sockets_info(
        AddressFamilyFlags::IPV4,
        ProtocolFlags::TCP
    )?;

    get_state!(config as app_config);
    for item in sockets_iter {
        let sock_info = item?;
        let proto_info = match sock_info.protocol_socket_info {
            ProtocolSocketInfo::Tcp(tcp_info) => tcp_info,
            ProtocolSocketInfo::Udp(_) => {continue;}
        };

        if proto_info.local_port == local_port
            && proto_info.remote_port == app_config.listen_port
            && proto_info.local_addr == app_config.listen_addr
            && proto_info.remote_addr == app_config.listen_addr
        {
            return Ok(sock_info.associated_pids)
        }
    }
    Ok(vec![])
}


// Theoretically, on some systems, multiple processes can share a socket
pub fn get_clients(local_port: u16) -> Result<Vec<Option<Client>>, ClientInfoError> {
    let mut clients = Vec::new();    
    let mut sys = System::new();
    for p in get_associated_pids(local_port)? {
        let pid = Pid::from_u32(p);
        sys.refresh_process(pid);
        let proc = sys.process(pid)
            .ok_or(ClientInfoError::ProcessNotFound)?;

        let client = Client {
            pid: p,
            exe: proc.exe().to_string_lossy().into_owned(),
        };
        clients.push(Some(client));
    }

    if clients.is_empty() {
        clients.push(None); 
    }

    Ok(clients)
}
