use super::utils::{adjust_padding, align_counter, DbusError, Result};

#[derive(Debug)]
/// Indicates the endian of message
pub enum Endian {
    Little,
    Big, // we do not support this unless explicitly requested in youki's issues
}

impl Endian {
    fn to_byte(&self) -> u8 {
        match self {
            Self::Big => b'b',
            Self::Little => b'l',
        }
    }
    fn from_byte(byte: u8) -> Self {
        match byte {
            b'l' => Self::Little,
            b'b' => Self::Big,
            _ => panic!("invalid endian {}", byte),
        }
    }
}

/// Represents the type of header data
// there are others, but these are the only once we need
#[derive(Debug, PartialEq, Eq)]
pub enum HeaderSignature {
    Object,
    U32,
    String,
    Signature,
}

impl HeaderSignature {
    fn to_byte(&self) -> u8 {
        match self {
            Self::Object => b'o',
            Self::Signature => b'g',
            Self::String => b's',
            Self::U32 => b'u',
        }
    }
    fn from_byte(byte: u8) -> Self {
        match byte {
            b'o' => Self::Object,
            b'g' => Self::Signature,
            b's' => Self::String,
            b'u' => Self::U32,
            _ => panic!("unexpected signature {}", byte),
        }
    }
}

/// Type of message
#[derive(Debug, PartialEq, Eq)]
pub enum MessageType {
    MethodCall,
    MethodReturn,
    Error,
    Signal, // we will ignore this for all intents and purposes
}

/// Represents the kind of header
#[derive(Debug, PartialEq, Eq)]
pub enum HeaderKind {
    Path,
    Interface,
    Member,
    ErrorName,
    ReplySerial,
    Destination,
    Sender,
    BodySignature,
    UnixFd, // we will not use this, just for the sake of completion
}

impl HeaderKind {
    fn signature(&self) -> HeaderSignature {
        match &self {
            Self::Path => HeaderSignature::Object,
            Self::ReplySerial => HeaderSignature::U32,
            Self::BodySignature => HeaderSignature::Signature, // this is also encoded as string, but we need special handling for how its length is encoded
            Self::UnixFd => HeaderSignature::U32,
            _ => HeaderSignature::String, // rest all are encoded as string
        }
    }
}

// This is separated from header kind, because I wanted
// HeaderKind to be u8 like directly comparable, passable thing
#[derive(Debug, PartialEq, Eq)]
pub enum HeaderValue {
    String(String),
    U32(u32),
}

impl HeaderValue {
    fn as_bytes(&self) -> Vec<u8> {
        match self {
            Self::String(s) => {
                let mut t: Vec<u8> = s.as_bytes().into();
                t.push(0); // null byte terminator
                t
            }
            Self::U32(v) => v.to_le_bytes().into(),
        }
    }

    fn len(&self) -> usize {
        match self {
            Self::String(s) => s.len(), // we don't consider terminating null byte in length
            Self::U32(_) => 4,          // u32 is encoded as 4 bytes
        }
    }
}

#[derive(Debug, PartialEq, Eq)]
pub struct Header {
    pub kind: HeaderKind,
    pub value: HeaderValue,
}

