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

use std::collections::HashMap;
use std::error::Error;
use std::time::Duration;

use azure_iot_operations_mqtt::session::SessionManagedClient;
use azure_iot_operations_protocol::application::ApplicationContext;
use azure_iot_operations_protocol::common::aio_protocol_error::{
    AIOProtocolError, AIOProtocolErrorKind,
};
use azure_iot_operations_protocol::common::payload_serialize::PayloadSerialize;
use azure_iot_operations_protocol::rpc_command;

use super::super::common_types::options::CommandInvokerOptions;
use super::MODEL_ID;
use super::REQUEST_TOPIC_PATTERN;
use super::akri_service_error::AkriServiceError;
use super::set_notification_preference_for_device_updates_request_payload::SetNotificationPreferenceForDeviceUpdatesRequestPayload;
use super::set_notification_preference_for_device_updates_response_payload::SetNotificationPreferenceForDeviceUpdatesResponsePayload;
use super::set_notification_preference_for_device_updates_response_schema::SetNotificationPreferenceForDeviceUpdatesResponseSchema;

pub type SetNotificationPreferenceForDeviceUpdatesRequest =
    rpc_command::invoker::Request<SetNotificationPreferenceForDeviceUpdatesRequestPayload>;
pub type SetNotificationPreferenceForDeviceUpdatesResponse =
    rpc_command::invoker::Response<SetNotificationPreferenceForDeviceUpdatesResponsePayload>;
pub type SetNotificationPreferenceForDeviceUpdatesResponseError =
    rpc_command::invoker::Response<AkriServiceError>;
pub type SetNotificationPreferenceForDeviceUpdatesRequestBuilderError =
    rpc_command::invoker::RequestBuilderError;

#[derive(Default)]
/// Builder for [`SetNotificationPreferenceForDeviceUpdatesRequest`]
pub struct SetNotificationPreferenceForDeviceUpdatesRequestBuilder {
    inner_builder: rpc_command::invoker::RequestBuilder<
        SetNotificationPreferenceForDeviceUpdatesRequestPayload,
    >,
    topic_tokens: HashMap<String, String>,
}

impl SetNotificationPreferenceForDeviceUpdatesRequestBuilder {
    /// Custom user data to set on the request
    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 request
    pub fn cloud_event(
        &mut self,
        cloud_event: Option<rpc_command::invoker::RequestCloudEvent>,
    ) -> &mut Self {
        self.inner_builder.cloud_event(cloud_event);
        self
    }

    /// Topic token keys/values to be replaced into the publish topic of the request message.
    /// A prefix of "ex:" will be prepended to each key before scanning the topic pattern.
    /// Thus, only tokens of the form `{ex:SOMEKEY}` will be replaced.
    pub fn topic_tokens(&mut self, topic_tokens: HashMap<String, String>) -> &mut Self {
        for (k, v) in topic_tokens {
            self.topic_tokens.insert(format!("ex:{k}"), v);
        }
        self
    }

    /// Timeout for the request
    pub fn timeout(&mut self, timeout: Duration) -> &mut Self {
        self.inner_builder.timeout(timeout);
        self
    }

    /// Payload of the request
    ///
    /// # Errors
    /// If the payload cannot be serialized
    pub fn payload(
        &mut self,
        payload: SetNotificationPreferenceForDeviceUpdatesRequestPayload,
    ) -> Result<&mut Self, AIOProtocolError> {
        self.inner_builder.payload(payload)?;
        Ok(self)
    }

    /// Builds a new `SetNotificationPreferenceForDeviceUpdatesRequest`
    ///
    /// # 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<
        SetNotificationPreferenceForDeviceUpdatesRequest,
        SetNotificationPreferenceForDeviceUpdatesRequestBuilderError,
    > {
        self.inner_builder.topic_tokens(self.topic_tokens.clone());

        self.inner_builder.build()
    }
}

