use std::{borrow::Cow, convert::TryFrom, io};
use crate::{
    io::ParseBuf,
    misc::raw::{bytes::LenEnc, Const, RawBytes, RawInt},
    proto::MyDeserialize,
};
use super::jsonb;
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u8)]
pub enum JsonDiffOperation {
    REPLACE,
    INSERT,
    REMOVE,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("Unknown JsonDiff operation {}", _0)]
#[repr(transparent)]
pub struct UnknownJsonDiffOperation(pub u8);
impl From<UnknownJsonDiffOperation> for u8 {
    fn from(x: UnknownJsonDiffOperation) -> Self {
        x.0
    }
}
impl TryFrom<u8> for JsonDiffOperation {
    type Error = UnknownJsonDiffOperation;
    fn try_from(value: u8) -> Result<Self, Self::Error> {
        match value {
            0 => Ok(Self::REPLACE),
            1 => Ok(Self::INSERT),
            2 => Ok(Self::REMOVE),
            x => Err(UnknownJsonDiffOperation(x)),
        }
    }
}
#[derive(Debug, Clone, PartialEq)]
pub struct JsonDiff<'a> {
    path: RawBytes<'a, LenEnc>,
    operation: Const<JsonDiffOperation, u8>,
    value: Option<jsonb::Value<'a>>,
}
impl<'a> JsonDiff<'a> {
    pub fn path(&'a self) -> &'a [u8] {
        self.path.as_bytes()
    }
    pub fn path_str(&'a self) -> Cow<'a, str> {
        self.path.as_str()
    }
    pub fn operation(&self) -> JsonDiffOperation {
        *self.operation
    }
    pub fn value(&'a self) -> Option<&'a jsonb::Value<'a>> {
        self.value.as_ref()
    }
    pub fn into_owned(self) -> JsonDiff<'static> {
        JsonDiff {
            path: self.path.into_owned(),
            operation: self.operation,
            value: self.value.map(|x| x.into_owned()),
        }
    }
}
impl<'de> MyDeserialize<'de> for JsonDiff<'de> {
    const SIZE: Option<usize> = None;
    type Ctx = ();
    fn deserialize((): Self::Ctx, buf: &mut ParseBuf<'de>) -> io::Result<Self> {
        let operation: Const<JsonDiffOperation, u8> = buf.parse(())?;
        let path = buf.parse(())?;
        let value = if *operation != JsonDiffOperation::REMOVE {
            let len: RawInt<LenEnc> = buf.parse(())?;
            let mut value: ParseBuf = buf.parse(*len as usize)?;
            Some(value.parse(())?)
        } else {
            None
        };
        Ok(Self {
            path,
            operation,
            value,
        })
    }
}