impl Header {
    /// Parses a single header from given u8 vec,
    /// assuming the header to start from given counter point
    fn parse(buf: &[u8], ctr: &mut usize) -> Result<Self> {
        let header_kind = match buf[*ctr] {
            1 => HeaderKind::Path,
            2 => HeaderKind::Interface,
            3 => HeaderKind::Member,
            4 => HeaderKind::ErrorName,
            5 => HeaderKind::ReplySerial,
            6 => HeaderKind::Destination,
            7 => HeaderKind::Sender,
            8 => HeaderKind::BodySignature,
            9 => HeaderKind::UnixFd,
            v => {
                // should not occur unless we mess up parsing somewhere else
                return Err(DbusError::DeserializationError(format!(
                    "found invalid header kind value : {}",
                    v
                ))
                .into());
            }
        };

        // account for the header_kind byte
        *ctr += 1;

        // length of signature is always < 255, i.e. stored in 1 byte
        let signature_length = buf[*ctr] as usize;
        *ctr += 1;

        // we only support string, u32 signature and object,
        // all of which have signature length of 1 byte
        if signature_length != 1 {
            return Err(DbusError::IncompleteImplementation(
                "complex header type not supported".into(),
            )
            .into());
        }

        let actual_signature = HeaderSignature::from_byte(buf[*ctr]);

        // we can simply += 1, but I think this is more sensible
        *ctr += signature_length;

        let expected_signature = header_kind.signature();

        if actual_signature != expected_signature {
            return Err(DbusError::DeserializationError(format!(
                "header signature mismatch, expected {:?}, found {:?}",
                expected_signature, actual_signature
            ))
            .into());
        }

        *ctr += 1; // accounting for extra null byte that is always there

        let value = match expected_signature {
            HeaderSignature::U32 => {
                if buf.len() < *ctr + 4 {
                    return Err(DbusError::DeserializationError(
                        "incomplete response : partial header value".into(),
                    )
                    .into());
                }
                let ret = HeaderValue::U32(u32::from_le_bytes(
                    buf[*ctr..*ctr + 4].try_into().unwrap(), // we can unwrap here as we know 4 byte buffer will satisfy [u8;4]
                ));
                *ctr += 4;
                ret
            }
            // both are encoded as string
            HeaderSignature::Object | HeaderSignature::String => {
                if buf.len() < *ctr + 4 {
                    return Err(DbusError::DeserializationError(
                        "incomplete response : partial header value length".into(),
                    )
                    .into());
                }
                let len = u32::from_le_bytes(buf[*ctr..*ctr + 4].try_into().unwrap()) as usize;
                *ctr += 4;
                if buf.len() < *ctr + len {
                    return Err(DbusError::DeserializationError(
                        "incomplete response : partial header value".into(),
                    )
                    .into());
                }
                let string = String::from_utf8(buf[*ctr..*ctr + len].into()).unwrap();
                *ctr += len + 1; // +1 to account for null
                HeaderValue::String(string)
            }
            // only difference here is that length is 1 byte, not 4 bytes
            HeaderSignature::Signature => {
                let len = buf[*ctr] as usize;
                *ctr += 1;
                if buf.len() < *ctr + len {
                    return Err(DbusError::DeserializationError(
                        "incomplete response : partial header value".into(),
                    )
                    .into());
                }
                let signature = String::from_utf8(buf[*ctr..*ctr + len].into()).unwrap();
                *ctr += len + 1; //+1 to account for null byte
                HeaderValue::String(signature)
            }
        };
        Ok(Self {
            kind: header_kind,
            value,
        })
    }
}

/// Message preamble of initial 4 bytes
#[derive(Debug)]
pub struct Preamble {
    endian: Endian,
    pub mtype: MessageType,
    flags: u8,
    version: u8,
}

impl Preamble {
    fn new(mtype: MessageType) -> Self {
        Self {
            endian: Endian::Little, // we don't support big endian until requested
            mtype,
            flags: 0,   // until we need some flags to be used, this is fixed
            version: 1, // this is fixed until dbus releases a new major version
        }
    }
}

/// Represents a complete message transported over dbus connection
#[derive(Debug)]
pub struct Message {
    /// Initial 4 byte preamble needed for all messages
    pub preamble: Preamble,
    /// Serial ID of message
    pub serial: u32,
    // Message headers
    pub headers: Vec<Header>,
    /// Actual body, serialized
    pub body: Vec<u8>,
}

impl Message {
    /// create a new message structure
    pub fn new(mtype: MessageType, serial: u32, headers: Vec<Header>, body: Vec<u8>) -> Self {
        let preamble = Preamble::new(mtype);
        Self {
            preamble,
            serial,
            headers,
            body,
        }
    }
}

