//! Socket diagnostics implementation for `Connection<SockDiag>`.
//!
//! This module provides methods for querying socket information via the
//! NETLINK_SOCK_DIAG protocol, integrated into the Connection pattern.
//!
//! # Example
//!
//! ```ignore
//! use nlink::netlink::{Connection, SockDiag};
//! use nlink::sockdiag::{SocketFilter, TcpState};
//!
//! let conn = Connection::<SockDiag>::new()?;
//!
//! // Query all TCP sockets
//! let sockets = conn.query_tcp().await?;
//!
//! // Query with filter
//! let filter = SocketFilter::tcp()
//!     .listening()
//!     .with_tcp_info()
//!     .build();
//! let sockets = conn.query(&filter).await?;
//! ```

use std::net::{IpAddr, SocketAddr};

use super::connection::Connection;
use super::error::Result;
use super::protocol::SockDiag;

// Re-export sockdiag types that are needed for the API
pub use crate::sockdiag::filter::{
    FilterKind, InetFilter, NetlinkFilter, PacketFilter, SocketFilter, UnixFilter,
};
pub use crate::sockdiag::socket::{InetSocket, SocketInfo, UnixSocket, UnixType};
pub use crate::sockdiag::types::{
    AddressFamily, MemInfo, Protocol as InetProtocol, SocketState, SocketSummary, TcpInfo,
    TcpState, Timer,
};

// Netlink constants
const NLMSG_DONE: u16 = 3;
const NLMSG_ERROR: u16 = 2;
const NLM_F_REQUEST: u16 = 0x01;
const NLM_F_ROOT: u16 = 0x100;
const NLM_F_MATCH: u16 = 0x200;
const NLM_F_DUMP: u16 = NLM_F_ROOT | NLM_F_MATCH;

// Socket diagnostics constants
const SOCK_DIAG_BY_FAMILY: u16 = 20;
const SOCK_DESTROY: u16 = 21;
const TCPDIAG_GETSOCK: u16 = 18;
const NLM_F_ACK: u16 = 0x04;

// Inet diag extensions
const INET_DIAG_MEMINFO: u16 = 1;
const INET_DIAG_INFO: u16 = 2;
const INET_DIAG_CONG: u16 = 4;
const INET_DIAG_TOS: u16 = 5;
const INET_DIAG_TCLASS: u16 = 6;
const INET_DIAG_SKMEMINFO: u16 = 7;
const INET_DIAG_SHUTDOWN: u16 = 8;
const INET_DIAG_MARK: u16 = 15;
const INET_DIAG_CGROUP_ID: u16 = 21;
const INET_DIAG_SKV6ONLY: u16 = 11;

// Unix diag attributes
const UNIX_DIAG_NAME: u16 = 0;
const UNIX_DIAG_VFS: u16 = 1;
const UNIX_DIAG_PEER: u16 = 2;
const UNIX_DIAG_ICONS: u16 = 3;
const UNIX_DIAG_RQLEN: u16 = 4;
const UNIX_DIAG_MEMINFO: u16 = 5;
const UNIX_DIAG_SHUTDOWN: u16 = 6;
const UNIX_DIAG_UID: u16 = 7;

// Netlink diag show flags
const NDIAG_SHOW_MEMINFO: u32 = 0x01;
const NDIAG_SHOW_GROUPS: u32 = 0x02;

// Netlink diag attributes
const NETLINK_DIAG_MEMINFO: u16 = 0;
const NETLINK_DIAG_GROUPS: u16 = 1;

impl Connection<SockDiag> {
    /// Query sockets matching the given filter.
    ///
    /// # Example
    ///
    /// ```ignore
    /// use nlink::netlink::{Connection, SockDiag};
    /// use nlink::sockdiag::SocketFilter;
    ///
    /// let conn = Connection::<SockDiag>::new()?;
    /// let filter = SocketFilter::tcp().listening().build();
    /// let sockets = conn.query(&filter).await?;
    /// ```
    pub async fn query(&self, filter: &SocketFilter) -> Result<Vec<SocketInfo>> {
        match &filter.kind {
            FilterKind::Inet(f) => self.query_inet(f).await,
            FilterKind::Unix(f) => self.query_unix(f).await,
            FilterKind::Netlink(f) => self.query_netlink(f).await,
            FilterKind::Packet(f) => self.query_packet(f).await,
        }
    }

    /// Query TCP sockets with default filter.
    ///
    /// # Example
    ///
    /// ```ignore
    /// use nlink::netlink::{Connection, SockDiag};
    ///
    /// let conn = Connection::<SockDiag>::new()?;
    /// let sockets = conn.query_tcp().await?;
    /// for sock in sockets {
    ///     println!("{} -> {}", sock.local, sock.remote);
    /// }
    /// ```
    pub async fn query_tcp(&self) -> Result<Vec<InetSocket>> {
        let filter = InetFilter {
            protocol: InetProtocol::Tcp,
            ..Default::default()
        };
        self.query_inet_typed(&filter).await
    }

    /// Query UDP sockets with default filter.
    pub async fn query_udp(&self) -> Result<Vec<InetSocket>> {
        let filter = InetFilter {
            protocol: InetProtocol::Udp,
            ..Default::default()
        };
        self.query_inet_typed(&filter).await
    }

    /// Query Unix sockets with default filter.
    pub async fn query_unix_sockets(&self) -> Result<Vec<UnixSocket>> {
        let filter = UnixFilter::default();
        self.query_unix_typed(&filter).await
    }

    /// Query Netlink sockets with default filter.
    pub async fn query_netlink_sockets(
        &self,
    ) -> Result<Vec<crate::sockdiag::socket::NetlinkSocket>> {
        let filter = NetlinkFilter::default();
        self.query_netlink_typed(&filter).await
    }

