// SPDX-License-Identifier: MIT

use netlink_packet_utils::{
    nla::{DefaultNla, NlaBuffer},
    Emitable, Parseable,
};

use crate::link::{
    af_spec::VecAfSpecUnspec, AfSpecInet, AfSpecInet6, AfSpecUnspec,
    Inet6CacheInfo, Inet6DevConf, Inet6IfaceFlag, Inet6IfaceFlags, InetDevConf,
};

#[test]
fn test_link_loopback() {
    // This is nlmon capture for `ip link show lo` on Linux kernel 6.5.8
    let raw: Vec<u8> = vec![
        0x0c, 0x00, // length 12
        0x2d, 0x00, // AF_MCTP 45
        0x08, 0x00, // length 8
        0x01, 0x00, // IFLA_MCTP_NET 1
        0x01, 0x00, 0x00, 0x00, // u32 value 1
        0x8c, 0x00, // length 140
        0x02, 0x00, // AF_INET 2
        0x88, 0x00, // length 136
        0x01, 0x00, // IFLA_INET_CONF
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x27, 0x00, 0x00,
        0xe8, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x10, 0x01, // length 272
        0x0a, 0x00, // AF_INET 6
        0x08, 0x00, // length 8
        0x01, 0x00, // IFLA_INET6_FLAGS 1
        0x00, 0x00, 0x00, 0x80, // IF_READY: 0x80000000
        0x14, 0x00, // length 20
        0x05, 0x00, // IFLA_INET6_CACHEINFO 5
        0xff, 0xff, 0x00, 0x00, // max_reasm_len: 65535
        0xb2, 0x00, 0x00, 0x00, // tstamp: 178
        0x05, 0x75, 0x00, 0x00, // reachable_time: 29957
        0xe8, 0x03, 0x00, 0x00, // retrans_time: 1000
        0xf0, 0x00, // length 240
        0x02, 0x00, // IFLA_INET6_CONF 2
        0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xa0, 0x0f, 0x00, 0x00,
        0xe8, 0x03, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x80, 0x3a, 0x09, 0x00,
        0x80, 0x51, 0x01, 0x00, 0x03, 0x00, 0x00, 0x00, 0x58, 0x02, 0x00, 0x00,
        0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x60, 0xea, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x10, 0x27, 0x00, 0x00, 0xe8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x80, 0xee, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, // End of IFLA_INET6_CONF
    ];

    let expected = vec![
        AfSpecUnspec::Other(DefaultNla::new(
            45,
            vec![8u8, 0, 1, 0, 1, 0, 0, 0],
        )),
        AfSpecUnspec::Inet(vec![AfSpecInet::DevConf(InetDevConf {
            forwarding: 1,
            mc_forwarding: 0,
            proxy_arp: 0,
            accept_redirects: 1,
            secure_redirects: 1,
            send_redirects: 1,
            shared_media: 1,
            rp_filter: 2,
            accept_source_route: 0,
            bootp_relay: 0,
            log_martians: 0,
            tag: 0,
            arpfilter: 0,
            medium_id: 0,
            noxfrm: 1,
            nopolicy: 1,
            force_igmp_version: 0,
            arp_announce: 0,
            arp_ignore: 0,
            promote_secondaries: 1,
            arp_accept: 0,
            arp_notify: 0,
            accept_local: 0,
            src_vmark: 0,
            proxy_arp_pvlan: 0,
            route_localnet: 0,
            igmpv2_unsolicited_report_interval: 10000,
            igmpv3_unsolicited_report_interval: 1000,
            ignore_routes_with_linkdown: 0,
            drop_unicast_in_l2_multicast: 0,
            drop_gratuitous_arp: 0,
            bc_forwarding: 0,
            arp_evict_nocarrier: 1,
        })]),
        AfSpecUnspec::Inet6(vec![
            AfSpecInet6::Flags(Inet6IfaceFlags(vec![Inet6IfaceFlag::Ready])),
            AfSpecInet6::CacheInfo(Inet6CacheInfo {
                max_reasm_len: 65535,
                tstamp: 178,
                reachable_time: 29957,
                retrans_time: 1000,
            }),
            AfSpecInet6::DevConf(Inet6DevConf {
                forwarding: 0,
                hoplimit: 64,
                mtu6: 65536,
                accept_ra: 1,
                accept_redirects: 1,
                autoconf: 1,
                dad_transmits: 1,
                rtr_solicits: -1,
                rtr_solicit_interval: 4000,
                rtr_solicit_delay: 1000,
                use_tempaddr: -1,
                temp_valid_lft: 604800,
                temp_prefered_lft: 86400,
                regen_max_retry: 3,
                max_desync_factor: 600,
                max_addresses: 16,
                force_mld_version: 0,
                accept_ra_defrtr: 1,
                accept_ra_pinfo: 1,
                accept_ra_rtr_pref: 1,
                rtr_probe_interval: 60000,
                accept_ra_rt_info_max_plen: 0,
                proxy_ndp: 0,
                optimistic_dad: 0,
                accept_source_route: 0,
                mc_forwarding: 0,
                disable_ipv6: 0,
                accept_dad: -1,
                force_tllao: 0,
                ndisc_notify: 0,
                mldv1_unsolicited_report_interval: 10000,
                mldv2_unsolicited_report_interval: 1000,
                suppress_frag_ndisc: 1,
                accept_ra_from_local: 0,
                use_optimistic: 0,
                accept_ra_mtu: 1,
                stable_secret: 0,
                use_oif_addrs_only: 0,
                accept_ra_min_hop_limit: 1,
                ignore_routes_with_linkdown: 0,
                drop_unicast_in_l2_multicast: 0,
                drop_unsolicited_na: 0,
                keep_addr_on_down: 0,
                rtr_solicit_max_interval: 3600000,
                seg6_enabled: 0,
                seg6_require_hmac: 0,
                enhanced_dad: 1,
                addr_gen_mode: 0,
                disable_policy: 0,
                accept_ra_rt_info_min_plen: 0,
                ndisc_tclass: 0,
                rpl_seg_enabled: 0,
                ra_defrtr_metric: 1024,
                ioam6_enabled: 0,
                ioam6_id: 65535,
                ioam6_id_wide: -1,
                ndisc_evict_nocarrier: 1,
                accept_untracked_na: 0,
                accept_ra_min_lft: 0,
            }),
        ]),
    ];

    assert_eq!(
        VecAfSpecUnspec::parse(&NlaBuffer::new(&raw)).unwrap().0,
        expected
    );
    let mut buffer = vec![0; expected.as_slice().buffer_len()];
    expected.as_slice().emit(&mut buffer);
    assert_eq!(buffer.as_slice(), raw);
}
