//! Content types for MCP messages
//!
//! Provides polymorphic content system inspired by Go mcphost

use serde::{Deserialize, Serialize};
use serde_json::Value;

use super::annotations::Annotations;

/// Content type trait
pub trait Content: Send + Sync {
    /// Get the content type identifier
    fn content_type(&self) -> &'static str;

    /// Convert to JSON value
    fn to_value(&self) -> Value;

    /// Get annotations if present
    fn annotations(&self) -> Option<&Annotations>;
}

/// Text content
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TextContent {
    /// Content type
    #[serde(rename = "type")]
    pub r#type: String,

    /// The text
    pub text: String,

    /// Optional annotations
    #[serde(skip_serializing_if = "Option::is_none", flatten)]
    pub annotations: Option<Annotations>,
}

impl TextContent {
    /// Create new text content
    pub fn new(text: impl Into<String>) -> Self {
        Self {
            r#type: "text".to_string(),
            text: text.into(),
            annotations: None,
        }
    }

    /// Set annotations
    pub fn with_annotations(mut self, annotations: Annotations) -> Self {
        self.annotations = Some(annotations);
        self
    }
}

impl Content for TextContent {
    fn content_type(&self) -> &'static str {
        "text"
    }

    fn to_value(&self) -> Value {
        serde_json::to_value(self).unwrap()
    }

    fn annotations(&self) -> Option<&Annotations> {
        self.annotations.as_ref()
    }
}

/// Image content
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ImageContent {
    /// Content type
    #[serde(rename = "type")]
    pub r#type: String,

    /// Base64-encoded image data
    pub data: String,

    /// MIME type (e.g., "image/png")
    #[serde(rename = "mimeType")]
    pub mime_type: String,

    /// Optional annotations
    #[serde(skip_serializing_if = "Option::is_none", flatten)]
    pub annotations: Option<Annotations>,
}

impl ImageContent {
    /// Create new image content
    pub fn new(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
        Self {
            r#type: "image".to_string(),
            data: data.into(),
            mime_type: mime_type.into(),
            annotations: None,
        }
    }

    /// Set annotations
    pub fn with_annotations(mut self, annotations: Annotations) -> Self {
        self.annotations = Some(annotations);
        self
    }
}

impl Content for ImageContent {
    fn content_type(&self) -> &'static str {
        "image"
    }

    fn to_value(&self) -> Value {
        serde_json::to_value(self).unwrap()
    }

    fn annotations(&self) -> Option<&Annotations> {
        self.annotations.as_ref()
    }
}

/// Audio content
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct AudioContent {
    /// Content type
    #[serde(rename = "type")]
    pub r#type: String,

    /// Base64-encoded audio data
    pub data: String,

    /// MIME type (e.g., "audio/wav")
    #[serde(rename = "mimeType")]
    pub mime_type: String,

    /// Optional annotations
    #[serde(skip_serializing_if = "Option::is_none", flatten)]
    pub annotations: Option<Annotations>,
}

impl AudioContent {
    /// Create new audio content
    pub fn new(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
        Self {
            r#type: "audio".to_string(),
            data: data.into(),
            mime_type: mime_type.into(),
            annotations: None,
        }
    }

    /// Set annotations
    pub fn with_annotations(mut self, annotations: Annotations) -> Self {
        self.annotations = Some(annotations);
        self
    }
}

impl Content for AudioContent {
    fn content_type(&self) -> &'static str {
        "audio"
    }

    fn to_value(&self) -> Value {
        serde_json::to_value(self).unwrap()
    }

    fn annotations(&self) -> Option<&Annotations> {
        self.annotations.as_ref()
    }
}

/// Resource link content
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ResourceLink {
    /// Content type
    #[serde(rename = "type")]
    pub r#type: String,

    /// Resource URI
    pub uri: String,

    /// Optional title
    #[serde(skip_serializing_if = "Option::is_none")]
    pub title: Option<String>,

    /// Optional annotations
    #[serde(skip_serializing_if = "Option::is_none", flatten)]
    pub annotations: Option<Annotations>,
}

impl ResourceLink {
    /// Create new resource link
    pub fn new(uri: impl Into<String>) -> Self {
        Self {
            r#type: "resource".to_string(),
            uri: uri.into(),
            title: None,
            annotations: None,
        }
    }

    /// Set title
    pub fn with_title(mut self, title: impl Into<String>) -> Self {
        self.title = Some(title.into());
        self
    }

    /// Set annotations
    pub fn with_annotations(mut self, annotations: Annotations) -> Self {
        self.annotations = Some(annotations);
        self
    }
}

impl Content for ResourceLink {
    fn content_type(&self) -> &'static str {
        "resource"
    }

    fn to_value(&self) -> Value {
        serde_json::to_value(self).unwrap()
    }

    fn annotations(&self) -> Option<&Annotations> {
        self.annotations.as_ref()
    }
}

/// Content array helper
pub type ContentArray = Vec<Box<dyn Content>>;

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

    #[test]
    fn test_text_content() {
        let content = TextContent::new("Hello, world!");
        assert_eq!(content.content_type(), "text");
        assert_eq!(content.text, "Hello, world!");
        assert!(content.annotations.is_none());
    }

    #[test]
    fn test_text_with_annotations() {
        let ann = Annotations::new().with_priority(0.9);
        let content = TextContent::new("Important!").with_annotations(ann.clone());

        assert_eq!(content.annotations, Some(ann));
    }

    #[test]
    fn test_image_content() {
        let content = ImageContent::new("base64data", "image/png");
        assert_eq!(content.content_type(), "image");
        assert_eq!(content.data, "base64data");
        assert_eq!(content.mime_type, "image/png");
    }

    #[test]
    fn test_resource_link() {
        let link = ResourceLink::new("file://test.txt").with_title("Test File");

        assert_eq!(link.content_type(), "resource");
        assert_eq!(link.uri, "file://test.txt");
        assert_eq!(link.title, Some("Test File".to_string()));
    }

    #[test]
    fn test_serialization() {
        let content = TextContent::new("test");
        let json = content.to_value();

        assert_eq!(json["text"], "test");
        assert_eq!(json["type"], "text");
    }
}
