#![cfg(feature = "derive")]

use core::marker::PhantomData;

#[cfg(feature = "p3-baby-bear")]
use p3_baby_bear::BabyBear;
#[cfg(feature = "p3-baby-bear")]
use p3_field::PrimeField32;
use spongefish::{Codec, Encoding, NargDeserialize, NargSerialize};

#[derive(Clone, Copy, Debug, PartialEq, Eq, Codec)]
struct TaggedValue<T, const N: usize> {
    value: u32,
    #[spongefish(skip)]
    _marker: PhantomData<(T, [(); N])>,
}

#[cfg(feature = "p3-baby-bear")]
#[allow(dead_code)]
#[derive(Debug, spongefish::NargDeserialize)]
struct TwoBabyBears {
    first: BabyBear,
    second: BabyBear,
}

#[test]
fn codec_derive_handles_generic_types() {
    let tagged = TaggedValue::<u8, 4> {
        value: 7,
        _marker: PhantomData,
    };

    let encoded = tagged.encode();
    assert_eq!(encoded.as_ref(), 7u32.to_le_bytes());

    let serialized = tagged.serialize_into_new_narg();
    let mut buf: &[u8] = serialized.as_ref();
    let roundtrip = TaggedValue::<u8, 4>::deserialize_from_narg(&mut buf).expect("roundtrip");
    assert_eq!(roundtrip.value, tagged.value);
    assert!(buf.is_empty());

    #[allow(clippy::items_after_statements)]
    fn assert_codec<T: Codec>(_: &T) {}
    assert_codec(&tagged);
}

#[test]
#[cfg(feature = "p3-baby-bear")]
fn derive_deserialize_rejects_without_advancing_cursor() {
    let input = [
        1u32.to_be_bytes().as_slice(),
        BabyBear::ORDER_U32.to_be_bytes().as_slice(),
        &[9],
    ]
    .concat();
    let mut slice = input.as_slice();

    assert!(TwoBabyBears::deserialize_from_narg(&mut slice).is_err());
    assert_eq!(slice, input.as_slice());
}