    /// Get aggregated socket statistics across all families.
    ///
    /// Queries TCP, UDP, raw, and Unix sockets and aggregates the counts
    /// by state. This is equivalent to `ss -s`.
    ///
    /// # Example
    ///
    /// ```ignore
    /// use nlink::netlink::{Connection, SockDiag};
    ///
    /// let conn = Connection::<SockDiag>::new()?;
    /// let summary = conn.socket_summary().await?;
    /// println!("{}", summary);
    /// // Total: 234
    /// // TCP:   45 (estab 23, closed 12, orphaned 0, timewait 8)
    /// // UDP:   12
    /// // RAW:   2
    /// // UNIX:  175
    /// ```
    pub async fn socket_summary(&self) -> Result<SocketSummary> {
        use crate::sockdiag::types::{SocketSummary, TcpSummary};

        let tcp_filter = InetFilter {
            protocol: InetProtocol::Tcp,
            states: TcpState::all_mask(),
            ..Default::default()
        };
        let tcp_sockets = self.query_inet_typed(&tcp_filter).await?;

        let udp_filter = InetFilter {
            protocol: InetProtocol::Udp,
            ..Default::default()
        };
        let udp_sockets = self.query_inet_typed(&udp_filter).await?;

        let raw_filter = InetFilter {
            protocol: InetProtocol::Raw,
            ..Default::default()
        };
        let raw_sockets = self.query_inet_typed(&raw_filter).await.unwrap_or_default();

        let unix_sockets = self.query_unix_sockets().await?;

        let mut tcp = TcpSummary {
            total: tcp_sockets.len() as u32,
            ..Default::default()
        };

        for sock in &tcp_sockets {
            match sock.state {
                SocketState::Tcp(TcpState::Established) => tcp.established += 1,
                SocketState::Tcp(TcpState::SynSent) => tcp.syn_sent += 1,
                SocketState::Tcp(TcpState::SynRecv) => tcp.syn_recv += 1,
                SocketState::Tcp(TcpState::FinWait1) => tcp.fin_wait1 += 1,
                SocketState::Tcp(TcpState::FinWait2) => tcp.fin_wait2 += 1,
                SocketState::Tcp(TcpState::TimeWait) => tcp.time_wait += 1,
                SocketState::Tcp(TcpState::Close) => tcp.close += 1,
                SocketState::Tcp(TcpState::CloseWait) => tcp.close_wait += 1,
                SocketState::Tcp(TcpState::LastAck) => tcp.last_ack += 1,
                SocketState::Tcp(TcpState::Listen) => tcp.listen += 1,
                SocketState::Tcp(TcpState::Closing) => tcp.closing += 1,
                _ => {}
            }
        }

        Ok(SocketSummary {
            tcp,
            udp: udp_sockets.len() as u32,
            raw: raw_sockets.len() as u32,
            unix: unix_sockets.len() as u32,
        })
    }

    /// Force-close a TCP socket by sending `SOCK_DESTROY`.
    ///
    /// The kernel sends a RST to the remote peer. Requires `CAP_NET_ADMIN`
    /// and Linux 4.9+. Only TCP sockets can be destroyed.
    ///
    /// # Errors
    ///
    /// Returns `Error::Kernel` with `EPERM` if insufficient privileges.
    /// Returns `Error::Kernel` with `EOPNOTSUPP` if the socket type doesn't
    /// support destruction.
    ///
    /// # Example
    ///
    /// ```ignore
    /// let sockets = conn.query_tcp().await?;
    /// for sock in &sockets {
    ///     if sock.remote.port() == 8080 {
    ///         conn.destroy_tcp_socket(sock).await?;
    ///     }
    /// }
    /// ```
    pub async fn destroy_tcp_socket(&self, socket: &InetSocket) -> Result<()> {
        let seq = self.socket().next_seq();
        let pid = self.socket().pid();

        let mut buf = Vec::with_capacity(128);

        // Netlink header (16 bytes)
        buf.extend_from_slice(&0u32.to_ne_bytes()); // nlmsg_len (filled later)
        buf.extend_from_slice(&SOCK_DESTROY.to_ne_bytes()); // nlmsg_type
        buf.extend_from_slice(&(NLM_F_REQUEST | NLM_F_ACK).to_ne_bytes()); // nlmsg_flags
        buf.extend_from_slice(&seq.to_ne_bytes()); // nlmsg_seq
        buf.extend_from_slice(&pid.to_ne_bytes()); // nlmsg_pid

        // inet_diag_req_v2 (56 bytes)
        buf.push(socket.family as u8); // sdiag_family
        buf.push(socket.protocol.number()); // sdiag_protocol
        buf.push(0); // idiag_ext
        buf.push(0); // pad
        buf.extend_from_slice(&0u32.to_ne_bytes()); // idiag_states (unused for destroy)

        // inet_diag_sockid (48 bytes)
        buf.extend_from_slice(&socket.local.port().to_be_bytes()); // idiag_sport
        buf.extend_from_slice(&socket.remote.port().to_be_bytes()); // idiag_dport

        // Source address (16 bytes, IPv4 in first 4 bytes)
        match socket.local.ip() {
            std::net::IpAddr::V4(v4) => {
                buf.extend_from_slice(&v4.octets());
                buf.extend_from_slice(&[0u8; 12]);
            }
            std::net::IpAddr::V6(v6) => {
                buf.extend_from_slice(&v6.octets());
            }
        }

        // Destination address (16 bytes)
        match socket.remote.ip() {
            std::net::IpAddr::V4(v4) => {
                buf.extend_from_slice(&v4.octets());
                buf.extend_from_slice(&[0u8; 12]);
            }
            std::net::IpAddr::V6(v6) => {
                buf.extend_from_slice(&v6.octets());
            }
        }

        buf.extend_from_slice(&socket.interface.to_ne_bytes()); // idiag_if
        buf.extend_from_slice(&socket.cookie.to_ne_bytes()); // idiag_cookie

        // Update length
        let len = buf.len() as u32;
        buf[0..4].copy_from_slice(&len.to_ne_bytes());

        // Send request
        self.socket().send(&buf).await?;

        // Wait for ACK
        let data: Vec<u8> = self.socket().recv_msg().await?;
        if data.len() >= 20 {
            let msg_type = u16::from_ne_bytes([data[4], data[5]]);
            if msg_type == NLMSG_ERROR {
                let errno = i32::from_ne_bytes([data[16], data[17], data[18], data[19]]);
                if errno != 0 {
                    return Err(crate::netlink::Error::Kernel {
                        errno: -errno,
                        message: format!(
                            "SOCK_DESTROY failed: {}",
                            std::io::Error::from_raw_os_error(-errno)
                        ),
                    });
                }
            }
        }

        Ok(())
    }

