//! The type that represents a request of `chat.postMessage`.
//!
//! <https://api.slack.com/methods/chat.postMessage>

use crate::Block;
use serde::Serialize;

/// The type that represents a request of `chat.postMessage`.
///
/// <https://api.slack.com/methods/chat.postMessage>
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct ChatPostMessageRequest {
    /// Channel, private group, or IM channel to send message to.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_channel>
    pub channel: String,

    /// attachments / blocks / text.
    /// One of these arguments is required to describe the content of the message.
    #[serde(flatten)]
    pub content: ChatPostMessageContent,

    /// (Legacy) Pass true to post the message as the authed user instead of as a bot.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_as_user>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub as_user: Option<bool>,

    /// Emoji to use as the icon for this message.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_icon_emoji>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub icon_emoji: Option<String>,

    /// URL to an image to use as the icon for this message.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_icon_url>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub icon_url: Option<String>,

    /// Find and link user groups.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_link_names>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub link_names: Option<bool>,

    /// JSON object with event_type and event_payload fields, presented as a URL-encoded string.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_metadata>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub metadata: Option<String>,

    /// Disable Slack markup parsing by setting to `false`.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_mrkdwn>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub mrkdwn: Option<bool>,

    /// Change how messages are treated.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_parse>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parse: Option<String>,

    /// Used in conjunction with `thread_ts` and indicates whether reply should be made visible to everyone in the channel or conversation.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_reply_broadcast>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub reply_broadcast: Option<bool>,

    /// Provide another message's `ts` value to make this message a reply.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_thread_ts>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub thread_ts: Option<String>,

    /// Pass true to enable unfurling of primarily text-based content.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_unfurl_links>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub unfurl_links: Option<bool>,

    /// Pass false to disable unfurling of media content.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_unfurl_media>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub unfurl_media: Option<bool>,

    /// Set your bot's user name.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_username>
    #[serde(skip_serializing_if = "Option::is_none")]
    pub username: Option<String>,
}

/// The type that represents a selection of [`ChatPostMessageRequest::content`].
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ChatPostMessageContent {
    /// A JSON-based array of structured attachments, presented as a URL-encoded string.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_attachments>
    Attachments(Vec<Attachments>),

    /// A JSON-based array of structured blocks, presented as a URL-encoded string.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_blocks>
    Blocks(Vec<Block>),

    /// How this field works and whether it is required depends on other fields you use in your API call.
    ///
    /// <https://api.slack.com/methods/chat.postMessage#arg_text>
    Text(String),
}

/// A JSON-based array of structured attachments, presented as a URL-encoded string.
///
/// <https://api.slack.com/methods/chat.postMessage#arg_attachments>
#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct Attachments {
    /// The `pretext`.
    pub pretext: String,

    /// The `text`.
    pub text: String,
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::{Block, PlainTextObject, SectionBlock, TextObject};
    use anyhow::Result;

    #[test]
    fn test_ser_text() -> Result<()> {
        let testdata = ChatPostMessageRequest {
            channel: "channel dummy".into(),
            content: ChatPostMessageContent::Text("content-text dummy".into()),
            as_user: Some(false),
            icon_emoji: Some("icon_emoji dummy".into()),
            icon_url: Some("icon_url dummy".into()),
            link_names: Some(false),
            metadata: Some("metadata dummy".into()),
            mrkdwn: Some(false),
            parse: Some("parse dummy".into()),
            reply_broadcast: Some(false),
            thread_ts: Some("thread_ts dummy".into()),
            unfurl_links: Some(false),
            unfurl_media: Some(false),
            username: Some("username dummy".into()),
        };

        let ser = serde_json::to_string(&testdata)?;
        let expected = r#"{"channel":"channel dummy","text":"content-text dummy","as_user":false,"icon_emoji":"icon_emoji dummy","icon_url":"icon_url dummy","link_names":false,"metadata":"metadata dummy","mrkdwn":false,"parse":"parse dummy","reply_broadcast":false,"thread_ts":"thread_ts dummy","unfurl_links":false,"unfurl_media":false,"username":"username dummy"}"#;

        assert_eq!(ser, expected);

        Ok(())
    }

    #[test]
    fn test_ser_attachments() -> Result<()> {
        let testdata = ChatPostMessageRequest {
            channel: "channel dummy".into(),
            content: ChatPostMessageContent::Attachments(vec![Attachments {
                pretext: "content-attachments-pretext dummy".into(),
                text: "content-attachments-text dummy".into(),
            }]),
            as_user: None,
            icon_emoji: None,
            icon_url: None,
            link_names: None,
            metadata: None,
            mrkdwn: None,
            parse: None,
            reply_broadcast: None,
            thread_ts: None,
            unfurl_links: None,
            unfurl_media: None,
            username: None,
        };

        let ser = serde_json::to_string(&testdata)?;
        let expected = r#"{"channel":"channel dummy","attachments":[{"pretext":"content-attachments-pretext dummy","text":"content-attachments-text dummy"}]}"#;

        assert_eq!(ser, expected);

        Ok(())
    }

    #[test]
    fn test_ser_blocks() -> Result<()> {
        let testdata = ChatPostMessageRequest {
            channel: "channel dummy".into(),
            content: ChatPostMessageContent::Blocks(vec![Block::Section(SectionBlock {
                text: Some(TextObject::PlainText(PlainTextObject {
                    text: "content-blocks-0-section-text dummy".into(),
                    emoji: None,
                })),
                ..Default::default()
            })]),
            as_user: None,
            icon_emoji: None,
            icon_url: None,
            link_names: None,
            metadata: None,
            mrkdwn: None,
            parse: None,
            reply_broadcast: None,
            thread_ts: None,
            unfurl_links: None,
            unfurl_media: None,
            username: None,
        };

        let ser = serde_json::to_string(&testdata)?;
        let expected = r#"{"channel":"channel dummy","blocks":[{"type":"section","text":{"type":"plain_text","text":"content-blocks-0-section-text dummy"}}]}"#;

        assert_eq!(ser, expected);

        Ok(())
    }
}