// NOTE that this does not add padding after last header, because we need
// non-padded header length
// the 8-byte alignment must be done separately after this
fn serialize_headers(headers: &[Header]) -> Vec<u8> {
    let mut ret = vec![];

    for header in headers {
        // all headers are always 8 byte aligned
        adjust_padding(&mut ret, 8);

        let header_kind: u8 = match &header.kind {
            HeaderKind::Path => 1,
            HeaderKind::Interface => 2,
            HeaderKind::Member => 3,
            HeaderKind::ErrorName => 4,
            HeaderKind::ReplySerial => 5,
            HeaderKind::Destination => 6,
            HeaderKind::Sender => 7,
            HeaderKind::BodySignature => 8,
            HeaderKind::UnixFd => 9,
        };

        let header_signature: u8 = header.kind.signature().to_byte();

        let signature_length = 1; // signature length is always 1 byte, and for all our headers, it is going to be 1

        // header preamble
        ret.extend_from_slice(&[header_kind, signature_length, header_signature, 0]);

        let header_value_length = header.value.len() as u32;

        // add header value length
        match &header.kind {
            HeaderKind::BodySignature => {
                // signature length is always 1 byte
                ret.push(header_value_length as u8);
            }
            HeaderKind::ReplySerial | HeaderKind::UnixFd => {
                /* do nothing as u32 does not need length appended*/
            }
            _ => {
                ret.extend_from_slice(&header_value_length.to_le_bytes());
            }
        }

        ret.extend_from_slice(&header.value.as_bytes());
    }

    ret
}

/// deserializes multiple headers from given array
fn deserialize_headers(buf: &[u8]) -> Result<Vec<Header>> {
    let mut ret = Vec::new();

    let mut ctr = 0;
    // headers are always aligned at 8 byte boundary
    align_counter(&mut ctr, 8);
    while ctr < buf.len() {
        let header = Header::parse(buf, &mut ctr)?;
        align_counter(&mut ctr, 8);
        ret.push(header);
    }
    Ok(ret)
}

impl Message {
    /// Serialize the given message into u8 vec
    pub fn serialize(mut self) -> Vec<u8> {
        let mtype = match self.preamble.mtype {
            MessageType::MethodCall => 1,
            MessageType::MethodReturn => 2,
            MessageType::Error => 3,
            MessageType::Signal => 4,
        };

        // preamble
        // Endian, message type, flags, dbus spec version
        let mut message: Vec<u8> = vec![
            self.preamble.endian.to_byte(),
            mtype,
            self.preamble.flags,
            self.preamble.version,
        ];

        // set body length
        message.extend_from_slice(&(self.body.len() as u32).to_le_bytes());

        // set id
        message.extend_from_slice(&self.serial.to_le_bytes());

        let serialized_headers = serialize_headers(&self.headers);

        // header length -  to be calculated without padding
        message.extend_from_slice(&(serialized_headers.len() as u32).to_le_bytes());
        // actual headers
        message.extend_from_slice(&serialized_headers);

        // padding to 8 byte boundary
        adjust_padding(&mut message, 8);

        // body
        message.append(&mut self.body);

        // no padding after body

        message
    }

    /// deserialize a single message from given buffer, assumed to start from given counter value
    pub fn deserialize(buf: &[u8], counter: &mut usize) -> Result<Self> {
        let endian = Endian::from_byte(buf[*counter]);

        if !matches!(endian, Endian::Little) {
            return Err(
                DbusError::IncompleteImplementation("big endian not supported".into()).into(),
            );
        }

        let mtype = match buf[*counter + 1] {
            1 => MessageType::MethodCall,
            2 => MessageType::MethodReturn,
            3 => MessageType::Error,
            4 => MessageType::Signal,
            v => {
                return Err(
                    DbusError::DeserializationError(format!("invalid message type {}", v)).into(),
                );
            }
        };

        let _flags = buf[*counter + 2]; // we basically ignore flags
        let version = buf[*counter + 3];

        if version != 1 {
            return Err(DbusError::IncompleteImplementation(
                "only dbus protocol v1 is supported".into(),
            )
            .into());
        }

        *counter += 4; // account for preamble bytes

        let preamble = Preamble::new(mtype);

        if buf.len() < *counter + 4 {
            return Err(DbusError::DeserializationError(
                "incomplete response : partial body length".into(),
            )
            .into());
        }
        let body_length =
            u32::from_le_bytes(buf[*counter..*counter + 4].try_into().unwrap()) as usize;
        *counter += 4;

        if buf.len() < *counter + 4 {
            return Err(DbusError::DeserializationError(
                "incomplete response : partial header serial".into(),
            )
            .into());
        }

        let serial = u32::from_le_bytes(buf[*counter..*counter + 4].try_into().unwrap());
        *counter += 4;

        if buf.len() < *counter + 4 {
            return Err(DbusError::DeserializationError(
                "incomplete response : partial header header array length".into(),
            )
            .into());
        }
        let header_array_length =
            u32::from_le_bytes(buf[*counter..*counter + 4].try_into().unwrap()) as usize;
        *counter += 4;

        if buf.len() < *counter + header_array_length {
            return Err(DbusError::DeserializationError(
                "incomplete response : partial header array".into(),
            )
            .into());
        }
        let headers = deserialize_headers(&buf[*counter..*counter + header_array_length])?;
        *counter += header_array_length;
        align_counter(counter, 8);

        if buf.len() < *counter + body_length {
            return Err(DbusError::DeserializationError(
                "incomplete response : partial body value".into(),
            )
            .into());
        }

        // we do not deserialize body here, and instead let the caller do it as needed
        // that way we don't have do deal with checking if the message sent id error or validating the body signature etc
        let body = Vec::from(&buf[*counter..*counter + body_length]);
        *counter += body_length;

        Ok(Self {
            preamble,
            serial,
            headers,
            body,
        })
    }
}

