//! The type that represents all erros in `slack-framework-rs`.

mod api;
mod core;
mod server;

pub use api::ApiError;
pub use core::CoreError;
pub use server::ServerError;

/// The `Box<Error>`.
type BoxError = Box<dyn std::error::Error + 'static + Send + Sync>;

/// The type that represents all erros in `slack-framework-rs`.
#[derive(thiserror::Error, Debug)]
pub enum Error {
    /// The error with Slack API Client.
    #[error("error in API Client: {0}")]
    Api(#[from] ApiError),

    /// The error with commonly process.
    #[error("error in commonly process: {0}")]
    Core(#[from] CoreError),

    /// The error with server process.
    #[error("error about server: {0}")]
    Server(#[from] ServerError),

    /// The error when the required value is missing.
    #[error("{0}")]
    RequiredValueIsMissing(String),

    /// The error that occurs with failed to deserialize JSON data.
    #[error("failed to deserialize: {0}")]
    DeserializeJson(serde_json::Error),

    /// The stacked error.
    #[error("stacked error: message = {message}, stack = {stack:?}")]
    Stacked {
        /// The error message for this stack.
        message: &'static str,

        /// The Stacked errors.
        stack: Option<Box<Error>>,
    },
}

impl Error {
    /// Creates [`Error::RequiredValueIsMissing`].
    pub fn required_value_is_missing(values: &[&str]) -> Self {
        if values.is_empty() {
            Self::RequiredValueIsMissing("Some required value is missing.".into())
        } else if values.len() == 1 {
            Self::RequiredValueIsMissing(format!("`{}` is required.", values.first().unwrap()))
        } else {
            Self::RequiredValueIsMissing(format!("One of `{}` is required.", values.join("` or `")))
        }
    }

    /// Creates the stacked error.
    pub fn stack(self, message: &'static str) -> Self {
        Self::Stacked {
            message,
            stack: Some(Box::new(self)),
        }
    }

    /// Creates [`ApiError::FailedToRequestByHttp`].
    pub fn failed_to_request_by_http(
        inner: &'static str,
        e: impl std::error::Error + 'static + Send + Sync,
    ) -> Self {
        Self::Api(ApiError::FailedToRequestByHttp(inner, Box::new(e)))
    }

    /// Creates [`ApiError::FailedToReadJson`].
    pub fn failed_to_read_json(
        inner: &'static str,
        e: impl std::error::Error + 'static + Send + Sync,
    ) -> Self {
        Self::Api(ApiError::FailedToReadJson(inner, Box::new(e)))
    }

    /// Creates [`ApiError::FailedToReadStream`].
    pub fn failed_to_read_stream(
        inner: &'static str,
        e: impl std::error::Error + 'static + Send + Sync,
    ) -> Self {
        Self::Api(ApiError::FailedToReadStream(inner, Box::new(e)))
    }

    /// Creates [`ApiError::FailedToReadFileInFilesUpload`].
    pub fn failed_to_read_file_in_files_upload(
        e: impl std::error::Error + 'static + Send + Sync,
    ) -> Self {
        Self::Api(ApiError::FailedToReadFileInFilesUpload(Box::new(e)))
    }

    /// Creates [`ApiError::FailedCreatingMulipartData`].
    pub fn failed_creating_mulipart_data(
        e: impl std::error::Error + 'static + Send + Sync,
    ) -> Self {
        Self::Api(ApiError::FailedCreatingMulipartData(Box::new(e)))
    }

    /// Creates [`CoreError::ParsingNumber`].
    pub fn parsing_number(
        inner: &'static str,
        e: impl std::error::Error + 'static + Send + Sync,
    ) -> Self {
        Self::Core(CoreError::ParsingNumber(inner, Box::new(e)))
    }

    /// Creates [`CoreError::FailedToInitializeHmac`].
    pub fn failed_to_initialize_hmac(e: impl std::error::Error + 'static + Send + Sync) -> Self {
        Self::Core(CoreError::FailedToInitializeHmac(Box::new(e)))
    }

    /// Creates [`ServerError::FailedToBindSocket`].
    pub fn failed_to_bind_socket(e: impl std::error::Error + 'static + Send + Sync) -> Self {
        Self::Server(ServerError::FailedToBindSocket(Box::new(e)))
    }

    /// Creates [`ServerError::FailedToServe`].
    pub fn failed_to_serve(e: impl std::error::Error + 'static + Send + Sync) -> Self {
        Self::Server(ServerError::FailedToServe(Box::new(e)))
    }
}
