/* Code generated by Azure.Iot.Operations.ProtocolCompilerLib v0.10.0.0; DO NOT EDIT. */

use std::collections::HashMap;

use azure_iot_operations_mqtt::session::SessionManagedClient;
use azure_iot_operations_protocol::application::ApplicationContext;
use azure_iot_operations_protocol::common::aio_protocol_error::AIOProtocolError;
use azure_iot_operations_protocol::common::payload_serialize::PayloadSerialize;
use azure_iot_operations_protocol::rpc_command;
use iso8601_duration;

use super::super::common_types::options::CommandExecutorOptions;
use super::COMMAND_SERVICE_GROUP_ID;
use super::MODEL_ID;
use super::REQUEST_TOPIC_PATTERN;
use super::get_request_schema::GetRequestSchema;
use super::get_response_payload::GetResponsePayload;
use super::get_response_schema::GetResponseSchema;
use super::schema_registry_error::SchemaRegistryError;

pub type GetRequest = rpc_command::executor::Request<GetRequestSchema, GetResponseSchema>;
pub type GetResponse = rpc_command::executor::Response<GetResponseSchema>;
pub type GetResponseBuilderError = rpc_command::executor::ResponseBuilderError;

/// Builder for [`GetResponse`]
#[derive(Default)]
pub struct GetResponseBuilder {
    inner_builder: rpc_command::executor::ResponseBuilder<GetResponseSchema>,
}

impl GetResponseBuilder {
    /// Custom user data to set on the response
    pub fn custom_user_data(&mut self, custom_user_data: Vec<(String, String)>) -> &mut Self {
        self.inner_builder.custom_user_data(custom_user_data);
        self
    }

    /// Cloud event for the response
    pub fn cloud_event(
        &mut self,
        cloud_event: Option<rpc_command::executor::ResponseCloudEvent>,
    ) -> &mut Self {
        self.inner_builder.cloud_event(cloud_event);
        self
    }

    /// Payload of the response
    ///
    /// # Errors
    /// If the payload cannot be serialized
    pub fn payload(&mut self, payload: GetResponsePayload) -> Result<&mut Self, AIOProtocolError> {
        self.inner_builder.payload(GetResponseSchema {
            schema: Some(payload.schema),
            error: None,
        })?;
        Ok(self)
    }

    pub fn error(&mut self, error: SchemaRegistryError) -> Result<&mut Self, AIOProtocolError> {
        self.inner_builder.payload(GetResponseSchema {
            schema: None,
            error: Some(error),
        })?;
        Ok(self)
    }

    /// Builds a new `GetResponse`
    ///
    /// # Errors
    /// If a required field has not been initialized
    #[allow(clippy::missing_panics_doc)] // The panic is not possible
    pub fn build(&mut self) -> Result<GetResponse, GetResponseBuilderError> {
        self.inner_builder.build()
    }
}

/// Command Executor for `get`
pub struct GetCommandExecutor(rpc_command::Executor<GetRequestSchema, GetResponseSchema>);

impl GetCommandExecutor {
    /// Creates a new [`GetCommandExecutor`]
    ///
    /// # Panics
    /// If the DTDL that generated this code was invalid
    pub fn new(
        application_context: ApplicationContext,
        client: SessionManagedClient,
        options: &CommandExecutorOptions,
    ) -> Self {
        let mut executor_options_builder = rpc_command::executor::OptionsBuilder::default();
        if let Some(topic_namespace) = &options.topic_namespace {
            executor_options_builder.topic_namespace(topic_namespace.clone());
        }

        let mut topic_token_map: HashMap<String, String> = options
            .topic_token_map
            .clone()
            .into_iter()
            .map(|(k, v)| (format!("ex:{k}"), v))
            .collect();

        topic_token_map.insert("modelId".to_string(), MODEL_ID.to_string());
        topic_token_map.insert("executorId".to_string(), client.client_id().to_string());
        topic_token_map.insert("commandName".to_string(), "get".to_string());

        let executor_options = executor_options_builder
            .request_topic_pattern(REQUEST_TOPIC_PATTERN)
            .command_name("get")
            .is_idempotent(true)
            .topic_token_map(topic_token_map)
            .service_group_id(COMMAND_SERVICE_GROUP_ID.to_string())
            .build()
            .expect("DTDL schema generated invalid arguments");

        Self(
            rpc_command::Executor::new(application_context, client, executor_options)
                .expect("DTDL schema generated invalid arguments"),
        )
    }

    /// Receive the next [`GetRequest`] or [`None`] if there will be no more requests
    ///
    /// # Errors
    /// [`AIOProtocolError`] if there is a failure receiving a request
    pub async fn recv(&mut self) -> Option<Result<GetRequest, AIOProtocolError>> {
        self.0.recv().await
    }

    /// Shutdown the [`GetCommandExecutor`]. Unsubscribes from the request topic.
    ///
    /// Returns Ok(()) on success, otherwise returns [`AIOProtocolError`].
    /// # Errors
    /// [`AIOProtocolError`] of kind [`ClientError`](azure_iot_operations_protocol::common::aio_protocol_error::AIOProtocolErrorKind::ClientError) if the unsubscribe fails or if the unsuback reason code doesn't indicate success.
    pub async fn shutdown(&mut self) -> Result<(), AIOProtocolError> {
        self.0.shutdown().await
    }
}
