use std::sync::Arc;

use async_trait::async_trait;
use serde_json::json;

use crate::storage::Storage;
use crate::tools::traits::{Tool, ToolResult};

pub struct ChatManagerTool {
    storage: Arc<Storage>,
    available_channels: Vec<String>,
}

impl ChatManagerTool {
    pub fn new(storage: Arc<Storage>, available_channels: Vec<String>) -> Self {
        Self {
            storage,
            available_channels,
        }
    }
}

#[async_trait]
impl Tool for ChatManagerTool {
    fn name(&self) -> &str {
        "chat_manager"
    }

    fn description(&self) -> &str {
        "聊天管理工具。可以列出全部 session、可用的 channel，以及查看指定 session 的消息内容，支持时间范围筛选和分页翻页。\
action 可选值: list_sessions (列出全部会话), list_channels (列出可用渠道), list_messages (查看消息)"
    }

    fn parameters_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "action": {
                    "type": "string",
                    "enum": ["list_sessions", "list_channels", "list_messages"],
                    "description": "操作类型: list_sessions 列出全部会话, list_channels 列出可用渠道, list_messages 查看指定会话的消息"
                },
                "session_id": {
                    "type": "string",
                    "description": "会话 ID，格式 channel:chat_id:dialog_id，仅在 action 为 list_messages 时必填"
                },
                "count": {
                    "type": "integer",
                    "description": "获取数量，在 action 为 list_sessions 或 list_messages 时有效，默认 20，最大 100"
                },
                "offset": {
                    "type": "integer",
                    "description": "跳过前 N 条（用于翻页），在 action 为 list_sessions 或 list_messages 时有效，默认 0"
                },
                "before_time": {
                    "type": "integer",
                    "description": "Unix 时间戳（秒），仅返回此时间之前的消息，仅在 action 为 list_messages 时有效"
                },
                "after_time": {
                    "type": "integer",
                    "description": "Unix 时间戳（秒），仅返回此时间之后的消息，仅在 action 为 list_messages 时有效"
                }
            },
            "required": ["action"]
        })
    }

    fn read_only(&self) -> bool {
        true
    }

    fn concurrency_safe(&self) -> bool {
        true
    }

    async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
        let action = args["action"]
            .as_str()
            .ok_or_else(|| anyhow::anyhow!("missing required parameter: action"))?;

        match action {
            "list_channels" => self.list_channels().await,
            "list_sessions" => self.list_sessions(&args).await,
            "list_messages" => self.list_messages(&args).await,
            _ => Ok(ToolResult {
                success: false,
                output: String::new(),
                error: Some(format!(
                    "Unknown action: {}. Supported: list_sessions, list_channels, list_messages",
                    action
                )),
            }),
        }
    }
}

impl ChatManagerTool {
    async fn list_channels(&self) -> anyhow::Result<ToolResult> {
        let channels = self.available_channels.join(", ");
        Ok(ToolResult {
            success: true,
            output: format!("可用渠道 ({}): {}", self.available_channels.len(), channels),
            error: None,
        })
    }

    async fn list_sessions(&self, args: &serde_json::Value) -> anyhow::Result<ToolResult> {
        let count = args["count"].as_i64().unwrap_or(20).clamp(1, 100);
        let offset = args["offset"].as_i64().unwrap_or(0).max(0);

        let (sessions, total) = self
            .storage
            .query_sessions_range(offset, count)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to list sessions: {}", e))?;

        if sessions.is_empty() {
            return Ok(ToolResult {
                success: true,
                output: "当前没有会话".to_string(),
                error: None,
            });
        }

        let now_ms = chrono::Utc::now().timestamp_millis();
        let start_num = offset + 1;
        let end_num = offset + sessions.len() as i64;

        let mut output = format!(
            "全部会话 (共 {} 个，第 {}-{} 个):\n",
            total, start_num, end_num
        );

        for s in &sessions {
            let ago = format_duration_ago(now_ms - s.last_active_at);
            output.push_str(&format!(
                "- {}\n  title={} | channel={} chat_id={} | {}条消息 | 最后活动: {}前\n",
                s.id, s.title, s.channel, s.chat_id, s.message_count, ago
            ));
        }

        Ok(ToolResult {
            success: true,
            output,
            error: None,
        })
    }