#[cfg(test)]
mod tests {
    use crate::systemd::dbus_native::serialize::{Structure, Variant};

    use super::super::serialize::DbusSerialize;
    use super::{Header, HeaderKind, HeaderValue, MessageType};
    use super::{Message, Result};
    // The hardcoded serialized values are captured from
    // original dbus library communication
    // and manually decoded.
    // see https://github.com/YJDoc2/dbus_native/tree/2d0dbc78d067c508ccc96343673b122f0a0cb48a/raw-decoded
    #[test]
    fn test_method_call_deserialize() -> Result<()> {
        let serialized = b"l\x01\x00\x019\x00\x00\x00\x02\x00\x00\x00\xa0\x00\x00\x00\x01\x01o\x00\x19\x00\x00\x00/org/freedesktop/systemd1\x00\x00\x00\x00\x00\x00\x00\x03\x01s\x00\x03\x00\x00\x00Get\x00\x00\x00\x00\x00\x07\x01s\x00\x07\x00\x00\x00:1.1309\x00\x06\x01s\x00\x18\x00\x00\x00org.freedesktop.systemd1\x00\x00\x00\x00\x00\x00\x00\x00\x02\x01s\x00\x1f\x00\x00\x00org.freedesktop.DBus.Properties\x00\x08\x01g\x00\x02ss\x00 \x00\x00\x00org.freedesktop.systemd1.Manager\x00\x00\x00\x00\x0c\x00\x00\x00ControlGroup\x00";

        let mut counter = 0;

        let res = Message::deserialize(serialized, &mut counter)?;
        assert_eq!(res.preamble.mtype, MessageType::MethodCall);

        let expected_headers = vec![
            Header {
                kind: HeaderKind::Path,
                value: HeaderValue::String("/org/freedesktop/systemd1".into()),
            },
            Header {
                kind: HeaderKind::Member,
                value: HeaderValue::String("Get".into()),
            },
            Header {
                kind: HeaderKind::Sender,
                value: HeaderValue::String(":1.1309".into()),
            },
            Header {
                kind: HeaderKind::Destination,
                value: HeaderValue::String("org.freedesktop.systemd1".into()),
            },
            Header {
                kind: HeaderKind::Interface,
                value: HeaderValue::String("org.freedesktop.DBus.Properties".into()),
            },
            Header {
                kind: HeaderKind::BodySignature,
                value: HeaderValue::String("ss".into()),
            },
        ];
        assert_eq!(res.headers, expected_headers);

        let mut counter = 0;
        let body = <(String, String)>::deserialize(&res.body, &mut counter)?;
        assert_eq!(body.0, "org.freedesktop.systemd1.Manager");
        assert_eq!(body.1, "ControlGroup");

        let mut body = vec![];
        (
            "org.freedesktop.systemd1.Manager".to_string(),
            "ControlGroup".to_string(),
        )
            .serialize(&mut body);

        let msg = Message::new(MessageType::MethodCall, 2, expected_headers, body);
        let actual_serialized = msg.serialize();

        assert_eq!(
            Vec::from_iter(serialized.iter().copied()),
            actual_serialized
        );

        Ok(())
    }

