#[cfg(test)]
use crate::bus::ChatMessage;
use crate::bus::OutboundMessage;
use crate::bus::message::OutboundEventKind;
#[cfg(test)]
use crate::bus::message::{ToolMessageState, format_tool_call_content};

use super::{MediaSummary, WsOutbound};

const TOOL_PENDING_RESUME_HINT: &str = "完成外部操作后，直接发一条继续消息即可。";

#[cfg(test)]
pub(crate) fn ws_outbound_from_chat_message(message: &ChatMessage) -> Vec<WsOutbound> {
    match message.role.as_str() {
        "assistant" => {
            if let Some(tool_calls) = &message.tool_calls {
                let mut outbound = Vec::new();
                let has_content_or_reasoning = !message.content.trim().is_empty() || message.reasoning_content.is_some();
                if has_content_or_reasoning {
                    outbound.push(WsOutbound::AssistantResponse {
                        id: message.id.clone(),
                        content: message.content.clone(),
                        role: message.role.clone(),
                        attachments: Vec::new(),
                        subagent_task_id: None,
                        topic_id: None,
                        timestamp: None,
                        reasoning_content: message.reasoning_content.clone(),
                    });
                }

                // AssistantResponse 已携带 reasoning 时，ToolCall 不再重复
                let tc_reasoning = if has_content_or_reasoning { None } else { message.reasoning_content.clone() };
                outbound.extend(tool_calls.iter().map(|tool_call| WsOutbound::ToolCall {
                    id: tool_call.id.clone(),
                    tool_call_id: tool_call.id.clone(),
                    tool_name: tool_call.name.clone(),
                    arguments: tool_call.arguments.clone(),
                    content: format_tool_call_content(&tool_call.name, &tool_call.arguments),
                    role: message.role.clone(),
                    subagent_task_id: None,
                    topic_id: None,
                    timestamp: None,
                    reasoning_content: tc_reasoning.clone(),
                }));
                outbound
            } else {
                vec![WsOutbound::AssistantResponse {
                    id: message.id.clone(),
                    content: message.content.clone(),
                    role: message.role.clone(),
                    attachments: Vec::new(),
                    subagent_task_id: None,
                    topic_id: None,
                    timestamp: None,
                    reasoning_content: message.reasoning_content.clone(),
                }]
            }
        }
        "tool" => match message
            .tool_state
            .as_ref()
            .unwrap_or(&ToolMessageState::Completed)
        {
            ToolMessageState::Completed => vec![WsOutbound::ToolResult {
                id: message.tool_call_id.clone().unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
                tool_call_id: message.tool_call_id.clone().unwrap_or_default(),
                tool_name: message.tool_name.clone().unwrap_or_default(),
                content: message.content.clone(),
                role: message.role.clone(),
                subagent_task_id: None,
                topic_id: None,
                duration_ms: None,
                timestamp: None,
            }],
            ToolMessageState::PendingUserAction => vec![WsOutbound::ToolPending {
                id: message.tool_call_id.clone().unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
                tool_call_id: message.tool_call_id.clone().unwrap_or_default(),
                tool_name: message.tool_name.clone().unwrap_or_default(),
                content: message.content.clone(),
                role: message.role.clone(),
                resume_hint: TOOL_PENDING_RESUME_HINT.to_string(),
                subagent_task_id: None,
                topic_id: None,
                timestamp: None,
            }],
        },
        _ => Vec::new(),
    }
}