/// Command Invoker for `setNotificationPreferenceForDeviceUpdates`
pub struct SetNotificationPreferenceForDeviceUpdatesCommandInvoker(
    rpc_command::Invoker<
        SetNotificationPreferenceForDeviceUpdatesRequestPayload,
        SetNotificationPreferenceForDeviceUpdatesResponseSchema,
    >,
);

impl SetNotificationPreferenceForDeviceUpdatesCommandInvoker {
    /// Creates a new [`SetNotificationPreferenceForDeviceUpdatesCommandInvoker`]
    ///
    /// # Panics
    /// If the DTDL that generated this code was invalid
    pub fn new(
        application_context: ApplicationContext,
        client: SessionManagedClient,
        options: &CommandInvokerOptions,
    ) -> Self {
        let mut invoker_options_builder = rpc_command::invoker::OptionsBuilder::default();
        if let Some(topic_namespace) = &options.topic_namespace {
            invoker_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(
            "invokerClientId".to_string(),
            client.client_id().to_string(),
        );
        topic_token_map.insert(
            "commandName".to_string(),
            "setNotificationPreferenceForDeviceUpdates".to_string(),
        );

        let invoker_options = invoker_options_builder
            .request_topic_pattern(REQUEST_TOPIC_PATTERN)
            .command_name("setNotificationPreferenceForDeviceUpdates")
            .topic_token_map(topic_token_map)
            .response_topic_prefix(options.response_topic_prefix.clone())
            .response_topic_suffix(options.response_topic_suffix.clone())
            .build()
            .expect("DTDL schema generated invalid arguments");

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

    /// Invokes the [`SetNotificationPreferenceForDeviceUpdatesRequest`]
    ///
    /// # Errors
    /// [`AIOProtocolError`] if there is a failure invoking the request
    pub async fn invoke(
        &self,
        request: SetNotificationPreferenceForDeviceUpdatesRequest,
    ) -> Result<
        Result<
            SetNotificationPreferenceForDeviceUpdatesResponse,
            SetNotificationPreferenceForDeviceUpdatesResponseError,
        >,
        AIOProtocolError,
    > {
        let response = self.0.invoke(request).await;
        match response {
            Ok(response) => {
                if let Some(set_notification_preference_for_device_updates_error) = response
                    .payload
                    .set_notification_preference_for_device_updates_error
                {
                    Ok(Err(
                        SetNotificationPreferenceForDeviceUpdatesResponseError {
                            payload: set_notification_preference_for_device_updates_error,
                            content_type: response.content_type,
                            format_indicator: response.format_indicator,
                            custom_user_data: response.custom_user_data,
                            timestamp: response.timestamp,
                            executor_id: response.executor_id,
                        },
                    ))
                } else if let Some(response_payload) = response.payload.response_payload {
                    Ok(Ok(SetNotificationPreferenceForDeviceUpdatesResponse {
                        payload: SetNotificationPreferenceForDeviceUpdatesResponsePayload {
                            response_payload,
                        },
                        content_type: response.content_type,
                        format_indicator: response.format_indicator,
                        custom_user_data: response.custom_user_data,
                        timestamp: response.timestamp,
                        executor_id: response.executor_id,
                    }))
                } else {
                    Err(AIOProtocolError {
                        message: Some(
                            "Command response has neither normal nor error payload content"
                                .to_string(),
                        ),
                        kind: AIOProtocolErrorKind::PayloadInvalid,
                        is_shallow: false,
                        is_remote: true,
                        nested_error: None,
                        header_name: None,
                        header_value: None,
                        timeout_name: None,
                        timeout_value: None,
                        property_name: None,
                        property_value: None,
                        command_name: Some("setNotificationPreferenceForDeviceUpdates".to_string()),
                        protocol_version: None,
                        supported_protocol_major_versions: None,
                    })
                }
            }
            Err(err) => Err(err),
        }
    }

    /// Shutdown the [`SetNotificationPreferenceForDeviceUpdatesCommandInvoker`]. Unsubscribes from the response topic and cancels the receiver loop to drop the receiver and to prevent the task from looping indefinitely.
    ///
    /// 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(&self) -> Result<(), AIOProtocolError> {
        self.0.shutdown().await
    }
}
