//! Protocol state types for strongly-typed connections.
//!
//! This module provides the type-level protocol distinction that enables
//! compile-time safety for protocol-specific operations on [`Connection`].
//!
//! # Design
//!
//! Each netlink protocol (Route, Generic, etc.) has an associated state type
//! that may carry protocol-specific data:
//!
//! - [`Route`]: Zero-sized, no additional state needed
//! - [`Generic`]: Contains a family ID cache for efficient lookups
//!
//! The [`ProtocolState`] trait is sealed to prevent external implementations.
//!
//! # Example
//!
//! ```ignore
//! use nlink::netlink::{Connection, Route, Generic};
//!
//! // Route connection for interface/address/route/TC operations
//! let route = Connection::<Route>::new()?;
//! route.get_links().await?;
//!
//! // Generic connection for WireGuard/MACsec configuration
//! let genl = Connection::<Generic>::new()?;
//! genl.get_family("wireguard").await?;
//! ```

use std::collections::HashMap;
use std::sync::RwLock;

use super::genl::FamilyInfo;
use super::socket::Protocol;

/// Sealed trait module to prevent external implementations.
mod private {
    pub trait Sealed {}
}

/// Protocol state trait for typed connections.
///
/// This trait is sealed and cannot be implemented outside this crate.
/// Each implementation provides:
/// - The underlying netlink protocol constant
/// - Protocol-specific state (if any)
///
/// Types that implement `Default` can use the generic `Connection::new()`.
/// Types that require special initialization should provide their own constructor.
pub trait ProtocolState: private::Sealed {
    /// The netlink protocol for this state type.
    const PROTOCOL: Protocol;
}

/// Route protocol state (RTNetlink).
///
/// Used for interface, address, route, neighbor, and traffic control operations.
/// This is a zero-sized type with no additional state.
#[derive(Debug, Default, Clone, Copy)]
pub struct Route;

impl private::Sealed for Route {}

impl ProtocolState for Route {
    const PROTOCOL: Protocol = Protocol::Route;
}

/// Socket diagnostics protocol state.
///
/// Used for querying socket information (TCP, UDP, Unix, etc.).
/// This is a zero-sized type with no additional state.
#[derive(Debug, Default, Clone, Copy)]
pub struct SockDiag;

impl private::Sealed for SockDiag {}

impl ProtocolState for SockDiag {
    const PROTOCOL: Protocol = Protocol::SockDiag;
}

/// Generic netlink protocol state.
///
/// Used for family-based protocols like WireGuard and MACsec.
/// Contains a cache of resolved family IDs for efficient lookups.
pub struct Generic {
    /// Cache of family name -> family info mappings.
    pub(crate) cache: RwLock<HashMap<String, FamilyInfo>>,
}

impl Default for Generic {
    fn default() -> Self {
        Self {
            cache: RwLock::new(HashMap::new()),
        }
    }
}

impl std::fmt::Debug for Generic {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Generic")
            .field(
                "cache_size",
                &self.cache.read().map(|c| c.len()).unwrap_or(0),
            )
            .finish()
    }
}

impl private::Sealed for Generic {}

impl ProtocolState for Generic {
    const PROTOCOL: Protocol = Protocol::Generic;
}

/// WireGuard protocol state.
///
/// Used for WireGuard device configuration via Generic Netlink.
/// Contains the resolved WireGuard family ID.
#[derive(Debug, Default)]
pub struct Wireguard {
    /// Resolved WireGuard GENL family ID.
    pub(crate) family_id: u16,
}

impl private::Sealed for Wireguard {}

impl ProtocolState for Wireguard {
    const PROTOCOL: Protocol = Protocol::Generic;
}

/// Kobject uevent protocol state.
///
/// Used for receiving kernel object events (device hotplug, like udev).
/// This is a zero-sized type with no additional state.
///
/// Note: Does not implement `Default` because connections require
/// multicast group subscription. Use `Connection::<KobjectUevent>::new()`.
#[derive(Debug, Clone, Copy)]
pub struct KobjectUevent;

impl private::Sealed for KobjectUevent {}

