use rapira::Rapira;
use strum::IntoStaticStr;

use crate::logdb::{ByteValue, KeyValue};

#[derive(Debug, Clone, IntoStaticStr)]
pub enum ChangeEvent {
    Upsert(KeyValue),
    /// delete by id
    Delete(ByteValue),
}
impl ChangeEvent {
    pub fn variant(&self) -> &'static str {
        self.into()
    }

    pub fn key(&self) -> &ByteValue {
        match self {
            ChangeEvent::Upsert(kv) => &kv.0,
            ChangeEvent::Delete(key) => key,
        }
    }

    pub fn from_kv(key: ByteValue, val: Option<ByteValue>) -> Self {
        match val {
            Some(val) => ChangeEvent::Upsert((key, val)),
            None => ChangeEvent::Delete(key),
        }
    }
}

impl Rapira for ChangeEvent {
    const MIN_SIZE: usize = 5;

    fn size(&self) -> usize {
        1 + match self {
            ChangeEvent::Upsert((k, v)) => k.len() + v.len(),
            ChangeEvent::Delete(k) => k.len(),
        }
    }

    fn check_bytes(slice: &mut &[u8]) -> rapira::Result<()> {
        let var = rapira::byte_rapira::from_slice(slice)?;
        match var {
            0 => {
                rapira::bytes_rapira::check_bytes::<()>(Default::default(), slice)?;
                rapira::bytes_rapira::check_bytes::<()>(Default::default(), slice)?;
                Ok(())
            }
            1 => Ok(()),
            _ => Err(rapira::RapiraError::EnumVariant),
        }
    }

    fn from_slice(slice: &mut &[u8]) -> rapira::Result<Self>
    where
        Self: Sized,
    {
        let var = rapira::byte_rapira::from_slice(slice)?;
        match var {
            0 => {
                let key = rapira::bytes_rapira::from_slice(slice)?;
                let val = rapira::bytes_rapira::from_slice(slice)?;
                Ok(ChangeEvent::Upsert((key.into(), val.into())))
            }
            1 => {
                let key = rapira::bytes_rapira::from_slice(slice)?;

                Ok(ChangeEvent::Delete(key.into()))
            }
            _ => Err(rapira::RapiraError::EnumVariant),
        }
    }

    fn convert_to_bytes(&self, slice: &mut [u8], cursor: &mut usize) {
        match self {
            ChangeEvent::Upsert((k, v)) => {
                rapira::byte_rapira::convert_to_bytes(&0, slice, cursor);
                rapira::bytes_rapira::convert_to_bytes(k, slice, cursor);
                rapira::bytes_rapira::convert_to_bytes(v, slice, cursor);
            }
            ChangeEvent::Delete(k) => {
                rapira::byte_rapira::convert_to_bytes(&1, slice, cursor);
                rapira::bytes_rapira::convert_to_bytes(k, slice, cursor);
            }
        }
    }
}
