#[allow(unused)]
use netlink_packet_route::{
    rtnl, AddressHeader, AddressMessage, LinkHeader, LinkMessage, NetlinkHeader, NetlinkMessage,
    NetlinkPayload, RtnlMessage, NLM_F_ACK, NLM_F_CREATE, NLM_F_DUMP, NLM_F_EXCL, NLM_F_REQUEST,
};

use ipnetwork::IpNetwork;
use std::fmt::{self, Display, Formatter};

use rnetmgr_lib::netinfo::MacAddr;

#[allow(unused)]
use {
    jlogger_tracing::{jdebug, jerror, jinfo, jwarn, JloggerBuilder, LevelFilter, LogTimeFormat},
};

pub struct NetIf {
    ifname: String,
    ipv4: Vec<IpNetwork>,
    mac: MacAddr,
    flags: u32,
    if_index: u32,
}

impl Default for NetIf {
    fn default() -> Self {
        NetIf {
            ifname: String::new(),
            ipv4: Vec::<IpNetwork>::new(),
            mac: Default::default(),
            flags: 0,
            if_index: NetIf::NETIF_INVALID_IF_INDEX,
        }
    }
}

impl PartialEq for NetIf {
    fn eq(&self, other: &Self) -> bool {
        if self.ifname != other.ifname {
            return false;
        }

        let ipv4_s = self
            .ipv4
            .iter()
            .map(|a| format!("{}", a))
            .collect::<Vec<String>>()
            .join(".");
        let ipv4_o = other
            .ipv4
            .iter()
            .map(|a| format!("{}", a))
            .collect::<Vec<String>>()
            .join(".");

        if ipv4_s != ipv4_o {
            return false;
        }

        if self.mac != other.mac {
            return false;
        }

        if self.if_index != other.if_index {
            return false;
        }

        true
    }
}

impl Eq for NetIf {}

impl Display for NetIf {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let mut ipv4_str = self
            .ipv4
            .iter()
            .map(|a| a.to_string())
            .collect::<Vec<String>>()
            .join("/");

        if ipv4_str.is_empty() {
            ipv4_str = "None".to_string();
        }

        write!(
            f,
            "{} : {} {} {} [{}]",
            self.ifname,
            self.if_index,
            self.mac,
            ipv4_str,
            NetIf::state_flag_str(self.flags).join(" ")
        )
    }
}

impl NetIf {
    pub const NETIF_INVALID_IF_INDEX: u32 = u32::MAX;
    pub fn new(ifname: &str, if_index: u32, mac: MacAddr, flags: u32) -> Self {
        NetIf {
            ifname: ifname.to_string(),
            ipv4: Vec::<IpNetwork>::new(),
            mac,
            flags,
            if_index,
        }
    }

    pub fn is_valid(&self) -> bool {
        if self.ifname.is_empty() {
            return false;
        }

        if !self.mac.is_valid() {
            return false;
        }

        if self.if_index == NetIf::NETIF_INVALID_IF_INDEX {
            return false;
        }
        true
    }

    #[allow(unused)]
    pub fn ifname(&self) -> String {
        self.ifname.clone()
    }

    pub fn if_index(&self) -> u32 {
        self.if_index
    }

    #[allow(unused)]
    pub fn mac(&self) -> &MacAddr {
        &self.mac
    }

    #[allow(unused)]
    pub fn ipv4addr(&self) -> &Vec<IpNetwork> {
        &self.ipv4
    }

    pub fn state_flag_str(flags: u32) -> Vec<String> {
        let mut state = Vec::<String>::new();
        if flags & rtnl::constants::IFF_BROADCAST != 0 {
            state.push("BROADCAST".to_string());
        }

        if flags & rtnl::constants::IFF_MULTICAST != 0 {
            state.push("MULTICAST".to_string());
        }

        if flags & rtnl::constants::IFF_RUNNING != 0 {
            state.push("RUNNING".to_string());
        }

        if flags & rtnl::constants::IFF_RUNNING == 0 {
            state.push("STOPPED".to_string());
        }

        if flags & rtnl::constants::IFF_UP != 0 {
            state.push("UP".to_string());
        }

        if flags & rtnl::constants::IFF_UP == 0 {
            state.push("DOWN".to_string());
        }
        state
    }

    pub fn add_ipv4_addr(&mut self, ipaddr: &IpNetwork) {
        jdebug!("Add {} to {}", ipaddr, self.ifname);
        if !self.ipv4.iter().any(|a| a == ipaddr) {
            self.ipv4.push(*ipaddr);
        }
    }

    pub fn del_ipv4_addr(&mut self, ipaddr: &IpNetwork) {
        jdebug!("Del {} to {}", ipaddr, self.ifname);
        self.ipv4.retain(|addr| addr != ipaddr);
    }

    #[allow(unused)]
    pub fn flags(&self) -> u32 {
        self.flags
    }

    pub fn update_flags(&mut self, new_flag: u32) {
        self.flags = new_flag;
    }
}
