//! Defines the functions performing serialization and deserialization of messages using bincode.
use nanoservices_utils::errors::{NanoServiceError, NanoServiceErrorStatus};
use tokio::io::AsyncReadExt;
use tokio::net::tcp::OwnedReadHalf;

use crate::messages::serialization::header::MessageHeader;
use crate::messages::server::wrapper::WrappedServerMessage;

/// Serializes a server message into bytes using bincode.
///
/// # Arguments
/// * `wrapped_message`: the message to be serialized
///
/// # Returns
/// a vector of bytes representing the serialized message
pub fn serialize_server_message(
	wrapped_message: &WrappedServerMessage,
) -> Result<Vec<u8>, NanoServiceError> {
	let serialized_message = bincode::serialize(wrapped_message).map_err(|e| {
		NanoServiceError::new(
			format!("Failed to serialize message metadata: {}", e),
			NanoServiceErrorStatus::BadRequest,
		)
	})?;
	let length = serialized_message.len();
	let mut message_header = MessageHeader {
		value: length,
	}
	.to_bytes();
	message_header.extend(serialized_message);
	Ok(message_header)
}

/// Deserializes a server message from a stream of bytes.
///
/// # Arguments
/// * `reader`: the stream of bytes to be deserialized
///
/// # Returns
/// the deserialized server message
pub async fn deserialize_from_stream(
	reader: &mut OwnedReadHalf,
) -> Result<WrappedServerMessage, NanoServiceError> {
	let mut header_bytes = [0u8; 8];
	reader.read_exact(&mut header_bytes).await.map_err(|e| {
		NanoServiceError::new(
			format!("Failed to read message header: {}", e),
			NanoServiceErrorStatus::BadRequest,
		)
	})?;
	let message_header = MessageHeader::from_bytes(&header_bytes);
	let mut message_bytes = vec![0u8; message_header.value];
	reader.read_exact(&mut message_bytes).await.map_err(|e| {
		NanoServiceError::new(
			format!("Failed to read message header: {}", e),
			NanoServiceErrorStatus::BadRequest,
		)
	})?;
	let wrapped_message: WrappedServerMessage = bincode::deserialize(&message_bytes).unwrap();
	Ok(wrapped_message)
}

#[cfg(test)]
mod tests {

	use crate::messages::server::interface::ServerMessage;

	use super::*;
	static CONNECTION_ID: &str = "1-1234567890";

	#[test]
	fn test_serialize_server_message() {
		let wrapped_message =
			WrappedServerMessage::new(2, ServerMessage::Ping(5), CONNECTION_ID.into());
		let serialized = serialize_server_message(&wrapped_message).unwrap();

		let deserialized: WrappedServerMessage = bincode::deserialize(&serialized[8..]).unwrap();
		assert_eq!(wrapped_message, deserialized);
	}
}