    #[test]
    fn test_method_reply_deserialize() -> Result<()> {
        let serialized = b"l\x02\x00\x01\x0c\x00\x00\x00\xff\xff\xff\xff?\x00\x00\x00\x05\x01u\x00\x01\x00\x00\x00\x07\x01s\x00\x14\x00\x00\x00org.freedesktop.DBus\x00\x00\x00\x00\x06\x01s\x00\x07\x00\x00\x00:1.2072\x00\x08\x01g\x00\x01s\x00\x00\x07\x00\x00\x00:1.2072\x00";

        let mut counter = 0;

        let res = Message::deserialize(serialized, &mut counter)?;
        assert_eq!(res.preamble.mtype, MessageType::MethodReturn);

        let expected_headers = vec![
            Header {
                kind: HeaderKind::ReplySerial,
                value: HeaderValue::U32(1),
            },
            Header {
                kind: HeaderKind::Sender,
                value: HeaderValue::String("org.freedesktop.DBus".into()),
            },
            Header {
                kind: HeaderKind::Destination,
                value: HeaderValue::String(":1.2072".into()),
            },
            Header {
                kind: HeaderKind::BodySignature,
                value: HeaderValue::String("s".into()),
            },
        ];
        assert_eq!(res.headers, expected_headers);

        let mut counter = 0;
        let body = String::deserialize(&res.body, &mut counter)?;
        assert_eq!(body, ":1.2072");

        let mut body = vec![];
        String::from(":1.2072").serialize(&mut body);

        let msg = Message::new(MessageType::MethodReturn, u32::MAX, expected_headers, body);
        let actual_serialized = msg.serialize();

        assert_eq!(
            Vec::from_iter(serialized.iter().copied()),
            actual_serialized
        );

        Ok(())
    }

    // we don't support signals, but just checking if serialize-deserialize works
    #[test]
    fn test_signal_deserialize() -> Result<()> {
        let serialized = b"l\x04\x00\x01\x0c\x00\x00\x00\xff\xff\xff\xff\x8f\x00\x00\x00\x07\x01s\x00\x14\x00\x00\x00org.freedesktop.DBus\x00\x00\x00\x00\x06\x01s\x00\x07\x00\x00\x00:1.2072\x00\x01\x01o\x00\x15\x00\x00\x00/org/freedesktop/DBus\x00\x00\x00\x02\x01s\x00\x14\x00\x00\x00org.freedesktop.DBus\x00\x00\x00\x00\x03\x01s\x00\x0c\x00\x00\x00NameAcquired\x00\x00\x00\x00\x08\x01g\x00\x01s\x00\x00\x07\x00\x00\x00:1.2072\x00";

        let mut counter = 0;

        let res = Message::deserialize(serialized, &mut counter)?;
        assert_eq!(res.preamble.mtype, MessageType::Signal);

        let expected_headers = vec![
            Header {
                kind: HeaderKind::Sender,
                value: HeaderValue::String("org.freedesktop.DBus".into()),
            },
            Header {
                kind: HeaderKind::Destination,
                value: HeaderValue::String(":1.2072".into()),
            },
            Header {
                kind: HeaderKind::Path,
                value: HeaderValue::String("/org/freedesktop/DBus".into()),
            },
            Header {
                kind: HeaderKind::Interface,
                value: HeaderValue::String("org.freedesktop.DBus".into()),
            },
            Header {
                kind: HeaderKind::Member,
                value: HeaderValue::String("NameAcquired".into()),
            },
            Header {
                kind: HeaderKind::BodySignature,
                value: HeaderValue::String("s".into()),
            },
        ];
        assert_eq!(res.headers, expected_headers);

        let mut counter = 0;
        let body = String::deserialize(&res.body, &mut counter)?;
        assert_eq!(body, ":1.2072");

        let mut body = vec![];
        String::from(":1.2072").serialize(&mut body);

        let msg = Message::new(MessageType::Signal, u32::MAX, expected_headers, body);
        let actual_serialized = msg.serialize();

        assert_eq!(
            Vec::from_iter(serialized.iter().copied()),
            actual_serialized
        );

        Ok(())
    }

