use rpki::data_model::rc::{
    Afi, AsIdOrRange, AsIdentifierChoice, AsResourceSet, IpAddressChoice, IpAddressOrRange,
    IpResourceSet,
};

fn len_bytes(len: usize) -> Vec<u8> {
    if len < 128 {
        vec![len as u8]
    } else {
        let mut tmp = Vec::new();
        let mut n = len;
        while n > 0 {
            tmp.push((n & 0xFF) as u8);
            n >>= 8;
        }
        tmp.reverse();
        let mut out = vec![0x80 | (tmp.len() as u8)];
        out.extend(tmp);
        out
    }
}

fn tlv(tag: u8, content: &[u8]) -> Vec<u8> {
    let mut out = vec![tag];
    out.extend(len_bytes(content.len()));
    out.extend_from_slice(content);
    out
}

fn der_sequence(children: Vec<Vec<u8>>) -> Vec<u8> {
    let mut content = Vec::new();
    for c in children {
        content.extend(c);
    }
    tlv(0x30, &content)
}

fn der_octet_string(bytes: &[u8]) -> Vec<u8> {
    tlv(0x04, bytes)
}

fn der_null() -> Vec<u8> {
    vec![0x05, 0x00]
}

fn der_integer_u64(v: u64) -> Vec<u8> {
    let mut bytes = Vec::new();
    let mut n = v;
    if n == 0 {
        bytes.push(0);
    } else {
        while n > 0 {
            bytes.push((n & 0xFF) as u8);
            n >>= 8;
        }
        bytes.reverse();
        if bytes[0] & 0x80 != 0 {
            bytes.insert(0, 0);
        }
    }
    tlv(0x02, &bytes)
}

fn der_bit_string(unused: u8, bytes: &[u8]) -> Vec<u8> {
    let mut content = vec![unused];
    content.extend_from_slice(bytes);
    tlv(0x03, &content)
}

fn cs_cons(tag_no: u8, inner_der: Vec<u8>) -> Vec<u8> {
    tlv(0xA0 | (tag_no & 0x1F), &inner_der)
}

#[test]
fn ip_addr_blocks_decode_handles_inherit_and_range_endpoint_fill() {
    // IPv6 family with one range:
    // - min: 240a:a000::/32 (encoded as 32 bits)
    // - max: 240a:a008::/31 (encoded as 31 bits, so fill-ones yields 240a:a009:ffff..)
    let address_family = der_octet_string(&[0x00, 0x02]);
    let min = der_bit_string(0, &[0x24, 0x0A, 0xA0, 0x00]);
    let max = der_bit_string(1, &[0x24, 0x0A, 0xA0, 0x08]);
    let range = der_sequence(vec![min, max]);
    let choice = der_sequence(vec![range]);
    let fam = der_sequence(vec![address_family, choice]);
    let ip_addr_blocks = der_sequence(vec![fam]);

    let decoded = IpResourceSet::decode_extn_value(&ip_addr_blocks).expect("decode ipAddrBlocks");
    assert_eq!(decoded.families.len(), 1);
    let fam = &decoded.families[0];
    assert_eq!(fam.afi, Afi::Ipv6);
    let IpAddressChoice::AddressesOrRanges(items) = &fam.choice else {
        panic!("expected AddressesOrRanges");
    };
    let IpAddressOrRange::Range(r) = &items[0] else {
        panic!("expected Range");
    };
    assert_eq!(r.min[..4], [0x24, 0x0A, 0xA0, 0x00]);
    assert_eq!(r.max[..4], [0x24, 0x0A, 0xA0, 0x09]);
    assert!(r.max[4..].iter().all(|&b| b == 0xFF));

    // Inherit branch.
    let fam_inherit = der_sequence(vec![der_octet_string(&[0x00, 0x01]), der_null()]);
    let decoded =
        IpResourceSet::decode_extn_value(&der_sequence(vec![fam_inherit])).expect("decode inherit");
    assert!(decoded.is_all_inherit());
}

#[test]
fn autonomous_sys_ids_decode_handles_inherit_ids_and_ranges() {
    // ASIdentifiers with:
    // - asnum [0] EXPLICIT asIdsOrRanges { id 64496, range 64500..64510 }
    let as_id_or_ranges = der_sequence(vec![
        der_integer_u64(64496),
        der_sequence(vec![der_integer_u64(64500), der_integer_u64(64510)]),
    ]);
    let asnum = cs_cons(0, as_id_or_ranges);
    let as_identifiers = der_sequence(vec![asnum]);

    let decoded = AsResourceSet::decode_extn_value(&as_identifiers).expect("decode asIdentifiers");
    assert!(decoded.rdi.is_none());
    let Some(asnum) = decoded.asnum else {
        panic!("missing asnum");
    };
    let AsIdentifierChoice::AsIdsOrRanges(items) = asnum else {
        panic!("expected AsIdsOrRanges");
    };
    assert_eq!(items.len(), 2);
    assert!(matches!(items[0], AsIdOrRange::Id(64496)));
    assert!(matches!(
        items[1],
        AsIdOrRange::Range {
            min: 64500,
            max: 64510
        }
    ));

    // asnum inherit.
    let asnum_inherit = cs_cons(0, der_null());
    let decoded = AsResourceSet::decode_extn_value(&der_sequence(vec![asnum_inherit]))
        .expect("decode inherit");
    assert!(decoded.is_asnum_inherit());
}