pub(crate) fn ws_outbound_from_outbound_message(message: &OutboundMessage) -> Vec<WsOutbound> {
    match message.event_kind {
        OutboundEventKind::AssistantResponse | OutboundEventKind::SchedulerNotification => {
            let attachments: Vec<MediaSummary> = message
                .media
                .iter()
                .map(|m| MediaSummary {
                    path: m.path.clone(),
                    media_type: m.media_type.clone(),
                    mime_type: m.mime_type.clone(),
                    content_base64: m.content_base64.clone(),
                    file_name: m.file_name.clone(),
                })
                .collect();
            vec![WsOutbound::AssistantResponse {
                id: uuid::Uuid::new_v4().to_string(),
                content: message.content.clone(),
                role: message.role.clone(),
                attachments,
                subagent_task_id: message.metadata.get("subagent_task_id").cloned(),
                topic_id: message.metadata.get("topic_id").cloned(),
                timestamp: Some(crate::protocol::now_timestamp()),
                reasoning_content: message.reasoning_content.clone(),
            }]
        }
        OutboundEventKind::ToolCall => vec![WsOutbound::ToolCall {
            id: message
                .tool_call_id
                .clone()
                .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
            tool_call_id: message.tool_call_id.clone().unwrap_or_default(),
            tool_name: message.tool_name.clone().unwrap_or_default(),
            arguments: message
                .tool_arguments
                .clone()
                .unwrap_or(serde_json::Value::Null),
            content: message.content.clone(),
            role: message.role.clone(),
            subagent_task_id: message.metadata.get("subagent_task_id").cloned(),
            topic_id: message.metadata.get("topic_id").cloned(),
            timestamp: Some(crate::protocol::now_timestamp()),
            reasoning_content: message.reasoning_content.clone(),
        }],
        OutboundEventKind::ToolResult => vec![WsOutbound::ToolResult {
            id: message
                .tool_call_id
                .clone()
                .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
            tool_call_id: message.tool_call_id.clone().unwrap_or_default(),
            tool_name: message.tool_name.clone().unwrap_or_default(),
            content: message.content.clone(),
            role: message.role.clone(),
            subagent_task_id: message.metadata.get("subagent_task_id").cloned(),
            topic_id: message.metadata.get("topic_id").cloned(),
            duration_ms: message
                .metadata
                .get("tool_duration_ms")
                .and_then(|v| v.parse().ok()),
            timestamp: Some(crate::protocol::now_timestamp()),
        }],
        OutboundEventKind::ToolPending => vec![WsOutbound::ToolPending {
            id: message
                .tool_call_id
                .clone()
                .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()),
            tool_call_id: message.tool_call_id.clone().unwrap_or_default(),
            tool_name: message.tool_name.clone().unwrap_or_default(),
            content: message.content.clone(),
            role: message.role.clone(),
            resume_hint: TOOL_PENDING_RESUME_HINT.to_string(),
            subagent_task_id: message.metadata.get("subagent_task_id").cloned(),
            topic_id: message.metadata.get("topic_id").cloned(),
            timestamp: Some(crate::protocol::now_timestamp()),
        }],
        OutboundEventKind::ErrorNotification => vec![WsOutbound::Error {
            code: "AGENT_ERROR".to_string(),
            message: message.content.clone(),
            timestamp: Some(crate::protocol::now_timestamp()),
        }],
        OutboundEventKind::TaskStarted => vec![WsOutbound::TaskStarted {
            task_id: message.metadata.get("task_id").cloned().unwrap_or_default(),
            description: message.metadata.get("task_description").cloned().unwrap_or_default(),
            subagent_type: message.metadata.get("task_subagent_type").cloned().unwrap_or_default(),
            topic_id: message.metadata.get("topic_id").cloned(),
        }],
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::domain::messages::ToolCall;
    use serde_json::json;

    #[test]
    fn test_ws_outbound_from_chat_message_expands_tool_calls() {
        let message = ChatMessage::assistant_with_tool_calls(
            "",
            vec![ToolCall {
                id: "call-1".to_string(),
                name: "calculator".to_string(),
                arguments: json!({"expression": "1 + 1"}),
            }],
        );

        let outbound = ws_outbound_from_chat_message(&message);

        assert_eq!(outbound.len(), 1);
        match &outbound[0] {
            WsOutbound::ToolCall {
                tool_call_id,
                tool_name,
                arguments,
                content,
                ..
            } => {
                assert_eq!(tool_call_id, "call-1");
                assert_eq!(tool_name, "calculator");
                assert_eq!(arguments["expression"], "1 + 1");
                assert_eq!(content, "calculator\nargs: {\"expression\":\"1 + 1\"}");
            }
            other => panic!("unexpected outbound variant: {:?}", other),
        }
    }

    #[test]
    fn test_ws_outbound_keeps_assistant_content_when_tool_calls_exist() {
        let message = ChatMessage::assistant_with_tool_calls(
            "日报已整理完成。",
            vec![ToolCall {
                id: "call-1".to_string(),
                name: "memory_manage".to_string(),
                arguments: json!({"action": "put"}),
            }],
        );

        let outbound = ws_outbound_from_chat_message(&message);

        assert_eq!(outbound.len(), 2);
        assert!(matches!(outbound[0], WsOutbound::AssistantResponse { .. }));
        assert!(matches!(outbound[1], WsOutbound::ToolCall { .. }));
    }

    #[test]
    fn test_ws_outbound_from_chat_message_includes_tool_results() {
        let message = ChatMessage::tool("call-1", "calculator", "2");

        let outbound = ws_outbound_from_chat_message(&message);

        assert_eq!(outbound.len(), 1);
        assert!(matches!(outbound[0], WsOutbound::ToolResult { .. }));
    }

    #[test]
    fn test_ws_outbound_from_chat_message_includes_tool_pending() {
        let message = ChatMessage::tool_with_state(
            "call-1",
            "bash",
            "等待你完成授权后再继续。",
            ToolMessageState::PendingUserAction,
        );

        let outbound = ws_outbound_from_chat_message(&message);

        assert_eq!(outbound.len(), 1);
        assert!(matches!(outbound[0], WsOutbound::ToolPending { .. }));
    }

    #[test]
    fn test_ws_outbound_from_outbound_message_maps_tool_call() {
        let message = OutboundMessage::tool_call(
            "cli",
            "session-1",
            None, // session_id
            "call-1",
            "calculator",
            json!({"expression": "1 + 1"}),
            None,
            Default::default(),
        );

        let outbound = ws_outbound_from_outbound_message(&message);

        assert_eq!(outbound.len(), 1);
        match &outbound[0] {
            WsOutbound::ToolCall {
                tool_call_id,
                tool_name,
                arguments,
                content,
                ..
            } => {
                assert_eq!(tool_call_id, "call-1");
                assert_eq!(tool_name, "calculator");
                assert_eq!(arguments["expression"], "1 + 1");
                assert_eq!(content, "calculator\nargs: {\"expression\":\"1 + 1\"}");
            }
            other => panic!("unexpected outbound variant: {:?}", other),
        }
    }
}