    #[test]
    fn test_no_body_deserialize() -> Result<()> {
        let serialized = b"l\x01\x00\x01\x00\x00\x00\x00\x01\x00\x00\x00n\x00\x00\x00\x01\x01o\x00\x15\x00\x00\x00/org/freedesktop/DBus\x00\x00\x00\x06\x01s\x00\x14\x00\x00\x00org.freedesktop.DBus\x00\x00\x00\x00\x02\x01s\x00\x14\x00\x00\x00org.freedesktop.DBus\x00\x00\x00\x00\x03\x01s\x00\x05\x00\x00\x00Hello\x00\x00\x00";

        let mut counter = 0;

        let res = Message::deserialize(serialized, &mut counter)?;
        assert_eq!(res.preamble.mtype, MessageType::MethodCall);

        let expected_headers = vec![
            Header {
                kind: HeaderKind::Path,
                value: HeaderValue::String("/org/freedesktop/DBus".into()),
            },
            Header {
                kind: HeaderKind::Destination,
                value: HeaderValue::String("org.freedesktop.DBus".into()),
            },
            Header {
                kind: HeaderKind::Interface,
                value: HeaderValue::String("org.freedesktop.DBus".into()),
            },
            Header {
                kind: HeaderKind::Member,
                value: HeaderValue::String("Hello".into()),
            },
        ];
        assert_eq!(res.headers, expected_headers);

        assert!(res.body.is_empty());

        let msg = Message::new(MessageType::MethodCall, 1, expected_headers, vec![]);
        let actual_serialized = msg.serialize();

        assert_eq!(
            Vec::from_iter(serialized.iter().copied()),
            actual_serialized
        );

        Ok(())
    }

    #[test]
    fn test_error_message_deserialize() -> Result<()> {
        let serialized = b"l\x03\x00\x01\x16\x00\x00\x00\xff\xff\xff\xffw\x00\x00\x00\x05\x01u\x00\x03\x00\x00\x00\x07\x01s\x00\x14\x00\x00\x00org.freedesktop.DBus\x00\x00\x00\x00\x04\x01s\x00+\x00\x00\x00org.freedesktop.DBus.Error.UnknownInterface\x00\x00\x00\x00\x00\x08\x01g\x00\x01s\x00\x00\x06\x01s\x00\x06\x00\x00\x00:1.868\x00\x00\x11\x00\x00\x00Invalid interface\x00";

        let mut counter = 0;

        let res = Message::deserialize(serialized, &mut counter)?;
        assert_eq!(res.preamble.mtype, MessageType::Error);

        let expected_headers = vec![
            Header {
                kind: HeaderKind::ReplySerial,
                value: HeaderValue::U32(3),
            },
            Header {
                kind: HeaderKind::Sender,
                value: HeaderValue::String("org.freedesktop.DBus".into()),
            },
            Header {
                kind: HeaderKind::ErrorName,
                value: HeaderValue::String("org.freedesktop.DBus.Error.UnknownInterface".into()),
            },
            Header {
                kind: HeaderKind::BodySignature,
                value: HeaderValue::String("s".into()),
            },
            Header {
                kind: HeaderKind::Destination,
                value: HeaderValue::String(":1.868".into()),
            },
        ];
        assert_eq!(res.headers, expected_headers);

        let mut counter = 0;
        let body = String::deserialize(&res.body, &mut counter)?;
        assert_eq!(body, "Invalid interface");

        let mut body = vec![];
        String::from("Invalid interface").serialize(&mut body);

        let msg = Message::new(MessageType::Error, u32::MAX, expected_headers, body);
        let actual_serialized = msg.serialize();

        assert_eq!(
            Vec::from_iter(serialized.iter().copied()),
            actual_serialized
        );

        Ok(())
    }

