use crate::blocks::RichText;
use serde::Serialize;

/// A table cell value in table rows
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(untagged)]
pub enum TableCell {
    /// A plain text table cell
    RawText(RawText),

    /// A rich text table cell
    RichText(RichText),
}

impl<T: Into<String>> From<T> for TableCell {
    fn from(value: T) -> Self {
        Self::RawText(RawText::from(value))
    }
}

impl From<RawText> for TableCell {
    fn from(value: RawText) -> Self {
        Self::RawText(value)
    }
}

impl From<RichText> for TableCell {
    fn from(value: RichText) -> Self {
        Self::RichText(value)
    }
}

/// A plain text table cell value which can be used both in [DataTable](crate::blocks::DataTable) and
/// [Table](crate::blocks::Table) blocks.
#[derive(Debug, Clone, PartialEq)]
pub struct RawText(String);

impl RawText {
    pub fn new<T: Into<String>>(text: T) -> Self {
        Self(text.into())
    }
}

impl<T: Into<String>> From<T> for RawText {
    fn from(value: T) -> Self {
        Self::new(value)
    }
}

impl Serialize for RawText {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer
    {
        use serde::ser::SerializeStruct;

        let mut state = serializer.serialize_struct("RawText", 2)?;
        state.serialize_field("type", "raw_text")?;
        state.serialize_field("text", &self.0)?;
        state.end()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::blocks::rich_text::{RichTextSection, types::RichTextElementLink};

    #[test]
    fn it_serializes_into_raw_text_cell() {
        let cell = TableCell::RawText("Data 1A".into());

        let expected = serde_json::json!({
            "type": "raw_text",
            "text": "Data 1A",
        });

        let json = serde_json::to_value(cell).unwrap();
        assert_eq!(json, expected);
    }

    #[test]
    fn it_serializes_into_rich_text_cell() {
        let cell = TableCell::RichText(rich_text());

        let expected = serde_json::json!({
            "type": "rich_text",
            "elements": [
                {
                    "type": "rich_text_section",
                    "elements": [
                        {
                            "text": "Data 1B",
                            "type": "link",
                            "url": "https://slack.com"
                        }
                    ]
                }
            ]
        });

        let json = serde_json::to_value(cell).unwrap();
        assert_eq!(json, expected);
    }

    fn rich_text() -> RichText {
        RichText::builder()
            .element(
                RichTextSection::builder()
                    .element(
                        RichTextElementLink::builder()
                            .text("Data 1B")
                            .url("https://slack.com")
                            .build()
                            .unwrap(),
                    )
                    .build()
                    .unwrap(),
            )
            .build()
            .unwrap()
    }
}
