/// The role of a message participant.
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Role {
    System,
    User,
    Assistant,
    Tool,
}

/// A reference to a tool call requested by the assistant.
///
/// Stored on assistant messages to record which tool calls were made,
/// so that subsequent `Tool` role messages can be correlated by id.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ToolCallRef {
    /// Unique identifier for the tool call.
    pub id: String,
    /// Name of the tool that was called.
    pub name: String,
    /// Parsed JSON arguments for the tool call.
    pub args: serde_json::Value,
}

/// A single message in the conversation history.
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Message {
    /// Who sent this message.
    pub role: Role,
    /// Textual content of the message.
    pub content: String,
    /// For `Tool` role messages, the id of the tool call this is a result for.
    pub tool_call_id: Option<String>,
    /// Tool calls requested by the assistant (empty for other roles).
    pub tool_calls: Vec<ToolCallRef>,
}

impl Message {
    /// Create a system message.
    pub fn system(content: &str) -> Self {
        Self {
            role: Role::System,
            content: content.to_string(),
            tool_call_id: None,
            tool_calls: Vec::new(),
        }
    }

    /// Create a user message.
    pub fn user(content: &str) -> Self {
        Self {
            role: Role::User,
            content: content.to_string(),
            tool_call_id: None,
            tool_calls: Vec::new(),
        }
    }

    /// Create an assistant message with text only.
    pub fn assistant(content: &str) -> Self {
        Self {
            role: Role::Assistant,
            content: content.to_string(),
            tool_call_id: None,
            tool_calls: Vec::new(),
        }
    }

    /// Create an assistant message with text and a single tool call reference.
    pub fn assistant_with_tool_call(content: &str, call: ToolCallRef) -> Self {
        Self {
            role: Role::Assistant,
            content: content.to_string(),
            tool_call_id: None,
            tool_calls: vec![call],
        }
    }

    /// Create an assistant message with text and multiple tool call references.
    pub fn assistant_with_tool_calls(content: &str, calls: Vec<ToolCallRef>) -> Self {
        Self {
            role: Role::Assistant,
            content: content.to_string(),
            tool_call_id: None,
            tool_calls: calls,
        }
    }

    /// Create a tool result message correlated to a previous tool call.
    pub fn tool_result(id: impl Into<String>, content: &str) -> Self {
        Self {
            role: Role::Tool,
            content: content.to_string(),
            tool_call_id: Some(id.into()),
            tool_calls: Vec::new(),
        }
    }
}

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

    #[test]
    fn system_message() {
        let msg = Message::system("You are helpful.");
        assert_eq!(msg.role, Role::System);
        assert_eq!(msg.content, "You are helpful.");
        assert!(msg.tool_call_id.is_none());
        assert!(msg.tool_calls.is_empty());
    }

    #[test]
    fn user_message() {
        let msg = Message::user("Hello");
        assert_eq!(msg.role, Role::User);
        assert_eq!(msg.content, "Hello");
        assert!(msg.tool_call_id.is_none());
        assert!(msg.tool_calls.is_empty());
    }

    #[test]
    fn assistant_message() {
        let msg = Message::assistant("Hi there");
        assert_eq!(msg.role, Role::Assistant);
        assert_eq!(msg.content, "Hi there");
        assert!(msg.tool_call_id.is_none());
        assert!(msg.tool_calls.is_empty());
    }

    #[test]
    fn assistant_with_tool_call_message() {
        let call = ToolCallRef {
            id: "call_abc".to_string(),
            name: "search".to_string(),
            args: serde_json::json!({"q": "rust"}),
        };
        let msg = Message::assistant_with_tool_call("Let me search that.", call);
        assert_eq!(msg.role, Role::Assistant);
        assert_eq!(msg.content, "Let me search that.");
        assert!(msg.tool_call_id.is_none());
        assert_eq!(msg.tool_calls.len(), 1);
        assert_eq!(msg.tool_calls[0].id, "call_abc");
        assert_eq!(msg.tool_calls[0].name, "search");
        assert_eq!(msg.tool_calls[0].args, serde_json::json!({"q": "rust"}));
    }

    #[test]
    fn tool_result_message() {
        let msg = Message::tool_result("call_abc", "search result here");
        assert_eq!(msg.role, Role::Tool);
        assert_eq!(msg.content, "search result here");
        assert_eq!(msg.tool_call_id.as_deref(), Some("call_abc"));
        assert!(msg.tool_calls.is_empty());
    }

    #[test]
    fn tool_result_accepts_string() {
        let id = String::from("call_xyz");
        let msg = Message::tool_result(id, "output");
        assert_eq!(msg.tool_call_id.as_deref(), Some("call_xyz"));
    }
}