    #[test]
    fn test_vector_payload_deserialize() -> Result<()> {
        let serialized = b"l\x01\x00\x01\xc8\x01\x00\x00\x03\x00\x00\x00\xc6\x00\x00\x00\x01\x01o\x00\x19\x00\x00\x00\x2forg\x2ffreedesktop\x2fsystemd1\x00\x00\x00\x00\x00\x00\x00\x03\x01s\x00\x12\x00\x00\x00StartTransientUnit\x00\x00\x00\x00\x00\x00\x07\x01s\x00\x07\x00\x00\x00\x3a1\x2e1021\x00\x06\x01s\x00\x18\x00\x00\x00org\x2efreedesktop\x2esystemd1\x00\x00\x00\x00\x00\x00\x00\x00\x02\x01s\x00\x20\x00\x00\x00org\x2efreedesktop\x2esystemd1\x2eManager\x00\x00\x00\x00\x00\x00\x00\x00\x08\x01g\x00\x10ssa\x28sv\x29a\x28sa\x28sv\x29\x29\x00\x00\x00M\x00\x00\x00libpod\x2d57f5869eaf80cee986095eebd1e0fbbbd148527f67d94f1fbe958f5bab8112f7\x2escope\x00\x00\x00\x07\x00\x00\x00replace\x00X\x01\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00Description\x00\x01s\x00\x00P\x00\x00\x00youki\x20container\x2057f5869eaf80cee986095eebd1e0fbbbd148527f67d94f1fbe958f5bab8112f7\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00Slice\x00\x01s\x00\x00\x00\x00\x0a\x00\x00\x00user\x2eslice\x00\x00\x08\x00\x00\x00Delegate\x00\x01b\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00MemoryAccounting\x00\x01b\x00\x01\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00CPUAccounting\x00\x01b\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00IOAccounting\x00\x01b\x00\x01\x00\x00\x00\x0f\x00\x00\x00TasksAccounting\x00\x01b\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x13\x00\x00\x00DefaultDependencies\x00\x01b\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00PIDs\x00\x02au\x00\x00\x00\x00\x04\x00\x00\x007g\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";

        let mut counter = 0;

        let res = Message::deserialize(serialized, &mut counter)?;
        assert_eq!(res.preamble.mtype, MessageType::MethodCall);

        let expected_headers = vec![
            Header {
                kind: HeaderKind::Path,
                value: HeaderValue::String("/org/freedesktop/systemd1".into()),
            },
            Header {
                kind: HeaderKind::Member,
                value: HeaderValue::String("StartTransientUnit".into()),
            },
            Header {
                kind: HeaderKind::Sender,
                value: HeaderValue::String(":1.1021".into()),
            },
            Header {
                kind: HeaderKind::Destination,
                value: HeaderValue::String("org.freedesktop.systemd1".into()),
            },
            Header {
                kind: HeaderKind::Interface,
                value: HeaderValue::String("org.freedesktop.systemd1.Manager".into()),
            },
            Header {
                kind: HeaderKind::BodySignature,
                value: HeaderValue::String("ssa(sv)a(sa(sv))".into()),
            },
        ];
        assert_eq!(res.headers, expected_headers);

        let mut counter = 0;

        let (name, mode, props, aux) = <(
            String,
            String,
            Vec<Structure<Variant>>,
            Vec<Structure<Vec<Structure<Variant>>>>,
        )>::deserialize(&res.body, &mut counter)?;

        let expected_name =
            "libpod-57f5869eaf80cee986095eebd1e0fbbbd148527f67d94f1fbe958f5bab8112f7.scope"
                .to_string();

        assert_eq!(name, expected_name);

        let expected_mode = "replace".to_string();
        assert_eq!(mode, expected_mode);

        let expected_aux = vec![];
        assert_eq!(aux, expected_aux);

        let expected_props = vec![Structure::new(
            "Description".into(),
            Variant::String(
                "youki container 57f5869eaf80cee986095eebd1e0fbbbd148527f67d94f1fbe958f5bab8112f7"
                    .into(),
            ),
        ),
        Structure::new("Slice".into(), Variant::String("user.slice".into())),
        Structure::new("Delegate".into(), Variant::Bool(true)),
        Structure::new("MemoryAccounting".into(), Variant::Bool(true)),
        Structure::new("CPUAccounting".into(), Variant::Bool(true)),
        Structure::new("IOAccounting".into(), Variant::Bool(true)),
        Structure::new("TasksAccounting".into(), Variant::Bool(true)),
        Structure::new("DefaultDependencies".into(), Variant::Bool(false)),
        Structure::new("PIDs".into(),Variant::ArrayU32(vec![26423]))
        ];

        assert_eq!(props, expected_props);

        let mut body = vec![];
        (expected_name, expected_mode, expected_props, expected_aux).serialize(&mut body);

        let msg = Message::new(MessageType::MethodCall, 3, expected_headers, body);
        let actual_serialized = msg.serialize();

        assert_eq!(
            Vec::from_iter(serialized.iter().copied()),
            actual_serialized
        );

        Ok(())
    }
}