    /// Destroy all TCP sockets matching the given filter.
    ///
    /// Queries sockets matching the filter, then destroys each one.
    /// Returns a `DestroyResult` with the count of destroyed sockets and any errors.
    /// Requires `CAP_NET_ADMIN`.
    ///
    /// # Example
    ///
    /// ```ignore
    /// use nlink::sockdiag::{InetFilter, Protocol, TcpState};
    ///
    /// let conn = Connection::<SockDiag>::new()?;
    /// let filter = InetFilter {
    ///     protocol: Protocol::Tcp,
    ///     states: TcpState::TimeWait.mask(),
    ///     ..Default::default()
    /// };
    /// let result = conn.destroy_matching(&filter).await?;
    /// println!("Destroyed {} sockets", result.destroyed);
    /// ```
    pub async fn destroy_matching(
        &self,
        filter: &InetFilter,
    ) -> Result<crate::sockdiag::types::DestroyResult> {
        let sockets = self.query_inet_typed(filter).await?;
        let mut destroyed = 0u32;
        let mut errors = Vec::new();

        for sock in &sockets {
            match self.destroy_tcp_socket(sock).await {
                Ok(()) => destroyed += 1,
                Err(e) => errors.push(crate::sockdiag::types::DestroyError {
                    socket: sock.local,
                    error: e,
                }),
            }
        }

        Ok(crate::sockdiag::types::DestroyResult { destroyed, errors })
    }

    async fn query_inet(&self, filter: &InetFilter) -> Result<Vec<SocketInfo>> {
        let sockets = self.query_inet_typed(filter).await?;
        Ok(sockets
            .into_iter()
            .map(|s| SocketInfo::Inet(Box::new(s)))
            .collect())
    }

    async fn query_inet_typed(&self, filter: &InetFilter) -> Result<Vec<InetSocket>> {
        let mut results = Vec::new();

        // Query IPv4 if not filtering to IPv6 only
        if filter.family.is_none() || filter.family == Some(AddressFamily::Inet) {
            let sockets = self.query_inet_family(filter, AddressFamily::Inet).await?;
            results.extend(sockets);
        }

        // Query IPv6 if not filtering to IPv4 only
        if filter.family.is_none() || filter.family == Some(AddressFamily::Inet6) {
            let sockets = self.query_inet_family(filter, AddressFamily::Inet6).await?;
            results.extend(sockets);
        }

        Ok(results)
    }

    async fn query_inet_family(
        &self,
        filter: &InetFilter,
        family: AddressFamily,
    ) -> Result<Vec<InetSocket>> {
        let seq = self.socket().next_seq();
        let pid = self.socket().pid();

        // Build request
        let mut buf = Vec::with_capacity(256);

        // Netlink header (16 bytes)
        let msg_type = SOCK_DIAG_BY_FAMILY;

        // Will fill in length later
        buf.extend_from_slice(&0u32.to_ne_bytes()); // nlmsg_len
        buf.extend_from_slice(&msg_type.to_ne_bytes()); // nlmsg_type
        buf.extend_from_slice(&(NLM_F_REQUEST | NLM_F_DUMP).to_ne_bytes()); // nlmsg_flags
        buf.extend_from_slice(&seq.to_ne_bytes()); // nlmsg_seq
        buf.extend_from_slice(&pid.to_ne_bytes()); // nlmsg_pid

        // inet_diag_req_v2 structure (56 bytes)
        buf.push(family as u8); // sdiag_family
        buf.push(filter.protocol.number()); // sdiag_protocol
        buf.push(filter.extensions); // idiag_ext
        buf.push(0); // pad
        buf.extend_from_slice(&filter.states.to_ne_bytes()); // idiag_states

        // inet_diag_sockid (48 bytes)
        buf.extend_from_slice(&0u16.to_be_bytes()); // idiag_sport
        buf.extend_from_slice(&0u16.to_be_bytes()); // idiag_dport
        buf.extend_from_slice(&[0u8; 16]); // idiag_src
        buf.extend_from_slice(&[0u8; 16]); // idiag_dst
        buf.extend_from_slice(&0u32.to_ne_bytes()); // idiag_if
        buf.extend_from_slice(&[0u8; 8]); // idiag_cookie

        // Update length
        let len = buf.len() as u32;
        buf[0..4].copy_from_slice(&len.to_ne_bytes());

        // Send request
        self.socket().send(&buf).await?;

        // Receive responses
        let mut sockets = Vec::new();

        loop {
            let data: Vec<u8> = self.socket().recv_msg().await?;

            let mut offset = 0;
            while offset + 16 <= data.len() {
                let nlmsg_len = u32::from_ne_bytes([
                    data[offset],
                    data[offset + 1],
                    data[offset + 2],
                    data[offset + 3],
                ]) as usize;

                let nlmsg_type = u16::from_ne_bytes([data[offset + 4], data[offset + 5]]);

                if nlmsg_len < 16 || offset + nlmsg_len > data.len() {
                    break;
                }

                match nlmsg_type {
                    NLMSG_DONE => return Ok(sockets),
                    NLMSG_ERROR => {
                        if nlmsg_len >= 20 {
                            let errno = i32::from_ne_bytes([
                                data[offset + 16],
                                data[offset + 17],
                                data[offset + 18],
                                data[offset + 19],
                            ]);
                            if errno != 0 {
                                return Err(super::error::Error::from_errno(-errno));
                            }
                        }
                    }
                    SOCK_DIAG_BY_FAMILY | TCPDIAG_GETSOCK => {
                        if let Some(sock) =
                            parse_inet_msg(&data[offset..offset + nlmsg_len], filter.protocol)
                        {
                            sockets.push(sock);
                        }
                    }
                    _ => {}
                }

                // Align to 4 bytes
                offset += (nlmsg_len + 3) & !3;
            }
        }
    }

