//! Rich text block.
//!
//! <https://api.slack.com/reference/block-kit/blocks#rich_text>

use crate::BlockId;
use serde::Serialize;

/// Rich text block.
///
/// <https://api.slack.com/reference/block-kit/blocks#rich_text>
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextBlock {
    /// An array of rich text objects - [`rich_text_section`](https://api.slack.com/reference/block-kit/blocks#rich_text_section), [`rich_text_list`](https://api.slack.com/reference/block-kit/blocks#rich_text_list), [`rich_text_preformatted`](https://api.slack.com/reference/block-kit/blocks#rich_text_preformatted), and [`rich_text_quote`](https://api.slack.com/reference/block-kit/blocks#rich_text_quote).
    pub elements: Vec<RichTextObject>,

    /// A unique identifier for a block.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub block_id: Option<BlockId>,
}

#[allow(clippy::enum_variant_names)]
/// An element in rich text block.
///
/// <https://api.slack.com/reference/block-kit/blocks#rich_text>
#[derive(Serialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum RichTextObject {
    /// Section element: `rich_text_section`.
    ///
    /// <https://api.slack.com/reference/block-kit/blocks#rich_text_section>
    RichTextSection(RichTextSection),

    /// List element: `rich_text_list`.
    ///
    /// <https://api.slack.com/reference/block-kit/blocks#rich_text_list>
    RichTextList(RichTextList),

    /// Preformatted code block element: `rich_text_preformatted`.
    ///
    /// <https://api.slack.com/reference/block-kit/blocks#rich_text_preformatted>
    RichTextPreformatted(RichTextPreformatted),

    /// Quote element: `rich_text_quote`.
    ///
    /// <https://api.slack.com/reference/block-kit/blocks#rich_text_quote>
    RichTextQuote(RichTextQuote),
}

/// Section element: `rich_text_section`.
///
/// <https://api.slack.com/reference/block-kit/blocks#rich_text_section>
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextSection {
    /// An array of [rich text elements](https://api.slack.com/reference/block-kit/blocks#element-types).
    pub elements: Vec<RichTextElement>,
}

/// List element: `rich_text_list`.
///
/// <https://api.slack.com/reference/block-kit/blocks#rich_text_list>
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextList {
    /// Either `bullet` or `ordered`, the latter meaning a numbered list.
    pub style: RichTextListStyle,

    /// An array of [`rich_text_section`](https://api.slack.com/reference/block-kit/blocks#rich_text_section) objects containing two properties: type, which is "rich_text_section", and elements, which is an array of [rich text element objects](https://api.slack.com/reference/block-kit/blocks#element-types).
    pub elements: Vec<RichTextSection>,

    /// Number of pixels to indent the list.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub indent: Option<u64>,

    /// Number of pixels to offset the list.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub offset: Option<u64>,

    /// Number of pixels of border thickness.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub border: Option<u64>,
}

/// The value of the `style` field in [`RichTextList`].
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum RichTextListStyle {
    /// `bullet`.
    Bullet,

    /// `ordered`.
    Ordered,
}

/// Preformatted code block element: `rich_text_preformatted`.
///
/// <https://api.slack.com/reference/block-kit/blocks#rich_text_preformatted>
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextPreformatted {
    /// An array of [rich text elements](https://api.slack.com/reference/block-kit/blocks#element-types).
    pub elements: Vec<RichTextElement>,

    /// Number of pixels of border thickness.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub border: Option<u64>,
}

/// Quote element: `rich_text_quote`.
///
/// <https://api.slack.com/reference/block-kit/blocks#rich_text_quote>
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextQuote {
    /// An array of [rich text elements](https://api.slack.com/reference/block-kit/blocks#element-types).
    pub elements: Vec<RichTextElement>,

    /// Number of pixels of border thickness.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub border: Option<u64>,
}

/// Rich text element types.
///
/// <https://api.slack.com/reference/block-kit/blocks#element-types>
#[derive(Serialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum RichTextElement {
    /// The following are the properties of the `broadcast` object type in the `elements` array.
    Broadcast(RichTextElementBroadcast),

    /// The following are the properties of the `color` object type in the `elements` array.
    Color(RichTextElementColor),

    /// The following are the properties of the `channel` object type in the `elements` array.
    Channel(RichTextElementChannel),

    /// The following are the properties of the `date` object type in the `elements` array.
    Date(RichTextElementDate),

    /// The following are the properties of the `emoji` object type in the `elements` array.
    Emoji(RichTextElementEmoji),

    /// The following are the properties of the `link` object type in the `elements` array.
    Link(RichTextElementLink),

    /// The following are the properties of the `text` object type in the `elements` array.
    Text(RichTextElementText),

    /// The following are the properties of the `user` object type in the `elements` array.
    User(RichTextElementUser),

    /// The following are the properties of the `usergroup` object type in the `elements` array.
    Usergroup(RichTextElementUsergroup),
}

