//! Flexible return types for tool, prompt, and resource execution
//!
//! The `IntoToolResult` trait enables ergonomic return types from tool handlers,
//! allowing direct returns of strings, content, structured data, or Result types.
//!
//! The `PromptResult` type provides similar ergonomics for prompt handlers.
//!
//! The `ResourceResult` type provides similar ergonomics for resource handlers.

use serde::Serialize;
use serde_json::Value;

use crate::content::prompt::PromptMessage;
use crate::content::resource::ResourceContent;
use crate::content::types::{Content, TextContent};
use crate::registry::prompts::{GetPromptResult, PromptError};
use crate::registry::resources::ResourceError;
use crate::registry::tools::{ToolError, ToolOutput};

/// Type alias for tool execution results
///
/// This is the canonical return type for tool handlers.
/// Returns either content (text, images) or structured JSON output.
pub type ToolResult = Result<ToolOutput, ToolError>;

/// Trait for types that can be converted to tool results
///
/// This trait enables flexible return types in tool handlers:
/// - `String` and `&str` automatically become `ToolOutput::Content` with `TextContent`
/// - `ToolOutput` passes through directly
/// - `Vec<Box<dyn Content>>` becomes `ToolOutput::Content`
/// - `serde_json::Value` becomes `ToolOutput::Structured`
///
/// # Example
///
/// ```rust,ignore
/// // All of these are valid returns from a tool:
///
/// // Direct string -> Content output
/// async fn tool_a(...) -> impl IntoToolResult {
///     "Hello, world!"
/// }
///
/// // ToolOutput directly
/// async fn tool_b(...) -> impl IntoToolResult {
///     ToolOutput::text("Hello")
/// }
///
/// // Structured output
/// async fn tool_c(...) -> ToolResult {
///     structured(MyOutput { value: 42 })
/// }
///
/// // Content vector
/// async fn tool_d(...) -> ToolResult {
///     Ok(ToolOutput::content(vec![text("Multiple"), text("Contents")]))
/// }
/// ```
pub trait IntoToolResult {
    /// Convert self into a ToolResult
    fn into_tool_result(self) -> ToolResult;
}

// Implementation for String -> Content
impl IntoToolResult for String {
    fn into_tool_result(self) -> ToolResult {
        Ok(ToolOutput::text(self))
    }
}

// Implementation for &str -> Content
impl IntoToolResult for &str {
    fn into_tool_result(self) -> ToolResult {
        Ok(ToolOutput::text(self))
    }
}

// Implementation for ToolOutput (passthrough)
impl IntoToolResult for ToolOutput {
    fn into_tool_result(self) -> ToolResult {
        Ok(self)
    }
}

// Implementation for Vec<Box<dyn Content>> -> Content
impl IntoToolResult for Vec<Box<dyn Content>> {
    fn into_tool_result(self) -> ToolResult {
        Ok(ToolOutput::Content(self))
    }
}

// Implementation for single Box<dyn Content> -> Content
impl IntoToolResult for Box<dyn Content> {
    fn into_tool_result(self) -> ToolResult {
        Ok(ToolOutput::Content(vec![self]))
    }
}

// Implementation for TextContent directly -> Content
impl IntoToolResult for TextContent {
    fn into_tool_result(self) -> ToolResult {
        Ok(ToolOutput::Content(
            vec![Box::new(self) as Box<dyn Content>],
        ))
    }
}

// Implementation for () -> empty Content
impl IntoToolResult for () {
    fn into_tool_result(self) -> ToolResult {
        Ok(ToolOutput::Content(vec![]))
    }
}

// Implementation for serde_json::Value -> Structured
impl IntoToolResult for Value {
    fn into_tool_result(self) -> ToolResult {
        Ok(ToolOutput::Structured(self))
    }
}

/// Helper function to create TextContent easily
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::text;
///
/// async fn my_tool(...) -> ToolResult {
///     Ok(ToolOutput::content(vec![text("Hello!"), text("World!")]))
/// }
/// ```
pub fn text(content: &str) -> Box<dyn Content> {
    Box::new(TextContent::new(content))
}

/// Helper function to create multiple text contents
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::texts;
///
/// async fn my_tool(...) -> ToolResult {
///     Ok(ToolOutput::content(texts(&["Line 1", "Line 2", "Line 3"])))
/// }
/// ```
pub fn texts(contents: &[&str]) -> Vec<Box<dyn Content>> {
    contents
        .iter()
        .map(|s| Box::new(TextContent::new(*s)) as Box<dyn Content>)
        .collect()
}