    async fn query_unix(&self, filter: &UnixFilter) -> Result<Vec<SocketInfo>> {
        let sockets = self.query_unix_typed(filter).await?;
        Ok(sockets.into_iter().map(SocketInfo::Unix).collect())
    }

    async fn query_unix_typed(&self, filter: &UnixFilter) -> Result<Vec<UnixSocket>> {
        let seq = self.socket().next_seq();
        let pid = self.socket().pid();

        // Build request
        let mut buf = Vec::with_capacity(64);

        // Netlink header (16 bytes)
        buf.extend_from_slice(&0u32.to_ne_bytes()); // nlmsg_len (fill later)
        buf.extend_from_slice(&SOCK_DIAG_BY_FAMILY.to_ne_bytes()); // nlmsg_type
        buf.extend_from_slice(&(NLM_F_REQUEST | NLM_F_DUMP).to_ne_bytes()); // nlmsg_flags
        buf.extend_from_slice(&seq.to_ne_bytes()); // nlmsg_seq
        buf.extend_from_slice(&pid.to_ne_bytes()); // nlmsg_pid

        // unix_diag_req structure (20 bytes)
        buf.push(libc::AF_UNIX as u8); // sdiag_family
        buf.push(0); // sdiag_protocol
        buf.extend_from_slice(&0u16.to_ne_bytes()); // pad
        buf.extend_from_slice(&filter.states.to_ne_bytes()); // udiag_states
        buf.extend_from_slice(&filter.inode.unwrap_or(0).to_ne_bytes()); // udiag_ino
        buf.extend_from_slice(&filter.show.to_ne_bytes()); // udiag_show
        buf.extend_from_slice(&[0u8; 8]); // udiag_cookie

        // Update length
        let len = buf.len() as u32;
        buf[0..4].copy_from_slice(&len.to_ne_bytes());

        // Send request
        self.socket().send(&buf).await?;

        // Receive responses
        let mut sockets = Vec::new();

        loop {
            let data: Vec<u8> = self.socket().recv_msg().await?;

            let mut offset = 0;
            while offset + 16 <= data.len() {
                let nlmsg_len = u32::from_ne_bytes([
                    data[offset],
                    data[offset + 1],
                    data[offset + 2],
                    data[offset + 3],
                ]) as usize;

                let nlmsg_type = u16::from_ne_bytes([data[offset + 4], data[offset + 5]]);

                if nlmsg_len < 16 || offset + nlmsg_len > data.len() {
                    break;
                }

                match nlmsg_type {
                    NLMSG_DONE => return Ok(sockets),
                    NLMSG_ERROR => {
                        if nlmsg_len >= 20 {
                            let errno = i32::from_ne_bytes([
                                data[offset + 16],
                                data[offset + 17],
                                data[offset + 18],
                                data[offset + 19],
                            ]);
                            if errno != 0 {
                                return Err(super::error::Error::from_errno(-errno));
                            }
                        }
                    }
                    SOCK_DIAG_BY_FAMILY => {
                        if let Some(sock) = parse_unix_msg(&data[offset..offset + nlmsg_len]) {
                            sockets.push(sock);
                        }
                    }
                    _ => {}
                }

                offset += (nlmsg_len + 3) & !3;
            }
        }
    }

    async fn query_netlink(&self, filter: &NetlinkFilter) -> Result<Vec<SocketInfo>> {
        let sockets = self.query_netlink_typed(filter).await?;
        Ok(sockets.into_iter().map(SocketInfo::Netlink).collect())
    }

    async fn query_netlink_typed(
        &self,
        filter: &NetlinkFilter,
    ) -> Result<Vec<crate::sockdiag::socket::NetlinkSocket>> {
        let seq = self.socket().next_seq();
        let pid = self.socket().pid();

        // Build netlink_diag_req
        let mut buf = Vec::with_capacity(64);

        // Netlink header (16 bytes)
        buf.extend_from_slice(&0u32.to_ne_bytes()); // nlmsg_len (fill later)
        buf.extend_from_slice(&SOCK_DIAG_BY_FAMILY.to_ne_bytes()); // nlmsg_type
        buf.extend_from_slice(&(NLM_F_REQUEST | NLM_F_DUMP).to_ne_bytes()); // nlmsg_flags
        buf.extend_from_slice(&seq.to_ne_bytes()); // nlmsg_seq
        buf.extend_from_slice(&pid.to_ne_bytes()); // nlmsg_pid

        // netlink_diag_req structure (24 bytes)
        buf.push(libc::AF_NETLINK as u8); // sdiag_family
        buf.push(filter.protocol.unwrap_or(0xff)); // sdiag_protocol (0xff = all)
        buf.extend_from_slice(&0u16.to_ne_bytes()); // pad

        buf.extend_from_slice(&0u32.to_ne_bytes()); // ndiag_ino
        let mut show: u32 = 0;
        if filter.show_meminfo {
            show |= NDIAG_SHOW_MEMINFO;
        }
        if filter.show_groups {
            show |= NDIAG_SHOW_GROUPS;
        }
        buf.extend_from_slice(&show.to_ne_bytes()); // ndiag_show
        buf.extend_from_slice(&[0u8; 8]); // ndiag_cookie

        // Update length
        let len = buf.len() as u32;
        buf[0..4].copy_from_slice(&len.to_ne_bytes());

        // Send request
        self.socket().send(&buf).await?;

        // Receive responses
        let mut sockets = Vec::new();

        loop {
            let data: Vec<u8> = self.socket().recv_msg().await?;

            let mut offset = 0;
            while offset + 16 <= data.len() {
                let nlmsg_len = u32::from_ne_bytes([
                    data[offset],
                    data[offset + 1],
                    data[offset + 2],
                    data[offset + 3],
                ]) as usize;

                let nlmsg_type = u16::from_ne_bytes([data[offset + 4], data[offset + 5]]);

                if nlmsg_len < 16 || offset + nlmsg_len > data.len() {
                    break;
                }

                match nlmsg_type {
                    NLMSG_DONE => return Ok(sockets),
                    NLMSG_ERROR => {
                        if nlmsg_len >= 20 {
                            let errno = i32::from_ne_bytes([
                                data[offset + 16],
                                data[offset + 17],
                                data[offset + 18],
                                data[offset + 19],
                            ]);
                            if errno != 0 {
                                return Err(super::error::Error::from_errno(-errno));
                            }
                        }
                    }
                    SOCK_DIAG_BY_FAMILY => {
                        if let Some(sock) =
                            parse_netlink_msg(&data[offset..offset + nlmsg_len], filter)
                        {
                            sockets.push(sock);
                        }
                    }
                    _ => {}
                }

                offset += (nlmsg_len + 3) & !3;
            }
        }
    }