/// The following are the properties of the `broadcast` object type in the `elements` array.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextElementBroadcast {
    /// The range of the broadcast; value can be `here`, `channel`, or `everyone`.
    pub range: RichTextElementBroadcastRange,
}

/// The range of the broadcast; value can be `here`, `channel`, or `everyone`.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum RichTextElementBroadcastRange {
    /// `here`.
    Here,

    /// `channel`.
    Channel,

    /// `everyone`.
    Everyone,
}

/// The following are the properties of the `color` object type in the `elements` array.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextElementColor {
    /// The hex value for the color.
    pub value: String,
}

/// The following are the properties of the `channel` object type in the `elements` array.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextElementChannel {
    /// The ID of the channel to be mentioned.
    pub channel_id: String,

    /// An object of six optional boolean properties that dictate style: `bold`, `italic`, `strike`, `highlight`, `client_highlight`, and `unlink`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub style: Option<RichTextStyle2>,
}

/// An object of six optional boolean properties that dictate style: `bold`, `italic`, `strike`, `highlight`, `client_highlight`, and `unlink`.
///
/// like <https://github.com/slackapi/node-slack-sdk/blob/220da721c04d37dacf06c4b6c6630dc3b451b583/packages/types/src/block-kit/extensions.ts#L82>
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextStyle2 {
    /// When `true`, boldens the text in this element. Defaults to `false`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bold: Option<bool>,

    /// When `true`, italicizes the text in this element. Defaults to `false`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub italic: Option<bool>,

    /// When `true`, strikes through the text in this element. Defaults to `false`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub strike: Option<bool>,

    /// Defaults to `false`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub highlight: Option<bool>,

    /// Defaults to `false`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub client_highlight: Option<bool>,

    /// Defaults to `false`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub unlink: Option<bool>,
}

/// The following are the properties of the `date` object type in the `elements` array.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextElementDate {
    /// A Unix timestamp for the date to be displayed in seconds.
    pub timestamp: u64,

    /// A template string containing curly-brace-enclosed tokens to substitute your provided `timestamp`.
    pub format: String,

    /// URL to link the entire `format` string to.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,

    /// Text to display in place of the date should parsing, formatting or displaying fail.
    pub fallback: Option<String>,
}

/// The following are the properties of the `emoji` object type in the `elements` array.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextElementEmoji {
    /// The name of the emoji; i.e. "wave" or "wave::skin-tone-2".
    pub name: String,
}

/// The following are the properties of the `link` object type in the `elements` array.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextElementLink {
    /// The link's url.
    pub url: String,

    /// The text shown to the user (instead of the url).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub text: Option<String>,

    /// Indicates whether the link is safe.
    #[serde(skip_serializing_if = "Option::is_none", rename = "unsafe")]
    pub unsafe_: Option<bool>,

    /// An object containing four boolean properties: bold, italic, strike, and code.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub style: Option<RichTextStyle>,
}

/// An object containing four boolean properties: `bold`, `italic`, `strike`, and `code`.
///
/// <https://github.com/slackapi/node-slack-sdk/blob/220da721c04d37dacf06c4b6c6630dc3b451b583/packages/types/src/block-kit/extensions.ts#L82>
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextStyle {
    /// When `true`, boldens the text in this element. Defaults to `false`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub bold: Option<bool>,

    /// When `true`, the text is preformatted in an inline code style. Defaults to `false.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub code: Option<bool>,

    /// When `true`, italicizes the text in this element. Defaults to `false`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub italic: Option<bool>,

    /// When `true`, strikes through the text in this element. Defaults to `false`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub strike: Option<bool>,
}

/// The following are the properties of the `text` object type in the `elements` array.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextElementText {
    /// The text shown to the user.
    pub text: String,

    /// An object containing four boolean fields, none of which are required: `bold`, `italic`, `strike`, and `code`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub style: Option<RichTextStyle>,
}

/// The following are the properties of the `user` object type in the `elements` array.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextElementUser {
    /// The ID of the user to be mentioned.
    pub user_id: String,

    /// An object of six optional boolean properties that dictate style: `bold`, `italic`, `strike`, `highlight`, `client_highlight`, and `unlink`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub style: Option<RichTextStyle2>,
}

/// The following are the properties of the `usergroup` object type in the `elements` array.
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "snake_case")]
pub struct RichTextElementUsergroup {
    /// The ID of the user group to be mentioned.
    pub usergroup_id: String,

    /// An object of six optional boolean properties that dictate style: `bold`, `italic`, `strike`, `highlight`, `client_highlight`, and `unlink`.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub style: Option<RichTextStyle2>,
}