impl ProtocolState for KobjectUevent {
    const PROTOCOL: Protocol = Protocol::KobjectUevent;
}

/// Connector protocol state.
///
/// Used for kernel connector events, primarily process events (fork/exec/exit).
/// This is a zero-sized type with no additional state.
///
/// Note: Does not implement `Default` because connections require
/// registration with the kernel. Use `Connection::<Connector>::new()`.
#[derive(Debug, Clone, Copy)]
pub struct Connector;

impl private::Sealed for Connector {}

impl ProtocolState for Connector {
    const PROTOCOL: Protocol = Protocol::Connector;
}

/// Netfilter protocol state.
///
/// Used for netfilter operations, primarily connection tracking (conntrack).
/// This is a zero-sized type with no additional state.
#[derive(Debug, Clone, Copy)]
pub struct Netfilter;

impl private::Sealed for Netfilter {}

impl ProtocolState for Netfilter {
    const PROTOCOL: Protocol = Protocol::Netfilter;
}

/// XFRM protocol state.
///
/// Used for IPsec Security Association (SA) and Security Policy (SP) management.
/// This is a zero-sized type with no additional state.
#[derive(Debug, Clone, Copy)]
pub struct Xfrm;

impl private::Sealed for Xfrm {}

impl ProtocolState for Xfrm {
    const PROTOCOL: Protocol = Protocol::Xfrm;
}

/// FIB lookup protocol state.
///
/// Used for performing FIB (Forwarding Information Base) route lookups.
/// This is a zero-sized type with no additional state.
#[derive(Debug, Clone, Copy)]
pub struct FibLookup;

impl private::Sealed for FibLookup {}

impl ProtocolState for FibLookup {
    const PROTOCOL: Protocol = Protocol::FibLookup;
}

/// SELinux protocol state.
///
/// Used for receiving SELinux event notifications (setenforce, policyload).
/// This is a zero-sized type with no additional state.
///
/// Note: Does not implement `Default` because connections require
/// multicast group subscription. Use `Connection::<SELinux>::new()`.
#[derive(Debug, Clone, Copy)]
pub struct SELinux;

impl private::Sealed for SELinux {}

impl ProtocolState for SELinux {
    const PROTOCOL: Protocol = Protocol::SELinux;
}

/// Audit protocol state.
///
/// Used for interacting with the Linux Audit subsystem.
/// This is a zero-sized type with no additional state.
#[derive(Debug, Clone, Copy)]
pub struct Audit;

impl private::Sealed for Audit {}

impl ProtocolState for Audit {
    const PROTOCOL: Protocol = Protocol::Audit;
}

/// MACsec protocol state.
///
/// Used for MACsec (IEEE 802.1AE) device configuration via Generic Netlink.
/// Contains the resolved MACsec family ID.
#[derive(Debug, Default)]
pub struct Macsec {
    /// Resolved MACsec GENL family ID.
    pub(crate) family_id: u16,
}

impl private::Sealed for Macsec {}

impl ProtocolState for Macsec {
    const PROTOCOL: Protocol = Protocol::Generic;
}

/// MPTCP protocol state.
///
/// Used for MPTCP (Multipath TCP) endpoint configuration via Generic Netlink.
/// Contains the resolved MPTCP Path Manager family ID.
#[derive(Debug, Default)]
pub struct Mptcp {
    /// Resolved MPTCP PM GENL family ID.
    pub(crate) family_id: u16,
}

impl private::Sealed for Mptcp {}

impl ProtocolState for Mptcp {
    const PROTOCOL: Protocol = Protocol::Generic;
}

/// Devlink protocol state.
///
/// Used for querying hardware device management via Generic Netlink.
/// Contains the resolved devlink family ID.
#[derive(Debug, Default)]
pub struct Devlink {
    /// Resolved devlink GENL family ID.
    pub(crate) family_id: u16,
    /// Multicast group ID for event notifications.
    pub(crate) monitor_group_id: Option<u32>,
}

impl private::Sealed for Devlink {}

impl ProtocolState for Devlink {
    const PROTOCOL: Protocol = Protocol::Generic;
}

