mod author;
mod field;
mod footer;
mod image;
mod provider;
mod thumbnail;
mod video;

pub use self::{
    author::EmbedAuthor, field::EmbedField, footer::EmbedFooter, image::EmbedImage,
    provider::EmbedProvider, thumbnail::EmbedThumbnail, video::EmbedVideo,
};

use crate::datetime::Timestamp;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Embed {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub author: Option<EmbedAuthor>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub color: Option<u32>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(default, skip_serializing_if = "Vec::is_empty")]
    pub fields: Vec<EmbedField>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub footer: Option<EmbedFooter>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub image: Option<EmbedImage>,
    #[serde(rename = "type")]
    pub kind: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub provider: Option<EmbedProvider>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub thumbnail: Option<EmbedThumbnail>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timestamp: Option<Timestamp>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub video: Option<EmbedVideo>,
}

#[cfg(test)]
mod tests {
    use super::{
        Embed, EmbedAuthor, EmbedField, EmbedFooter, EmbedImage, EmbedProvider, EmbedThumbnail,
        EmbedVideo,
    };
    use crate::datetime::{Timestamp, TimestampParseError};
    use serde_test::Token;
    use std::str::FromStr;

    #[test]
    fn test_embed() -> Result<(), TimestampParseError> {
        let timestamp = Timestamp::from_str("2021-08-02T16:56:43.772000+00:00")?;

        let value = Embed {
            author: None,
            color: Some(123),
            description: Some("a description".to_owned()),
            fields: Vec::new(),
            footer: None,
            image: None,
            kind: "rich".to_owned(),
            provider: None,
            thumbnail: None,
            timestamp: Some(timestamp),
            title: Some("a title".to_owned()),
            url: Some("https://example.com".to_owned()),
            video: None,
        };

        serde_test::assert_tokens(
            &value,
            &[
                Token::Struct {
                    name: "Embed",
                    len: 6,
                },
                Token::Str("color"),
                Token::Some,
                Token::U32(123),
                Token::Str("description"),
                Token::Some,
                Token::Str("a description"),
                Token::Str("type"),
                Token::Str("rich"),
                Token::Str("timestamp"),
                Token::Some,
                Token::Str("2021-08-02T16:56:43.772000+00:00"),
                Token::Str("title"),
                Token::Some,
                Token::Str("a title"),
                Token::Str("url"),
                Token::Some,
                Token::Str("https://example.com"),
                Token::StructEnd,
            ],
        );

        Ok(())
    }