    async fn query_packet(&self, _filter: &PacketFilter) -> Result<Vec<SocketInfo>> {
        // Packet socket diagnostics - simplified for now
        Ok(Vec::new())
    }
}

// Helper functions for parsing

fn parse_ipv4(data: &[u8]) -> std::net::Ipv4Addr {
    if data.len() >= 4 {
        std::net::Ipv4Addr::new(data[0], data[1], data[2], data[3])
    } else {
        std::net::Ipv4Addr::UNSPECIFIED
    }
}

fn parse_ipv6(data: &[u8]) -> std::net::Ipv6Addr {
    if data.len() >= 16 {
        let mut octets = [0u8; 16];
        octets.copy_from_slice(&data[..16]);
        std::net::Ipv6Addr::from(octets)
    } else {
        std::net::Ipv6Addr::UNSPECIFIED
    }
}

fn parse_port(data: &[u8]) -> u16 {
    if data.len() >= 2 {
        u16::from_be_bytes([data[0], data[1]])
    } else {
        0
    }
}

fn parse_inet_msg(data: &[u8], protocol: InetProtocol) -> Option<InetSocket> {
    // Skip netlink header (16 bytes)
    if data.len() < 16 + 72 {
        return None;
    }

    let payload = &data[16..];

    // inet_diag_msg structure
    let family = AddressFamily::from_u8(payload[0])?;
    let state = TcpState::from_u8(payload[1]);
    let timer = payload[2];
    let retrans = payload[3];

    // inet_diag_sockid at offset 4
    let sport = parse_port(&payload[4..6]);
    let dport = parse_port(&payload[6..8]);

    let (src_ip, dst_ip) = match family {
        AddressFamily::Inet => {
            let src = IpAddr::V4(parse_ipv4(&payload[8..12]));
            let dst = IpAddr::V4(parse_ipv4(&payload[24..28]));
            (src, dst)
        }
        AddressFamily::Inet6 => {
            let src = IpAddr::V6(parse_ipv6(&payload[8..24]));
            let dst = IpAddr::V6(parse_ipv6(&payload[24..40]));
            (src, dst)
        }
        _ => return None,
    };

    let interface = u32::from_ne_bytes([payload[40], payload[41], payload[42], payload[43]]);
    let cookie = u64::from_ne_bytes([
        payload[44],
        payload[45],
        payload[46],
        payload[47],
        payload[48],
        payload[49],
        payload[50],
        payload[51],
    ]);

    // After sockid: expires, rqueue, wqueue, uid, inode
    let expires = u32::from_ne_bytes([payload[52], payload[53], payload[54], payload[55]]);
    let rqueue = u32::from_ne_bytes([payload[56], payload[57], payload[58], payload[59]]);
    let wqueue = u32::from_ne_bytes([payload[60], payload[61], payload[62], payload[63]]);
    let uid = u32::from_ne_bytes([payload[64], payload[65], payload[66], payload[67]]);
    let inode = u32::from_ne_bytes([payload[68], payload[69], payload[70], payload[71]]);

    let mut sock = InetSocket {
        family,
        protocol,
        state: SocketState::Tcp(state),
        local: SocketAddr::new(src_ip, sport),
        remote: SocketAddr::new(dst_ip, dport),
        interface,
        cookie,
        timer: Timer::from_raw(timer, expires, retrans),
        recv_q: rqueue,
        send_q: wqueue,
        uid,
        inode,
        refcnt: 0,
        mark: None,
        cgroup_id: None,
        tcp_info: None,
        mem_info: None,
        congestion: None,
        tos: None,
        tclass: None,
        shutdown: None,
        v6only: None,
    };

    // Parse attributes
    let mut attr_offset = 16 + 72;
    while attr_offset + 4 <= data.len() {
        let attr_len = u16::from_ne_bytes([data[attr_offset], data[attr_offset + 1]]) as usize;
        let attr_type = u16::from_ne_bytes([data[attr_offset + 2], data[attr_offset + 3]]);

        if attr_len < 4 || attr_offset + attr_len > data.len() {
            break;
        }

        let attr_data = &data[attr_offset + 4..attr_offset + attr_len];

        match attr_type {
            INET_DIAG_MEMINFO => {
                if attr_data.len() >= 16 {
                    sock.mem_info = Some(MemInfo {
                        rmem_alloc: u32::from_ne_bytes([
                            attr_data[0],
                            attr_data[1],
                            attr_data[2],
                            attr_data[3],
                        ]),
                        wmem_alloc: u32::from_ne_bytes([
                            attr_data[4],
                            attr_data[5],
                            attr_data[6],
                            attr_data[7],
                        ]),
                        fwd_alloc: u32::from_ne_bytes([
                            attr_data[8],
                            attr_data[9],
                            attr_data[10],
                            attr_data[11],
                        ]),
                        wmem_queued: u32::from_ne_bytes([
                            attr_data[12],
                            attr_data[13],
                            attr_data[14],
                            attr_data[15],
                        ]),
                        ..Default::default()
                    });
                }
            }
            INET_DIAG_INFO => {
                sock.tcp_info = Some(parse_tcp_info(attr_data));
            }
            INET_DIAG_CONG => {
                if let Ok(s) = std::str::from_utf8(attr_data) {
                    sock.congestion = Some(s.trim_end_matches('\0').to_string());
                }
            }
            INET_DIAG_TOS => {
                if !attr_data.is_empty() {
                    sock.tos = Some(attr_data[0]);
                }
            }
            INET_DIAG_TCLASS => {
                if !attr_data.is_empty() {
                    sock.tclass = Some(attr_data[0]);
                }
            }
            INET_DIAG_SKMEMINFO => {
                if attr_data.len() >= 36 {
                    sock.mem_info = Some(MemInfo {
                        rmem_alloc: u32::from_ne_bytes([
                            attr_data[0],
                            attr_data[1],
                            attr_data[2],
                            attr_data[3],
                        ]),
                        rcvbuf: u32::from_ne_bytes([
                            attr_data[4],
                            attr_data[5],
                            attr_data[6],
                            attr_data[7],
                        ]),
                        wmem_alloc: u32::from_ne_bytes([
                            attr_data[8],
                            attr_data[9],
                            attr_data[10],
                            attr_data[11],
                        ]),
                        sndbuf: u32::from_ne_bytes([
                            attr_data[12],
                            attr_data[13],
                            attr_data[14],
                            attr_data[15],
                        ]),
                        fwd_alloc: u32::from_ne_bytes([
                            attr_data[16],
                            attr_data[17],
                            attr_data[18],
                            attr_data[19],
                        ]),
                        wmem_queued: u32::from_ne_bytes([
                            attr_data[20],
                            attr_data[21],
                            attr_data[22],
                            attr_data[23],
                        ]),
                        optmem: u32::from_ne_bytes([
                            attr_data[24],
                            attr_data[25],
                            attr_data[26],
                            attr_data[27],
                        ]),
                        backlog: u32::from_ne_bytes([
                            attr_data[28],
                            attr_data[29],
                            attr_data[30],
                            attr_data[31],
                        ]),
                        drops: u32::from_ne_bytes([
                            attr_data[32],
                            attr_data[33],
                            attr_data[34],
                            attr_data[35],
                        ]),
                    });
                }
            }
            INET_DIAG_SHUTDOWN => {
                if !attr_data.is_empty() {
                    sock.shutdown = Some(attr_data[0]);
                }
            }
            INET_DIAG_MARK => {
                if attr_data.len() >= 4 {
                    sock.mark = Some(u32::from_ne_bytes([
                        attr_data[0],
                        attr_data[1],
                        attr_data[2],
                        attr_data[3],
                    ]));
                }
            }
            INET_DIAG_CGROUP_ID => {
                if attr_data.len() >= 8 {
                    sock.cgroup_id = Some(u64::from_ne_bytes([
                        attr_data[0],
                        attr_data[1],
                        attr_data[2],
                        attr_data[3],
                        attr_data[4],
                        attr_data[5],
                        attr_data[6],
                        attr_data[7],
                    ]));
                }
            }
            INET_DIAG_SKV6ONLY => {
                if !attr_data.is_empty() {
                    sock.v6only = Some(attr_data[0] != 0);
                }
            }
            _ => {}
        }

        // Align to 4 bytes
        attr_offset += (attr_len + 3) & !3;
    }

    Some(sock)
}