/// Nftables protocol state.
///
/// Used for nftables firewall management via NETLINK_NETFILTER.
/// This is a zero-sized type with no additional state.
#[derive(Debug, Default, Clone, Copy)]
pub struct Nftables;

impl private::Sealed for Nftables {}

impl ProtocolState for Nftables {
    const PROTOCOL: Protocol = Protocol::Netfilter;
}

/// nl80211 protocol state.
///
/// Used for WiFi configuration via Generic Netlink.
/// Contains the resolved nl80211 family ID.
#[derive(Debug, Default)]
pub struct Nl80211 {
    /// Resolved nl80211 GENL family ID.
    pub(crate) family_id: u16,
    /// Multicast group IDs for event monitoring.
    pub(crate) scan_group_id: Option<u32>,
    pub(crate) mlme_group_id: Option<u32>,
    pub(crate) regulatory_group_id: Option<u32>,
    pub(crate) config_group_id: Option<u32>,
}

impl private::Sealed for Nl80211 {}

impl ProtocolState for Nl80211 {
    const PROTOCOL: Protocol = Protocol::Generic;
}

/// Ethtool protocol state.
///
/// Used for querying and configuring network device settings via Generic Netlink.
/// Contains the resolved ethtool family ID and monitor group ID.
#[derive(Debug, Default)]
pub struct Ethtool {
    /// Resolved ethtool GENL family ID.
    pub(crate) family_id: u16,
    /// Monitor multicast group ID (for event notifications).
    pub(crate) monitor_group_id: Option<u32>,
}

impl private::Sealed for Ethtool {}

impl ProtocolState for Ethtool {
    const PROTOCOL: Protocol = Protocol::Generic;
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn route_is_zero_sized() {
        assert_eq!(std::mem::size_of::<Route>(), 0);
    }

    #[test]
    fn sockdiag_is_zero_sized() {
        assert_eq!(std::mem::size_of::<SockDiag>(), 0);
    }

    #[test]
    fn generic_has_cache() {
        let g = Generic::default();
        assert!(g.cache.read().unwrap().is_empty());
    }

    #[test]
    fn protocol_constants() {
        assert_eq!(Route::PROTOCOL, Protocol::Route);
        assert_eq!(SockDiag::PROTOCOL, Protocol::SockDiag);
        assert_eq!(Generic::PROTOCOL, Protocol::Generic);
        assert_eq!(Wireguard::PROTOCOL, Protocol::Generic);
        assert_eq!(Macsec::PROTOCOL, Protocol::Generic);
        assert_eq!(Mptcp::PROTOCOL, Protocol::Generic);
        assert_eq!(Ethtool::PROTOCOL, Protocol::Generic);
        assert_eq!(Nl80211::PROTOCOL, Protocol::Generic);
        assert_eq!(KobjectUevent::PROTOCOL, Protocol::KobjectUevent);
        assert_eq!(Connector::PROTOCOL, Protocol::Connector);
        assert_eq!(Netfilter::PROTOCOL, Protocol::Netfilter);
        assert_eq!(Nftables::PROTOCOL, Protocol::Netfilter);
        assert_eq!(Xfrm::PROTOCOL, Protocol::Xfrm);
        assert_eq!(FibLookup::PROTOCOL, Protocol::FibLookup);
        assert_eq!(SELinux::PROTOCOL, Protocol::SELinux);
        assert_eq!(Audit::PROTOCOL, Protocol::Audit);
    }

    #[test]
    fn new_types_are_zero_sized() {
        assert_eq!(std::mem::size_of::<KobjectUevent>(), 0);
        assert_eq!(std::mem::size_of::<Connector>(), 0);
        assert_eq!(std::mem::size_of::<Netfilter>(), 0);
        assert_eq!(std::mem::size_of::<Xfrm>(), 0);
        assert_eq!(std::mem::size_of::<FibLookup>(), 0);
        assert_eq!(std::mem::size_of::<SELinux>(), 0);
        assert_eq!(std::mem::size_of::<Audit>(), 0);
        assert_eq!(std::mem::size_of::<Nftables>(), 0);
    }
}