    async fn list_messages(&self, args: &serde_json::Value) -> anyhow::Result<ToolResult> {
        let session_id = args["session_id"]
            .as_str()
            .ok_or_else(|| anyhow::anyhow!("missing required parameter: session_id"))?;

        let count = args["count"].as_i64().unwrap_or(20).clamp(1, 100);
        let offset = args["offset"].as_i64().unwrap_or(0).max(0);

        let before_time = args["before_time"].as_i64().map(|t| t * 1000);
        let after_time = args["after_time"].as_i64().map(|t| t * 1000);

        let session = self
            .storage
            .get_session(session_id)
            .await
            .map_err(|e| anyhow::anyhow!("Session not found: {}", e))?;

        let (messages, total) = self
            .storage
            .query_messages_range(session_id, before_time, after_time, offset, count)
            .await
            .map_err(|e| anyhow::anyhow!("Failed to load messages: {}", e))?;

        let start_num = offset + 1;
        let end_num = offset + messages.len() as i64;

        let range_desc = if messages.is_empty() {
            "无消息".to_string()
        } else {
            format!("第 {}-{} 条", start_num, end_num)
        };

        let filter_desc = match (before_time, after_time) {
            (Some(_), Some(_)) => "（已按时间范围筛选）",
            (Some(_), None) => "（已按截止时间筛选）",
            (None, Some(_)) => "（已按起始时间筛选）",
            (None, None) => "",
        };

        let mut output = format!(
            "会话: {} ({})\n--- 消息 {} / 共 {} 条 {} ---\n",
            session_id, session.title, range_desc, total, filter_desc
        );

        if messages.is_empty() {
            output.push_str("(暂无消息)\n");
        } else {
            for m in &messages {
                let time = format_timestamp(m.created_at);
                let role_tag = match m.role.as_str() {
                    "user" => "user     ",
                    "assistant" => "assistant",
                    "tool" => "tool     ",
                    "system" => "system   ",
                    other => other,
                };
                let preview = truncate_content(&m.content, 200);
                output.push_str(&format!(
                    "[{}] {} | {} | {}\n",
                    m.seq, time, role_tag, preview
                ));
            }
        }

        Ok(ToolResult {
            success: true,
            output,
            error: None,
        })
    }
}

fn format_duration_ago(millis: i64) -> String {
    let secs = millis / 1000;
    if secs < 60 {
        format!("{}秒", secs)
    } else if secs < 3600 {
        format!("{}分钟", secs / 60)
    } else if secs < 86400 {
        format!("{}小时", secs / 3600)
    } else {
        format!("{}天", secs / 86400)
    }
}

fn format_timestamp(ms: i64) -> String {
    if let Some(dt) = chrono::DateTime::from_timestamp_millis(ms) {
        dt.format("%m-%d %H:%M").to_string()
    } else {
        ms.to_string()
    }
}