fn parse_tcp_info(data: &[u8]) -> TcpInfo {
    let mut info = TcpInfo::default();

    if data.len() < 104 {
        return info;
    }

    info.state = data[0];
    info.ca_state = data[1];
    info.retransmits = data[2];
    info.probes = data[3];
    info.backoff = data[4];
    info.options = data[5];
    info.wscale = data[6];
    info.delivery_rate_app_limited = data[7] != 0;

    info.rto = u32::from_ne_bytes([data[8], data[9], data[10], data[11]]);
    // ATO at 12-15 (skip)
    info.snd_mss = u32::from_ne_bytes([data[16], data[17], data[18], data[19]]);
    info.rcv_mss = u32::from_ne_bytes([data[20], data[21], data[22], data[23]]);

    info.unacked = u32::from_ne_bytes([data[24], data[25], data[26], data[27]]);
    info.sacked = u32::from_ne_bytes([data[28], data[29], data[30], data[31]]);
    info.lost = u32::from_ne_bytes([data[32], data[33], data[34], data[35]]);
    info.retrans = u32::from_ne_bytes([data[36], data[37], data[38], data[39]]);
    info.fackets = u32::from_ne_bytes([data[40], data[41], data[42], data[43]]);

    info.last_data_sent = u32::from_ne_bytes([data[44], data[45], data[46], data[47]]);
    info.last_ack_sent = u32::from_ne_bytes([data[48], data[49], data[50], data[51]]);
    info.last_data_recv = u32::from_ne_bytes([data[52], data[53], data[54], data[55]]);
    info.last_ack_recv = u32::from_ne_bytes([data[56], data[57], data[58], data[59]]);

    info.pmtu = u32::from_ne_bytes([data[60], data[61], data[62], data[63]]);
    info.rcv_ssthresh = u32::from_ne_bytes([data[64], data[65], data[66], data[67]]);
    info.rtt = u32::from_ne_bytes([data[68], data[69], data[70], data[71]]);
    info.rttvar = u32::from_ne_bytes([data[72], data[73], data[74], data[75]]);
    info.snd_ssthresh = u32::from_ne_bytes([data[76], data[77], data[78], data[79]]);
    info.snd_cwnd = u32::from_ne_bytes([data[80], data[81], data[82], data[83]]);
    info.advmss = u32::from_ne_bytes([data[84], data[85], data[86], data[87]]);
    info.reordering = u32::from_ne_bytes([data[88], data[89], data[90], data[91]]);

    info.rcv_rtt = u32::from_ne_bytes([data[92], data[93], data[94], data[95]]);
    info.rcv_space = u32::from_ne_bytes([data[96], data[97], data[98], data[99]]);

    info.total_retrans = u32::from_ne_bytes([data[100], data[101], data[102], data[103]]);

    // Extended fields if available
    if data.len() >= 160 {
        info.pacing_rate = u64::from_ne_bytes([
            data[104], data[105], data[106], data[107], data[108], data[109], data[110], data[111],
        ]);
        info.max_pacing_rate = u64::from_ne_bytes([
            data[112], data[113], data[114], data[115], data[116], data[117], data[118], data[119],
        ]);
        info.bytes_acked = u64::from_ne_bytes([
            data[120], data[121], data[122], data[123], data[124], data[125], data[126], data[127],
        ]);
        info.bytes_received = u64::from_ne_bytes([
            data[128], data[129], data[130], data[131], data[132], data[133], data[134], data[135],
        ]);
        info.segs_out = u32::from_ne_bytes([data[136], data[137], data[138], data[139]]);
        info.segs_in = u32::from_ne_bytes([data[140], data[141], data[142], data[143]]);
        info.notsent_bytes = u32::from_ne_bytes([data[144], data[145], data[146], data[147]]);
        info.min_rtt = u32::from_ne_bytes([data[148], data[149], data[150], data[151]]);
        info.data_segs_in = u32::from_ne_bytes([data[152], data[153], data[154], data[155]]);
        info.data_segs_out = u32::from_ne_bytes([data[156], data[157], data[158], data[159]]);
    }

    if data.len() >= 168 {
        info.delivery_rate = u64::from_ne_bytes([
            data[160], data[161], data[162], data[163], data[164], data[165], data[166], data[167],
        ]);
    }

    info
}

