use bytes::BufMut;

use super::{Encoder, EncoderSlice};

#[test]
fn encoder_new_capacity_is_page() {
    assert_eq!(Encoder::new().capacity(), Encoder::PAGE);
}

#[test]
fn encoder_with_capacity_zero_gives_zero() {
    assert_eq!(Encoder::with_capacity(0).capacity(), 0);
}

#[test]
fn encoder_with_capacity_one_gives_page() {
    assert_eq!(Encoder::with_capacity(1).capacity(), Encoder::PAGE);
}

#[test]
fn encoder_with_capacity_exact_page() {
    assert_eq!(
        Encoder::with_capacity(Encoder::PAGE).capacity(),
        Encoder::PAGE
    );
}

#[test]
fn encoder_with_capacity_page_plus_one_rounds_up() {
    assert_eq!(
        Encoder::with_capacity(Encoder::PAGE + 1).capacity(),
        2 * Encoder::PAGE
    );
}

#[test]
fn encoder_with_capacity_two_pages_exact() {
    assert_eq!(
        Encoder::with_capacity(2 * Encoder::PAGE).capacity(),
        2 * Encoder::PAGE
    );
}

#[test]
fn encoder_initial_is_empty() {
    let enc = Encoder::new();
    assert_eq!(enc.len(), 0);
    assert!(enc.is_empty());
}

#[test]
fn encoder_put_u8_single() {
    let mut enc = Encoder::new();
    enc.put_u8(0xAB);
    assert_eq!(&*enc, &[0xABu8]);
}

#[test]
fn encoder_put_u8_sequential() {
    let mut enc = Encoder::new();
    enc.put_u8(1);
    enc.put_u8(2);
    enc.put_u8(3);
    assert_eq!(&*enc, &[1u8, 2, 3]);
}

#[test]
fn encoder_put_slice_empty_is_noop() {
    let mut enc = Encoder::new();
    enc.put_slice(&[]);
    assert!(enc.is_empty());
}

#[test]
fn encoder_put_slice_basic() {
    let mut enc = Encoder::new();
    enc.put_slice(&[10u8, 20, 30]);
    assert_eq!(&*enc, &[10u8, 20, 30]);
}

#[test]
fn encoder_put_slice_multiple_accumulates() {
    let mut enc = Encoder::new();
    enc.put_slice(&[1u8, 2]);
    enc.put_slice(&[3u8, 4]);
    enc.put_slice(&[5u8]);
    assert_eq!(&*enc, &[1u8, 2, 3, 4, 5]);
}

#[test]
fn encoder_put_bytes_fills_value() {
    let mut enc = Encoder::new();
    enc.put_bytes(0xFF, 4);
    assert_eq!(&*enc, &[0xFFu8; 4]);
}

#[test]
fn encoder_put_bytes_zero_count_is_noop() {
    let mut enc = Encoder::new();
    enc.put_bytes(0xAA, 0);
    assert!(enc.is_empty());
}

#[test]
fn encoder_put_generic_buf() {
    let mut enc = Encoder::new();
    enc.put(bytes::Bytes::from_static(&[7u8, 8, 9]));
    assert_eq!(&*enc, &[7u8, 8, 9]);
}

#[test]
fn encoder_put_generic_buf_sequential() {
    let mut enc = Encoder::new();
    enc.put(bytes::Bytes::from_static(&[1u8, 2, 3]));
    enc.put(bytes::Bytes::from_static(&[4u8, 5]));
    assert_eq!(&*enc, &[1u8, 2, 3, 4, 5]);
}

#[test]
fn encoder_grows_beyond_one_page() {
    let mut enc = Encoder::new();
    let data: alloc::vec::Vec<u8> = (0u8..=255).cycle().take(Encoder::PAGE + 100).collect();
    enc.put_slice(&data);
    assert_eq!(&*enc, data.as_slice());
    assert!(enc.capacity() >= Encoder::PAGE + 100);
}

#[test]
fn encoder_capacity_expands_by_full_page() {
    let mut enc = Encoder::with_capacity(Encoder::PAGE);
    enc.put_bytes(0, Encoder::PAGE);
    let _ = enc.chunk_mut();
    assert!(enc.capacity() >= 2 * Encoder::PAGE);
}

