#[derive(Debug, Clone, PartialEq, Eq)]
pub enum PayloadError {
    LengthMismatch { expected: usize, actual: usize },
}

impl core::fmt::Display for PayloadError {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        match self {
            PayloadError::LengthMismatch { expected, actual } => {
                write!(f, "payload length mismatch: expected {}, got {}", expected, actual)
            }
        }
    }
}

impl std::error::Error for PayloadError {}

pub struct PayloadCursor<'a> {
    data: &'a [u8],
    offset: usize,
}

impl<'a> PayloadCursor<'a> {
    pub fn new(data: &'a [u8]) -> Self {
        Self { data, offset: 0 }
    }

    #[inline]
    fn take(&mut self, len: usize) -> &'a [u8] {
        debug_assert!(self.offset + len <= self.data.len());
        let start = self.offset;
        self.offset += len;
        &self.data[start..start + len]
    }

    pub fn read_u8(&mut self) -> u8 {
        let value = self.data[self.offset];
        self.offset += 1;
        value
    }

    pub fn read_bool(&mut self) -> bool {
        self.read_u8() != 0
    }

    pub fn read_i16(&mut self) -> i16 {
        let bytes: [u8; 2] = self.take(2).try_into().expect("slice length verified");
        i16::from_le_bytes(bytes)
    }

    pub fn read_i32(&mut self) -> i32 {
        let bytes: [u8; 4] = self.take(4).try_into().expect("slice length verified");
        i32::from_le_bytes(bytes)
    }

    pub fn read_i64(&mut self) -> i64 {
        let bytes: [u8; 8] = self.take(8).try_into().expect("slice length verified");
        i64::from_le_bytes(bytes)
    }

    pub fn read_u16(&mut self) -> u16 {
        let bytes: [u8; 2] = self.take(2).try_into().expect("slice length verified");
        u16::from_le_bytes(bytes)
    }

    pub fn read_u32(&mut self) -> u32 {
        let bytes: [u8; 4] = self.take(4).try_into().expect("slice length verified");
        u32::from_le_bytes(bytes)
    }

    pub fn read_u64(&mut self) -> u64 {
        let bytes: [u8; 8] = self.take(8).try_into().expect("slice length verified");
        u64::from_le_bytes(bytes)
    }

    pub fn read_f32(&mut self) -> f32 {
        let bytes: [u8; 4] = self.take(4).try_into().expect("slice length verified");
        f32::from_le_bytes(bytes)
    }

    pub fn read_f64(&mut self) -> f64 {
        let bytes: [u8; 8] = self.take(8).try_into().expect("slice length verified");
        f64::from_le_bytes(bytes)
    }
}

pub struct PayloadWriter<'a> {
    data: &'a mut [u8],
    offset: usize,
}

impl<'a> PayloadWriter<'a> {
    pub fn new(data: &'a mut [u8]) -> Self {
        Self { data, offset: 0 }
    }

    #[inline]
    fn put(&mut self, bytes: &[u8]) {
        debug_assert!(self.offset + bytes.len() <= self.data.len());
        let end = self.offset + bytes.len();
        self.data[self.offset..end].copy_from_slice(bytes);
        self.offset = end;
    }

    pub fn write_u8(&mut self, value: u8) {
        self.put(&[value]);
    }

    pub fn write_bool(&mut self, value: bool) {
        self.write_u8(value as u8);
    }

    pub fn write_i16(&mut self, value: i16) {
        self.put(&value.to_le_bytes());
    }

    pub fn write_i32(&mut self, value: i32) {
        self.put(&value.to_le_bytes());
    }

    pub fn write_i64(&mut self, value: i64) {
        self.put(&value.to_le_bytes());
    }

    pub fn write_u16(&mut self, value: u16) {
        self.put(&value.to_le_bytes());
    }

    pub fn write_u32(&mut self, value: u32) {
        self.put(&value.to_le_bytes());
    }

    pub fn write_u64(&mut self, value: u64) {
        self.put(&value.to_le_bytes());
    }

    pub fn write_f32(&mut self, value: f32) {
        self.put(&value.to_le_bytes());
    }

    pub fn write_f64(&mut self, value: f64) {
        self.put(&value.to_le_bytes());
    }
}