fn parse_unix_msg(data: &[u8]) -> Option<UnixSocket> {
    // Skip netlink header (16 bytes)
    if data.len() < 16 + 16 {
        return None;
    }

    let payload = &data[16..];

    // unix_diag_msg structure
    let family = payload[0];
    if family != libc::AF_UNIX as u8 {
        return None;
    }

    let socket_type = UnixType::from_u8(payload[1])?;
    let state_raw = payload[2];
    // pad at 3

    let inode = u32::from_ne_bytes([payload[4], payload[5], payload[6], payload[7]]);
    let cookie = u64::from_ne_bytes([
        payload[8],
        payload[9],
        payload[10],
        payload[11],
        payload[12],
        payload[13],
        payload[14],
        payload[15],
    ]);

    let state = match state_raw {
        1 => SocketState::Established,
        10 => SocketState::Listen,
        _ => SocketState::Close,
    };

    let mut sock = UnixSocket {
        socket_type,
        state,
        path: None,
        abstract_name: None,
        inode,
        cookie,
        peer_inode: None,
        vfs_dev: None,
        vfs_inode: None,
        recv_q: None,
        send_q: None,
        pending_connections: None,
        uid: None,
        mem_info: None,
        shutdown: None,
    };

    // Parse attributes
    let mut attr_offset = 16 + 16;
    while attr_offset + 4 <= data.len() {
        let attr_len = u16::from_ne_bytes([data[attr_offset], data[attr_offset + 1]]) as usize;
        let attr_type = u16::from_ne_bytes([data[attr_offset + 2], data[attr_offset + 3]]);

        if attr_len < 4 || attr_offset + attr_len > data.len() {
            break;
        }

        let attr_data = &data[attr_offset + 4..attr_offset + attr_len];

        match attr_type {
            UNIX_DIAG_NAME => {
                if !attr_data.is_empty() {
                    if attr_data[0] == 0 {
                        // Abstract socket
                        if let Ok(s) = std::str::from_utf8(&attr_data[1..]) {
                            sock.abstract_name = Some(s.trim_end_matches('\0').to_string());
                        }
                    } else if let Ok(s) = std::str::from_utf8(attr_data) {
                        sock.path = Some(s.trim_end_matches('\0').to_string());
                    }
                }
            }
            UNIX_DIAG_VFS => {
                if attr_data.len() >= 8 {
                    sock.vfs_inode = Some(u32::from_ne_bytes([
                        attr_data[0],
                        attr_data[1],
                        attr_data[2],
                        attr_data[3],
                    ]));
                    sock.vfs_dev = Some(u32::from_ne_bytes([
                        attr_data[4],
                        attr_data[5],
                        attr_data[6],
                        attr_data[7],
                    ]));
                }
            }
            UNIX_DIAG_PEER => {
                if attr_data.len() >= 4 {
                    sock.peer_inode = Some(u32::from_ne_bytes([
                        attr_data[0],
                        attr_data[1],
                        attr_data[2],
                        attr_data[3],
                    ]));
                }
            }
            UNIX_DIAG_ICONS => {
                let mut icons = Vec::new();
                let mut i = 0;
                while i + 4 <= attr_data.len() {
                    icons.push(u32::from_ne_bytes([
                        attr_data[i],
                        attr_data[i + 1],
                        attr_data[i + 2],
                        attr_data[i + 3],
                    ]));
                    i += 4;
                }
                if !icons.is_empty() {
                    sock.pending_connections = Some(icons);
                }
            }
            UNIX_DIAG_RQLEN => {
                if attr_data.len() >= 8 {
                    sock.recv_q = Some(u32::from_ne_bytes([
                        attr_data[0],
                        attr_data[1],
                        attr_data[2],
                        attr_data[3],
                    ]));
                    sock.send_q = Some(u32::from_ne_bytes([
                        attr_data[4],
                        attr_data[5],
                        attr_data[6],
                        attr_data[7],
                    ]));
                }
            }
            UNIX_DIAG_MEMINFO => {
                if attr_data.len() >= 36 {
                    sock.mem_info = Some(MemInfo {
                        rmem_alloc: u32::from_ne_bytes([
                            attr_data[0],
                            attr_data[1],
                            attr_data[2],
                            attr_data[3],
                        ]),
                        rcvbuf: u32::from_ne_bytes([
                            attr_data[4],
                            attr_data[5],
                            attr_data[6],
                            attr_data[7],
                        ]),
                        wmem_alloc: u32::from_ne_bytes([
                            attr_data[8],
                            attr_data[9],
                            attr_data[10],
                            attr_data[11],
                        ]),
                        sndbuf: u32::from_ne_bytes([
                            attr_data[12],
                            attr_data[13],
                            attr_data[14],
                            attr_data[15],
                        ]),
                        fwd_alloc: u32::from_ne_bytes([
                            attr_data[16],
                            attr_data[17],
                            attr_data[18],
                            attr_data[19],
                        ]),
                        wmem_queued: u32::from_ne_bytes([
                            attr_data[20],
                            attr_data[21],
                            attr_data[22],
                            attr_data[23],
                        ]),
                        optmem: u32::from_ne_bytes([
                            attr_data[24],
                            attr_data[25],
                            attr_data[26],
                            attr_data[27],
                        ]),
                        backlog: u32::from_ne_bytes([
                            attr_data[28],
                            attr_data[29],
                            attr_data[30],
                            attr_data[31],
                        ]),
                        drops: u32::from_ne_bytes([
                            attr_data[32],
                            attr_data[33],
                            attr_data[34],
                            attr_data[35],
                        ]),
                    });
                }
            }
            UNIX_DIAG_SHUTDOWN => {
                if !attr_data.is_empty() {
                    sock.shutdown = Some(attr_data[0]);
                }
            }
            UNIX_DIAG_UID => {
                if attr_data.len() >= 4 {
                    sock.uid = Some(u32::from_ne_bytes([
                        attr_data[0],
                        attr_data[1],
                        attr_data[2],
                        attr_data[3],
                    ]));
                }
            }
            _ => {}
        }

        attr_offset += (attr_len + 3) & !3;
    }

    Some(sock)
}