    #[allow(clippy::too_many_lines)]
    #[test]
    fn test_embed_complete() -> Result<(), TimestampParseError> {
        let timestamp = Timestamp::from_str("2021-08-02T16:56:43.772000+00:00")?;

        let value = Embed {
            author: Some(EmbedAuthor {
                icon_url: Some("https://example.com/1.png".to_owned()),
                name: "test".to_owned(),
                proxy_icon_url: Some("https://example.com".to_owned()),
                url: Some("https://example.com".to_owned()),
            }),
            color: Some(123),
            description: Some("a description".to_owned()),
            fields: vec![EmbedField {
                inline: true,
                name: "name".to_owned(),
                value: "value".to_owned(),
            }],
            footer: Some(EmbedFooter {
                icon_url: Some("https://example.com/1.png".to_owned()),
                proxy_icon_url: Some("https://cdn.example.com/1-hash.png".to_owned()),
                text: "a footer".to_owned(),
            }),
            image: Some(EmbedImage {
                height: Some(1440),
                proxy_url: Some("https://cdn.example.com/1-hash.png".to_owned()),
                url: "https://example.com/1.png".to_owned(),
                width: Some(2560),
            }),
            kind: "rich".to_owned(),
            provider: Some(EmbedProvider {
                name: Some("Example".to_owned()),
                url: Some("https://example.com".to_owned()),
            }),
            thumbnail: Some(EmbedThumbnail {
                height: Some(1440),
                proxy_url: Some("https://cdn.example.com/1-hash.png".to_owned()),
                url: "https://example.com/1.png".to_owned(),
                width: Some(2560),
            }),
            timestamp: Some(timestamp),
            title: Some("a title".to_owned()),
            url: Some("https://example.com".to_owned()),
            video: Some(EmbedVideo {
                height: Some(1440),
                proxy_url: Some("https://proxy.cdn.example.com/1-hash.mp4".to_owned()),
                url: Some("https://cdn.example.com/1-hash.mp4".to_owned()),
                width: Some(2560),
            }),
        };

        serde_test::assert_tokens(
            &value,
            &[
                Token::Struct {
                    name: "Embed",
                    len: 13,
                },
                Token::Str("author"),
                Token::Some,
                Token::Struct {
                    name: "EmbedAuthor",
                    len: 4,
                },
                Token::Str("icon_url"),
                Token::Some,
                Token::Str("https://example.com/1.png"),
                Token::Str("name"),
                Token::Str("test"),
                Token::Str("proxy_icon_url"),
                Token::Some,
                Token::Str("https://example.com"),
                Token::Str("url"),
                Token::Some,
                Token::Str("https://example.com"),
                Token::StructEnd,
                Token::Str("color"),
                Token::Some,
                Token::U32(123),
                Token::Str("description"),
                Token::Some,
                Token::Str("a description"),
                Token::Str("fields"),
                Token::Seq { len: Some(1) },
                Token::Struct {
                    name: "EmbedField",
                    len: 3,
                },
                Token::Str("inline"),
                Token::Bool(true),
                Token::Str("name"),
                Token::Str("name"),
                Token::Str("value"),
                Token::Str("value"),
                Token::StructEnd,
                Token::SeqEnd,
                Token::Str("footer"),
                Token::Some,
                Token::Struct {
                    name: "EmbedFooter",
                    len: 3,
                },
                Token::Str("icon_url"),
                Token::Some,
                Token::Str("https://example.com/1.png"),
                Token::Str("proxy_icon_url"),
                Token::Some,
                Token::Str("https://cdn.example.com/1-hash.png"),
                Token::Str("text"),
                Token::Str("a footer"),
                Token::StructEnd,
                Token::Str("image"),
                Token::Some,
                Token::Struct {
                    name: "EmbedImage",
                    len: 4,
                },
                Token::Str("height"),
                Token::Some,
                Token::U64(1440),
                Token::Str("proxy_url"),
                Token::Some,
                Token::Str("https://cdn.example.com/1-hash.png"),
                Token::Str("url"),
                Token::Str("https://example.com/1.png"),
                Token::Str("width"),
                Token::Some,
                Token::U64(2560),
                Token::StructEnd,
                Token::Str("type"),
                Token::Str("rich"),
                Token::Str("provider"),
                Token::Some,
                Token::Struct {
                    name: "EmbedProvider",
                    len: 2,
                },
                Token::Str("name"),
                Token::Some,
                Token::Str("Example"),
                Token::Str("url"),
                Token::Some,
                Token::Str("https://example.com"),
                Token::StructEnd,
                Token::Str("thumbnail"),
                Token::Some,
                Token::Struct {
                    name: "EmbedThumbnail",
                    len: 4,
                },
                Token::Str("height"),
                Token::Some,
                Token::U64(1440),
                Token::Str("proxy_url"),
                Token::Some,
                Token::Str("https://cdn.example.com/1-hash.png"),
                Token::Str("url"),
                Token::Str("https://example.com/1.png"),
                Token::Str("width"),
                Token::Some,
                Token::U64(2560),
                Token::StructEnd,
                Token::Str("timestamp"),
                Token::Some,
                Token::Str("2021-08-02T16:56:43.772000+00:00"),
                Token::Str("title"),
                Token::Some,
                Token::Str("a title"),
                Token::Str("url"),
                Token::Some,
                Token::Str("https://example.com"),
                Token::Str("video"),
                Token::Some,
                Token::Struct {
                    name: "EmbedVideo",
                    len: 4,
                },
                Token::Str("height"),
                Token::Some,
                Token::U64(1440),
                Token::Str("proxy_url"),
                Token::Some,
                Token::Str("https://proxy.cdn.example.com/1-hash.mp4"),
                Token::Str("url"),
                Token::Some,
                Token::Str("https://cdn.example.com/1-hash.mp4"),
                Token::Str("width"),
                Token::Some,
                Token::U64(2560),
                Token::StructEnd,
                Token::StructEnd,
            ],
        );

        Ok(())
    }
}