#[test]
fn encoder_multiple_page_growth() {
    let mut enc = Encoder::new();
    let data = alloc::vec![0u8; Encoder::PAGE * 3 + 1];
    enc.put_slice(&data);
    assert_eq!(enc.len(), data.len());
    assert!(enc.capacity() >= data.len());
}

#[test]
fn encoder_into_inner_yields_written_bytes() {
    let mut enc = Encoder::new();
    enc.put_slice(&[1u8, 2, 3]);
    assert_eq!(enc.into_inner(), alloc::vec![1u8, 2, 3]);
}

#[test]
fn encoder_into_inner_empty() {
    assert_eq!(Encoder::new().into_inner(), alloc::vec::Vec::<u8>::new());
}

#[test]
fn encoder_deref_reflects_only_written() {
    let mut enc = Encoder::new();
    enc.put_u8(42);
    assert_eq!(enc.len(), 1);
    assert_eq!(enc[0], 42);
}

#[test]
fn encoder_remaining_mut_is_isize_max_when_empty() {
    assert_eq!(Encoder::new().remaining_mut(), isize::MAX as usize);
}

#[test]
fn encoder_remaining_mut_decrements_per_write() {
    let mut enc = Encoder::new();
    let before = enc.remaining_mut();
    enc.put_u8(1);
    assert_eq!(enc.remaining_mut(), before - 1);
    enc.put_slice(&[2u8, 3, 4]);
    assert_eq!(enc.remaining_mut(), before - 4);
}

#[test]
fn encoder_advance_mut_increments_len() {
    let mut enc = Encoder::with_capacity(16);
    let _ = enc.chunk_mut();
    unsafe { enc.advance_mut(4) };
    assert_eq!(enc.len(), 4);
}

#[test]
fn encoder_chunk_mut_triggers_grow_when_at_capacity() {
    let mut enc = Encoder::with_capacity(Encoder::PAGE);
    enc.put_bytes(0, enc.capacity());
    let chunk = enc.chunk_mut();
    assert!(chunk.len() >= Encoder::PAGE);
}

#[test]
fn encoder_clone_equals_original() {
    let mut enc = Encoder::new();
    enc.put_slice(&[1u8, 2, 3]);
    assert_eq!(enc.clone(), enc);
}

#[test]
fn encoder_partial_eq_empty() {
    assert_eq!(Encoder::new(), Encoder::new());
}

#[test]
fn encoder_partial_eq_same_content() {
    let mut a = Encoder::new();
    let mut b = Encoder::new();
    a.put_slice(&[1u8, 2, 3]);
    b.put_slice(&[1u8, 2, 3]);
    assert_eq!(a, b);
}

#[test]
fn encoder_partial_eq_different_content() {
    let mut a = Encoder::new();
    let mut b = Encoder::new();
    a.put_u8(1);
    b.put_u8(2);
    assert_ne!(a, b);
}

#[test]
fn encoder_partial_eq_different_lengths() {
    let mut a = Encoder::new();
    let mut b = Encoder::new();
    a.put_slice(&[1u8, 2, 3]);
    b.put_slice(&[1u8, 2]);
    assert_ne!(a, b);
}

#[test]
fn encoder_slice_remaining_equals_buffer_len() {
    let mut buf = [0u8; 16];
    let enc = EncoderSlice::new(&mut buf);
    assert_eq!(enc.remaining_mut(), 16);
}

#[test]
fn encoder_slice_empty_buffer_remaining_is_zero() {
    let enc = EncoderSlice::new(&mut []);
    assert_eq!(enc.remaining_mut(), 0);
}

#[test]
fn encoder_slice_initial_written_is_empty() {
    let mut buf = [0u8; 8];
    let enc = EncoderSlice::new(&mut buf);
    assert!(enc.is_empty());
    assert_eq!(enc.written(), &[] as &[u8]);
}

#[test]
fn encoder_slice_put_u8_decrements_remaining() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_u8(0xCC);
    assert_eq!(enc.remaining_mut(), 7);
}

#[test]
fn encoder_slice_put_u8_value_in_written() {
    let mut buf = [0u8; 4];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_u8(55);
    assert_eq!(enc.written(), &[55u8]);
}

#[test]
fn encoder_slice_put_u8_sequential() {
    let mut buf = [0u8; 4];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_u8(1);
    enc.put_u8(2);
    enc.put_u8(3);
    assert_eq!(enc.written(), &[1u8, 2, 3]);
}