/// Helper function to create structured output from a serializable value
///
/// This is the ergonomic way to return structured output from a tool.
/// The value must implement `Serialize` and will be serialized to JSON.
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::structured;
/// use serde::Serialize;
///
/// #[derive(Serialize)]
/// struct WeatherOutput {
///     temperature: f64,
///     conditions: String,
/// }
///
/// async fn get_weather(...) -> ToolResult {
///     structured(WeatherOutput {
///         temperature: 22.5,
///         conditions: "Sunny".into(),
///     })
/// }
/// ```
pub fn structured<T: Serialize>(value: T) -> ToolResult {
    ToolOutput::structured(value).map_err(|e| ToolError::Internal(e.to_string()))
}

// ============================================================================
// Prompt Result Types and Helpers
// ============================================================================

/// Type alias for prompt execution results
///
/// This is the canonical return type for prompt handlers.
/// Returns either a GetPromptResult with messages or a PromptError.
pub type PromptResult = Result<GetPromptResult, PromptError>;

/// Helper function to create a prompt result with messages
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::{prompt_messages, user_message};
///
/// async fn my_prompt(...) -> PromptResult {
///     prompt_messages(vec![
///         user_message("Hello, how can I help?"),
///     ])
/// }
/// ```
pub fn prompt_messages(messages: Vec<PromptMessage>) -> PromptResult {
    Ok(GetPromptResult {
        description: None,
        messages,
    })
}

/// Helper function to create a prompt result with description and messages
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::{prompt_with_description, user_message};
///
/// async fn my_prompt(...) -> PromptResult {
///     prompt_with_description(
///         "A helpful greeting prompt",
///         vec![user_message("Hello!")],
///     )
/// }
/// ```
pub fn prompt_with_description(
    description: impl Into<String>,
    messages: Vec<PromptMessage>,
) -> PromptResult {
    Ok(GetPromptResult {
        description: Some(description.into()),
        messages,
    })
}

/// Helper function to create a user message
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::user_message;
///
/// let msg = user_message("Please analyze this code");
/// ```
pub fn user_message(text: impl Into<String>) -> PromptMessage {
    PromptMessage::user(text)
}

/// Helper function to create an assistant message
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::assistant_message;
///
/// let msg = assistant_message("I'll help you with that");
/// ```
pub fn assistant_message(text: impl Into<String>) -> PromptMessage {
    PromptMessage::assistant(text)
}

/// Helper function to create a user message with an image
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::user_image_message;
///
/// let msg = user_image_message(base64_data, "image/png");
/// ```
pub fn user_image_message(data: impl Into<String>, mime_type: impl Into<String>) -> PromptMessage {
    PromptMessage::user_image(data, mime_type)
}

/// Helper function to create an assistant message with an image
pub fn assistant_image_message(
    data: impl Into<String>,
    mime_type: impl Into<String>,
) -> PromptMessage {
    PromptMessage::assistant_image(data, mime_type)
}

// ============================================================================
// Resource Result Types and Helpers
// ============================================================================

/// Type alias for resource execution results
///
/// This is the canonical return type for resource handlers.
/// Returns a vector of ResourceContent items or a ResourceError.
pub type ResourceResult = Result<Vec<ResourceContent>, ResourceError>;

/// Helper function to create a text resource content
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::text_resource;
///
/// async fn my_resource(...) -> ResourceResult {
///     Ok(vec![text_resource("config:///app", "content here")])
/// }
/// ```
pub fn text_resource(uri: impl Into<String>, text: impl Into<String>) -> ResourceContent {
    ResourceContent::text(uri, text)
}

/// Helper function to create a text resource with MIME type
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::text_resource_with_mime;
///
/// async fn my_resource(...) -> ResourceResult {
///     Ok(vec![text_resource_with_mime("config:///app", "{}", "application/json")])
/// }
/// ```
pub fn text_resource_with_mime(
    uri: impl Into<String>,
    text: impl Into<String>,
    mime_type: impl Into<String>,
) -> ResourceContent {
    ResourceContent {
        uri: uri.into(),
        mime_type: Some(mime_type.into()),
        text: Some(text.into()),
        blob: None,
    }
}