fn truncate_content(content: &str, max_len: usize) -> String {
    let content = content.replace('\n', " ");
    if content.chars().count() > max_len {
        format!("{}...", content.chars().take(max_len).collect::<String>())
    } else {
        content
    }
}

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

    async fn create_test_storage() -> (Arc<Storage>, TempDir) {
        let dir = tempfile::tempdir().unwrap();
        let db_path = dir.path().join("test.db");
        let storage = Storage::new(&db_path).await.unwrap();
        (Arc::new(storage), dir)
    }

    #[tokio::test]
    async fn test_list_channels() {
        let (storage, _dir) = create_test_storage().await;
        let tool = ChatManagerTool::new(storage, vec!["cli_chat".into(), "feishu".into()]);

        let result = tool
            .execute(json!({ "action": "list_channels" }))
            .await
            .unwrap();
        assert!(result.success);
        assert!(result.output.contains("cli_chat"));
        assert!(result.output.contains("feishu"));
    }

    #[tokio::test]
    async fn test_list_sessions_empty() {
        let (storage, _dir) = create_test_storage().await;
        let tool = ChatManagerTool::new(storage, vec![]);

        let result = tool
            .execute(json!({ "action": "list_sessions" }))
            .await
            .unwrap();
        assert!(result.success);
        assert!(result.output.contains("没有"));
    }

    #[tokio::test]
    async fn test_list_sessions_with_data() {
        let (storage, _dir) = create_test_storage().await;

        let now = chrono::Utc::now().timestamp_millis();
        for i in 0..3 {
            let meta = crate::storage::session::SessionMeta {
                id: format!("cli_chat:sid{}:dialog{}", i, i),
                channel: "cli_chat".to_string(),
                chat_id: format!("sid{}", i),
                dialog_id: format!("dialog{}", i),
                title: format!("会话{}", i),
                created_at: now - i * 3_600_000,
                last_active_at: now - i * 3_600_000,
                message_count: i * 5,
                routing_info: None,
                archived_at: None,
                deleted_at: None,
                last_consolidated_at: None,
                last_compressed_message_at: None,
            };
            storage.upsert_session(&meta).await.unwrap();
        }

        let tool = ChatManagerTool::new(storage, vec![]);

        let result = tool
            .execute(json!({ "action": "list_sessions" }))
            .await
            .unwrap();
        assert!(result.success);
        assert!(result.output.contains("会话0"));
        assert!(result.output.contains("会话1"));
        assert!(result.output.contains("会话2"));
    }

    #[tokio::test]
    async fn test_list_messages_default() {
        let (storage, _dir) = create_test_storage().await;

        let now = chrono::Utc::now().timestamp_millis();
        let session_id = "cli_chat:sid0:dialog0";
        let meta = crate::storage::session::SessionMeta {
            id: session_id.to_string(),
            channel: "cli_chat".to_string(),
            chat_id: "sid0".to_string(),
            dialog_id: "dialog0".to_string(),
            title: "测试会话".to_string(),
            created_at: now,
            last_active_at: now,
            message_count: 3,
            routing_info: None,
            archived_at: None,
            deleted_at: None,
            last_consolidated_at: None,
            last_compressed_message_at: None,
        };
        storage.upsert_session(&meta).await.unwrap();

        for i in 0..3 {
            let msg = crate::storage::message::MessageMeta {
                id: format!("msg{}", i),
                session_id: session_id.to_string(),
                seq: i + 1,
                role: if i == 0 {
                    "user".to_string()
                } else {
                    "assistant".to_string()
                },
                content: format!("消息内容 {}", i),
                reasoning_content: None,
                provider_state: None,
                turn_id: None,
                iteration: None,
                completion_status: crate::bus::CompletionStatus::Completed,
                client_visibility: crate::bus::ClientVisibility::Visible,
                turn_origin: crate::bus::TurnOrigin::User,
                media_refs: None,
                tool_call_id: None,
                tool_name: None,
                tool_calls: None,
                source: None,
                created_at: now + i * 1000,
            };
            storage.append_message(session_id, &msg).await.unwrap();
        }

        let tool = ChatManagerTool::new(storage, vec![]);

        let result = tool
            .execute(json!({ "action": "list_messages", "session_id": session_id }))
            .await
            .unwrap();
        assert!(result.success);
        assert!(result.output.contains("消息内容 0"));
        assert!(result.output.contains("消息内容 2"));
        assert!(result.output.contains("测试会话"));
        assert!(result.output.contains("共 3 条"));
        // Verify ascending order: seq 1 before seq 3
        let pos_0 = result.output.find("消息内容 0").unwrap();
        let pos_2 = result.output.find("消息内容 2").unwrap();
        assert!(pos_0 < pos_2, "Messages should be in ascending order");
    }

    #[tokio::test]
    async fn test_list_messages_with_pagination() {
        let (storage, _dir) = create_test_storage().await;

        let now = chrono::Utc::now().timestamp_millis();
        let session_id = "cli_chat:sid0:dialog0";
        let meta = crate::storage::session::SessionMeta {
            id: session_id.to_string(),
            channel: "cli_chat".to_string(),
            chat_id: "sid0".to_string(),
            dialog_id: "dialog0".to_string(),
            title: "分页测试".to_string(),
            created_at: now,
            last_active_at: now,
            message_count: 5,
            routing_info: None,
            archived_at: None,
            deleted_at: None,
            last_consolidated_at: None,
            last_compressed_message_at: None,
        };
        storage.upsert_session(&meta).await.unwrap();

        for i in 0..5 {
            let msg = crate::storage::message::MessageMeta {
                id: format!("msg{}", i),
                session_id: session_id.to_string(),
                seq: i + 1,
                role: if i % 2 == 0 {
                    "user".to_string()
                } else {
                    "assistant".to_string()
                },
                content: format!("消息内容 {}", i),
                reasoning_content: None,
                provider_state: None,
                turn_id: None,
                iteration: None,
                completion_status: crate::bus::CompletionStatus::Completed,
                client_visibility: crate::bus::ClientVisibility::Visible,
                turn_origin: crate::bus::TurnOrigin::User,
                media_refs: None,
                tool_call_id: None,
                tool_name: None,
                tool_calls: None,
                source: None,
                created_at: now + i * 1000,
            };
            storage.append_message(session_id, &msg).await.unwrap();
        }

        let tool = ChatManagerTool::new(storage, vec![]);

        // offset=2, count=2 => should return messages 2,3 (seq 3,4)
        let result = tool
            .execute(json!({ "action": "list_messages", "session_id": session_id, "offset": 2, "count": 2 }))
            .await
            .unwrap();
        assert!(result.success);
        assert!(result.output.contains("第 3-4 条"));
        assert!(result.output.contains("消息内容 2"));
        assert!(result.output.contains("消息内容 3"));
        assert!(!result.output.contains("消息内容 0"));
        assert!(result.output.contains("共 5 条"));
    }

    #[tokio::test]
    async fn test_list_messages_with_time_range() {
        let (storage, _dir) = create_test_storage().await;

        let now = chrono::Utc::now().timestamp_millis();
        let session_id = "cli_chat:sid0:dialog0";
        let meta = crate::storage::session::SessionMeta {
            id: session_id.to_string(),
            channel: "cli_chat".to_string(),
            chat_id: "sid0".to_string(),
            dialog_id: "dialog0".to_string(),
            title: "时间范围测试".to_string(),
            created_at: now,
            last_active_at: now,
            message_count: 5,
            routing_info: None,
            archived_at: None,
            deleted_at: None,
            last_consolidated_at: None,
            last_compressed_message_at: None,
        };
        storage.upsert_session(&meta).await.unwrap();

        for i in 0..5 {
            let msg = crate::storage::message::MessageMeta {
                id: format!("msg{}", i),
                session_id: session_id.to_string(),
                seq: i + 1,
                role: "user".to_string(),
                content: format!("消息内容 {}", i),
                reasoning_content: None,
                provider_state: None,
                turn_id: None,
                iteration: None,
                completion_status: crate::bus::CompletionStatus::Completed,
                client_visibility: crate::bus::ClientVisibility::Visible,
                turn_origin: crate::bus::TurnOrigin::User,
                media_refs: None,
                tool_call_id: None,
                tool_name: None,
                tool_calls: None,
                source: None,
                created_at: now + i * 1000,
            };
            storage.append_message(session_id, &msg).await.unwrap();
        }

        let tool = ChatManagerTool::new(storage, vec![]);

        // after_time: filter to messages after msg1's second boundary
        let after_ts = now / 1000 + 2;
        let result = tool
            .execute(json!({ "action": "list_messages", "session_id": session_id, "after_time": after_ts }))
            .await
            .unwrap();
        assert!(result.success);
        assert!(result.output.contains("已按起始时间筛选"));
        assert!(!result.output.contains("消息内容 0"));
        assert!(!result.output.contains("消息内容 1"));
        assert!(result.output.contains("消息内容 3"));
        assert!(result.output.contains("消息内容 4"));
    }

    #[tokio::test]
    async fn test_unknown_action() {
        let (storage, _dir) = create_test_storage().await;
        let tool = ChatManagerTool::new(storage, vec![]);

        let result = tool.execute(json!({ "action": "unknown" })).await.unwrap();
        assert!(!result.success);
        assert!(result.error.unwrap().contains("Unknown action"));
    }
}