fn parse_netlink_msg(
    data: &[u8],
    filter: &NetlinkFilter,
) -> Option<crate::sockdiag::socket::NetlinkSocket> {
    // netlink_diag_msg: family(1) + type(1) + protocol(2) + state(4) +
    //                   portid(4) + dst_portid(4) + dst_group(4) + ino(4) + cookie(8) = 32 bytes
    if data.len() < 16 + 32 {
        return None;
    }

    let payload = &data[16..];

    let family = payload[0];
    if family != libc::AF_NETLINK as u8 {
        return None;
    }

    // payload[1] is ndiag_type (SOCK_RAW / SOCK_DGRAM)
    let protocol = u16::from_ne_bytes([payload[2], payload[3]]) as u8;

    // Apply protocol filter
    if let Some(filter_proto) = filter.protocol
        && protocol != filter_proto
    {
        return None;
    }

    // state at offset 4..8 (unused for display)
    let portid = u32::from_ne_bytes([payload[8], payload[9], payload[10], payload[11]]);
    let dst_portid = u32::from_ne_bytes([payload[12], payload[13], payload[14], payload[15]]);
    let dst_group = u32::from_ne_bytes([payload[16], payload[17], payload[18], payload[19]]);
    let inode = u32::from_ne_bytes([payload[20], payload[21], payload[22], payload[23]]);
    let cookie = u64::from_ne_bytes([
        payload[24],
        payload[25],
        payload[26],
        payload[27],
        payload[28],
        payload[29],
        payload[30],
        payload[31],
    ]);

    let mut sock = crate::sockdiag::socket::NetlinkSocket {
        protocol,
        portid,
        dst_portid,
        dst_group,
        groups: 0,
        inode,
        cookie,
        recv_q: None,
        send_q: None,
        mem_info: None,
    };

    // Parse attributes
    let mut attr_offset = 16 + 32;
    while attr_offset + 4 <= data.len() {
        let attr_len = u16::from_ne_bytes([data[attr_offset], data[attr_offset + 1]]) as usize;
        let attr_type = u16::from_ne_bytes([data[attr_offset + 2], data[attr_offset + 3]]);

        if attr_len < 4 || attr_offset + attr_len > data.len() {
            break;
        }

        let attr_data = &data[attr_offset + 4..attr_offset + attr_len];

        match attr_type {
            NETLINK_DIAG_MEMINFO => {
                if attr_data.len() >= 36 {
                    sock.mem_info = Some(MemInfo {
                        rmem_alloc: u32::from_ne_bytes([
                            attr_data[0],
                            attr_data[1],
                            attr_data[2],
                            attr_data[3],
                        ]),
                        rcvbuf: u32::from_ne_bytes([
                            attr_data[4],
                            attr_data[5],
                            attr_data[6],
                            attr_data[7],
                        ]),
                        wmem_alloc: u32::from_ne_bytes([
                            attr_data[8],
                            attr_data[9],
                            attr_data[10],
                            attr_data[11],
                        ]),
                        sndbuf: u32::from_ne_bytes([
                            attr_data[12],
                            attr_data[13],
                            attr_data[14],
                            attr_data[15],
                        ]),
                        fwd_alloc: u32::from_ne_bytes([
                            attr_data[16],
                            attr_data[17],
                            attr_data[18],
                            attr_data[19],
                        ]),
                        wmem_queued: u32::from_ne_bytes([
                            attr_data[20],
                            attr_data[21],
                            attr_data[22],
                            attr_data[23],
                        ]),
                        optmem: u32::from_ne_bytes([
                            attr_data[24],
                            attr_data[25],
                            attr_data[26],
                            attr_data[27],
                        ]),
                        backlog: u32::from_ne_bytes([
                            attr_data[28],
                            attr_data[29],
                            attr_data[30],
                            attr_data[31],
                        ]),
                        drops: u32::from_ne_bytes([
                            attr_data[32],
                            attr_data[33],
                            attr_data[34],
                            attr_data[35],
                        ]),
                    });
                }
            }
            NETLINK_DIAG_GROUPS => {
                // Groups bitmask - first 4 bytes give us the basic groups u32
                if attr_data.len() >= 4 {
                    sock.groups = u32::from_ne_bytes([
                        attr_data[0],
                        attr_data[1],
                        attr_data[2],
                        attr_data[3],
                    ]);
                }
            }
            _ => {}
        }

        attr_offset += (attr_len + 3) & !3;
    }

    Some(sock)
}