#[test]
fn encoder_slice_put_slice_basic() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[1u8, 2, 3]);
    assert_eq!(enc.remaining_mut(), 5);
    assert_eq!(enc.written(), &[1u8, 2, 3]);
}

#[test]
fn encoder_slice_put_slice_empty_is_noop() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[]);
    assert_eq!(enc.remaining_mut(), 8);
    assert!(enc.written().is_empty());
}

#[test]
fn encoder_slice_put_slice_multiple_accumulates() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[1u8, 2]);
    enc.put_slice(&[3u8, 4]);
    enc.put_slice(&[5u8]);
    assert_eq!(enc.written(), &[1u8, 2, 3, 4, 5]);
}

#[test]
fn encoder_slice_put_bytes_fills_value() {
    let mut buf = [0u8; 6];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_bytes(0xAB, 4);
    assert_eq!(enc.written(), &[0xABu8; 4]);
}

#[test]
fn encoder_slice_put_bytes_zero_count_is_noop() {
    let mut buf = [0u8; 4];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_bytes(0xFF, 0);
    assert_eq!(enc.remaining_mut(), 4);
    assert!(enc.written().is_empty());
}

#[test]
fn encoder_slice_fill_to_exact_capacity() {
    let mut buf = [0u8; 4];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[10u8, 20, 30, 40]);
    assert_eq!(enc.remaining_mut(), 0);
    assert_eq!(enc.written(), &[10u8, 20, 30, 40]);
}

#[test]
fn encoder_slice_mixed_puts_accumulate() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_u8(1);
    enc.put_slice(&[2u8, 3]);
    enc.put_bytes(4, 2);
    assert_eq!(enc.written(), &[1u8, 2, 3, 4, 4]);
}

#[test]
fn encoder_slice_written_borrows_without_consuming() {
    let mut buf = [0u8; 4];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_u8(7);
    assert_eq!(enc.written(), &[7u8]);
    enc.put_u8(8);
    assert_eq!(enc.written(), &[7u8, 8]);
}

#[test]
fn encoder_slice_into_written_empty() {
    let mut buf = [0u8; 4];
    let enc = EncoderSlice::new(&mut buf);
    assert_eq!(enc.into_written(), &[] as &[u8]);
}

#[test]
fn encoder_slice_into_written_partial() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[9u8, 8, 7]);
    assert_eq!(enc.into_written(), &[9u8, 8, 7]);
}

#[test]
fn encoder_slice_into_written_full() {
    let mut buf = [0u8; 3];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[1u8, 2, 3]);
    assert_eq!(enc.into_written(), &[1u8, 2, 3]);
}

#[test]
fn encoder_slice_into_written_is_backed_by_original_buffer() {
    let mut buf = [0u8; 4];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[11u8, 22]);
    let written = enc.into_written();
    assert_eq!(written, &[11u8, 22]);
    written[0] = 99;
    assert_eq!(buf[0], 99);
}

#[test]
fn encoder_slice_deref_shows_written() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[5u8, 6, 7]);
    assert_eq!(&*enc, &[5u8, 6, 7]);
}

#[test]
fn encoder_slice_deref_empty_before_any_write() {
    let mut buf = [0u8; 8];
    let enc = EncoderSlice::new(&mut buf);
    assert_eq!(enc.len(), 0);
    assert_eq!(&*enc, &[] as &[u8]);
}

#[test]
fn encoder_slice_deref_mut_matches_deref() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[1u8, 2, 3]);
    assert_eq!(enc.len(), (&mut *enc).len());
    assert_eq!(&mut *enc, &mut [1u8, 2, 3]);
}

#[test]
fn encoder_slice_reset_restores_remaining() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[1u8, 2, 3, 4]);
    enc.reset();
    assert_eq!(enc.remaining_mut(), 8);
}

#[test]
fn encoder_slice_reset_clears_written() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[1u8, 2, 3]);
    enc.reset();
    assert!(enc.written().is_empty());
    assert!(enc.is_empty());
}

#[test]
fn encoder_slice_reset_allows_rewrite() {
    let mut buf = [0u8; 4];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[1u8, 2, 3, 4]);
    enc.reset();
    enc.put_slice(&[5u8, 6]);
    assert_eq!(enc.written(), &[5u8, 6]);
}