/// Helper function to create a blob resource content
///
/// # Example
///
/// ```rust,ignore
/// use mcp_host::macros::blob_resource;
///
/// async fn my_resource(...) -> ResourceResult {
///     Ok(vec![blob_resource("file:///image.png", base64_data, "image/png")])
/// }
/// ```
pub fn blob_resource(
    uri: impl Into<String>,
    blob: impl Into<String>,
    mime_type: impl Into<String>,
) -> ResourceContent {
    ResourceContent {
        uri: uri.into(),
        mime_type: Some(mime_type.into()),
        text: None,
        blob: Some(blob.into()),
    }
}

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

    #[test]
    fn test_string_into_tool_result() {
        let result = String::from("test").into_tool_result();
        assert!(result.is_ok());
        assert!(result.unwrap().is_content());
    }

    #[test]
    fn test_str_into_tool_result() {
        let result = "test".into_tool_result();
        assert!(result.is_ok());
        assert!(result.unwrap().is_content());
    }

    #[test]
    fn test_empty_into_tool_result() {
        let result = ().into_tool_result();
        assert!(result.is_ok());
        assert!(result.unwrap().is_content());
    }

    #[test]
    fn test_vec_content_into_tool_result() {
        let contents: Vec<Box<dyn Content>> = vec![Box::new(TextContent::new("test"))];
        let result = contents.into_tool_result();
        assert!(result.is_ok());
        assert!(result.unwrap().is_content());
    }

    #[test]
    fn test_value_into_tool_result() {
        let value = serde_json::json!({"key": "value"});
        let result = value.into_tool_result();
        assert!(result.is_ok());
        assert!(result.unwrap().is_structured());
    }

    #[test]
    fn test_text_helper() {
        let content = text("hello");
        assert_eq!(content.content_type(), "text");
    }

    #[test]
    fn test_texts_helper() {
        let contents = texts(&["a", "b", "c"]);
        assert_eq!(contents.len(), 3);
    }

    #[test]
    fn test_structured_helper() {
        #[derive(Serialize)]
        struct TestOutput {
            value: i32,
        }

        let result = structured(TestOutput { value: 42 });
        assert!(result.is_ok());
        let output = result.unwrap();
        assert!(output.is_structured());

        if let ToolOutput::Structured(v) = output {
            assert_eq!(v["value"], 42);
        }
    }

    #[test]
    fn test_tool_output_passthrough() {
        let output = ToolOutput::text("hello");
        let result = output.into_tool_result();
        assert!(result.is_ok());
        assert!(result.unwrap().is_content());
    }

    // Prompt result tests
    #[test]
    fn test_prompt_messages() {
        let result = prompt_messages(vec![user_message("test")]);
        assert!(result.is_ok());
        let prompt_result = result.unwrap();
        assert!(prompt_result.description.is_none());
        assert_eq!(prompt_result.messages.len(), 1);
    }

    #[test]
    fn test_prompt_with_description() {
        let result = prompt_with_description("A test prompt", vec![user_message("hello")]);
        assert!(result.is_ok());
        let prompt_result = result.unwrap();
        assert_eq!(prompt_result.description, Some("A test prompt".to_string()));
        assert_eq!(prompt_result.messages.len(), 1);
    }

    #[test]
    fn test_user_message() {
        let msg = user_message("test");
        assert!(matches!(msg.role, crate::content::prompt::PromptRole::User));
    }

    #[test]
    fn test_assistant_message() {
        let msg = assistant_message("test");
        assert!(matches!(
            msg.role,
            crate::content::prompt::PromptRole::Assistant
        ));
    }

    // Resource result tests
    #[test]
    fn test_text_resource() {
        let content = text_resource("test:///uri", "content");
        assert_eq!(content.uri, "test:///uri");
        assert_eq!(content.text, Some("content".to_string()));
        assert!(content.blob.is_none());
    }

    #[test]
    fn test_text_resource_with_mime() {
        let content = text_resource_with_mime("test:///uri", "{}", "application/json");
        assert_eq!(content.uri, "test:///uri");
        assert_eq!(content.text, Some("{}".to_string()));
        assert_eq!(content.mime_type, Some("application/json".to_string()));
    }

    #[test]
    fn test_blob_resource() {
        let content = blob_resource("file:///img.png", "base64data", "image/png");
        assert_eq!(content.uri, "file:///img.png");
        assert_eq!(content.blob, Some("base64data".to_string()));
        assert_eq!(content.mime_type, Some("image/png".to_string()));
        assert!(content.text.is_none());
    }
}
