use libremetaverse::PacketFrequency;
use libremetaverse::packet_catalog::{
    BlockRepetition, FieldKind, PACKETS, descriptor_by_name, descriptor_by_type,
};
use libremetaverse::packets::{
    Packet, PacketAckPacket, PacketType, TestMessagePacket, UseCircuitCodePacket,
};
use std::collections::BTreeSet;

#[test]
fn generated_catalog_matches_the_complete_pinned_protocol() {
    assert_eq!(PACKETS.len(), 483);
    assert_eq!(
        PACKETS
            .iter()
            .filter(|packet| packet.frequency == PacketFrequency::Low)
            .count(),
        434
    );
    assert_eq!(
        PACKETS
            .iter()
            .filter(|packet| packet.frequency == PacketFrequency::Medium)
            .count(),
        17
    );
    assert_eq!(
        PACKETS
            .iter()
            .filter(|packet| packet.frequency == PacketFrequency::High)
            .count(),
        32
    );

    let names = PACKETS
        .iter()
        .map(|packet| packet.name)
        .collect::<BTreeSet<_>>();
    let ids = PACKETS
        .iter()
        .map(|packet| (packet.frequency as u8, packet.id))
        .collect::<BTreeSet<_>>();
    assert_eq!(names.len(), PACKETS.len());
    assert_eq!(ids.len(), PACKETS.len());

    for descriptor in PACKETS {
        assert_eq!(descriptor_by_name(descriptor.name), Some(descriptor));
        assert_eq!(descriptor_by_type(descriptor.packet_type), Some(descriptor));
        assert_eq!(
            Packet::get_type(descriptor.id, descriptor.frequency).unwrap(),
            descriptor.packet_type
        );
    }
}

#[test]
fn generated_ids_repetition_and_field_widths_match_golden_entries() {
    assert_eq!(PacketType::TestMessage as i32, 0x1_0001);
    assert_eq!(PacketType::PacketAck as i32, 0x1_fffb);
    assert_eq!(PacketType::ObjectUpdate as i32, 0x3_000c);

    let test = descriptor_by_name("TestMessage").unwrap();
    assert_eq!(test.id, 1);
    assert_eq!(test.blocks.len(), 2);
    assert_eq!(test.blocks[0].repetition, BlockRepetition::Single);
    assert_eq!(test.blocks[1].repetition, BlockRepetition::Multiple(4));
    assert_eq!(test.blocks[1].fields.len(), 3);
    assert!(
        test.blocks[1]
            .fields
            .iter()
            .all(|field| field.kind == FieldKind::U32 && field.fixed_width == 4)
    );

    let ack = descriptor_by_name("PacketAck").unwrap();
    assert_eq!(ack.id, 0xfffb);
    assert_eq!(ack.frequency, PacketFrequency::Low);
    assert_eq!(ack.blocks[0].repetition, BlockRepetition::Variable);
    assert_eq!(ack.blocks[0].fields[0].kind, FieldKind::U32);

    let object_update = descriptor_by_name("ObjectUpdate").unwrap();
    assert!(object_update.zerocoded);
    assert!(object_update.trusted);
    assert!(
        object_update
            .blocks
            .iter()
            .flat_map(|block| block.fields)
            .any(|field| field.kind == FieldKind::Fixed)
    );
}

#[test]
fn generated_constructors_initialize_blocks_headers_and_sizing_metadata() {
    let test = TestMessagePacket::new_with_constructor().unwrap();
    assert_eq!(test.base.type_, PacketType::TestMessage);
    assert_eq!(test.base.header.frequency, PacketFrequency::Low);
    assert_eq!(test.base.header.id, 1);
    assert!(test.base.header.reliable);
    assert!(test.base.header.zerocoded);
    assert_eq!(test.neighbor_block.len(), 4);
    assert_eq!(test.length(), 62);
    assert!(test.uses_buffer_pooling());

    let ack = PacketAckPacket::new_with_constructor().unwrap();
    assert!(ack.packets.is_empty());
    // The low-frequency header contributes ten bytes and the empty variable
    // block contributes its single count byte.
    assert_eq!(ack.length(), 11);
    assert!(!ack.uses_buffer_pooling());

    let circuit = UseCircuitCodePacket::new_with_constructor().unwrap();
    assert_eq!(circuit.circuit_code.code, 0);
    assert_eq!(circuit.circuit_code.id, libremetaverse_types::UUID::zero());
    assert_eq!(
        circuit.circuit_code.session_id,
        libremetaverse_types::UUID::zero()
    );
    assert_eq!(circuit.length(), 46);
    assert!(circuit.uses_buffer_pooling());
}

#[test]
fn generated_dispatch_handles_known_and_unknown_packets_without_fallbacks() {
    let packet = Packet::build_packet_with_packet_type(PacketType::UseCircuitCode).unwrap();
    assert_eq!(packet.type_, PacketType::UseCircuitCode);
    assert_eq!(packet.header.frequency, PacketFrequency::Low);
    assert_eq!(packet.header.id, 3);
    assert!(packet.header.reliable);
    assert!(!packet.header.zerocoded);
    assert!(!packet.has_variable_blocks);

    assert_eq!(
        Packet::get_type(u16::MAX, PacketFrequency::High).unwrap(),
        PacketType::Default
    );
    assert!(matches!(
        Packet::build_packet_with_packet_type(PacketType::Default),
        Err(libremetaverse::Error::InvalidOperation)
    ));
    assert_eq!(descriptor_by_name("NotAPacket"), None);
    assert_eq!(descriptor_by_type(PacketType::Default), None);
}