#[test]
fn encoder_slice_advance_mut_moves_cursor() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    unsafe { enc.advance_mut(3) };
    assert_eq!(enc.remaining_mut(), 5);
    assert_eq!(enc.written().len(), 3);
}

#[test]
fn encoder_slice_advance_mut_then_write() {
    let mut buf = [0u8; 8];
    let mut enc = EncoderSlice::new(&mut buf);
    unsafe { enc.advance_mut(2) };
    enc.put_u8(99);
    assert_eq!(enc.written().len(), 3);
    assert_eq!(enc.written()[2], 99);
}

#[test]
fn encoder_slice_chunk_mut_len_equals_remaining() {
    let mut buf = [0u8; 10];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_u8(0);
    assert_eq!(enc.chunk_mut().len(), 9);
}

#[test]
fn encoder_slice_chunk_mut_empty_when_full() {
    let mut buf = [0u8; 2];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_slice(&[1u8, 2]);
    assert_eq!(enc.chunk_mut().len(), 0);
}

#[test]
fn encoder_slice_remaining_mut_tracks_writes() {
    let mut buf = [0u8; 10];
    let mut enc = EncoderSlice::new(&mut buf);
    assert_eq!(enc.remaining_mut(), 10);
    enc.put_u8(0);
    assert_eq!(enc.remaining_mut(), 9);
    enc.put_slice(&[0u8; 3]);
    assert_eq!(enc.remaining_mut(), 6);
    enc.put_bytes(0, 6);
    assert_eq!(enc.remaining_mut(), 0);
}

#[test]
fn encoder_slice_partial_eq_both_empty() {
    let mut buf1 = [0u8; 8];
    let mut buf2 = [0u8; 8];
    assert_eq!(EncoderSlice::new(&mut buf1), EncoderSlice::new(&mut buf2));
}

#[test]
fn encoder_slice_partial_eq_same_written() {
    let mut buf1 = [0u8; 8];
    let mut buf2 = [0u8; 8];
    let mut enc1 = EncoderSlice::new(&mut buf1);
    let mut enc2 = EncoderSlice::new(&mut buf2);
    enc1.put_slice(&[1u8, 2, 3]);
    enc2.put_slice(&[1u8, 2, 3]);
    assert_eq!(enc1, enc2);
}

#[test]
fn encoder_slice_partial_eq_different_content() {
    let mut buf1 = [0u8; 8];
    let mut buf2 = [0u8; 8];
    let mut enc1 = EncoderSlice::new(&mut buf1);
    let mut enc2 = EncoderSlice::new(&mut buf2);
    enc1.put_slice(&[1u8, 2, 3]);
    enc2.put_slice(&[4u8, 5, 6]);
    assert_ne!(enc1, enc2);
}

#[test]
fn encoder_slice_partial_eq_different_lengths() {
    let mut buf1 = [0u8; 8];
    let mut buf2 = [0u8; 8];
    let mut enc1 = EncoderSlice::new(&mut buf1);
    let mut enc2 = EncoderSlice::new(&mut buf2);
    enc1.put_slice(&[1u8, 2, 3]);
    enc2.put_slice(&[1u8, 2]);
    assert_ne!(enc1, enc2);
}

#[test]
fn encoder_slice_debug_empty_slice() {
    let mut buf = [0u8; 4];
    let enc = EncoderSlice::new(&mut buf);
    assert_eq!(alloc::format!("{:?}", enc), "[]");
}

#[test]
fn encoder_slice_debug_contains_written_bytes() {
    let mut buf = [0u8; 4];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_u8(99);
    assert!(alloc::format!("{:?}", enc).contains("99"));
}

#[test]
fn encoder_slice_single_byte_buffer() {
    let mut buf = [0u8; 1];
    let mut enc = EncoderSlice::new(&mut buf);
    enc.put_u8(42);
    assert_eq!(enc.remaining_mut(), 0);
    assert_eq!(enc.written(), &[42u8]);
}

#[test]
fn encoder_slice_const_new() {
    const fn make(buf: &mut [u8]) -> EncoderSlice<'_> {
        EncoderSlice::new(buf)
    }
    let mut buf = [0u8; 4];
    let enc = make(&mut buf);
    assert_eq!(enc.remaining_mut(), 4);
}
