use super::traits::{Tool, ToolResult};
use async_trait::async_trait;
use serde_json::json;
use std::sync::Arc;

use crate::memory::{MemoryCategory, MemoryManager};

// ── MemoryStoreTool ──────────────────────────────────────────────────

pub struct MemoryStoreTool {
    memory: Arc<MemoryManager>,
}

impl MemoryStoreTool {
    pub fn new(memory: Arc<MemoryManager>) -> Self {
        Self { memory }
    }
}

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

    fn description(&self) -> &str {
        "Store a fact, preference, or insight into long-term knowledge memory. \
         Use this when the user shares important information you should remember. \
         Provide a descriptive key (e.g., 'user_prefers_python', 'project_auth_approach') \
         and the full content to remember."
    }

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

    fn parameters_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "key": {
                    "type": "string",
                    "description": "Semantic identifier for this memory (e.g., 'user_language_pref'). Unique key."
                },
                "content": {
                    "type": "string",
                    "description": "The full content of the memory entry."
                },
                "importance": {
                    "type": "number",
                    "description": "Importance score 0.0-1.0. Higher = more important. Use 0.8+ for critical facts, 0.5 for general info."
                }
            },
            "required": ["key", "content"]
        })
    }

    async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
        let key = args
            .get("key")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing required parameter: key"))?;

        let content = args
            .get("content")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing required parameter: content"))?;

        let importance = args.get("importance").and_then(|v| v.as_f64());

        self.memory
            .store(key, content, MemoryCategory::Knowledge, None, importance)
            .await?;

        Ok(ToolResult {
            success: true,
            output: format!("Memory stored: {}", key),
            error: None,
        })
    }
}

// ── MemoryRecallTool ─────────────────────────────────────────────────

pub struct MemoryRecallTool {
    memory: Arc<MemoryManager>,
}

impl MemoryRecallTool {
    pub fn new(memory: Arc<MemoryManager>) -> Self {
        Self { memory }
    }
}

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

    fn description(&self) -> &str {
        "Search and retrieve entries from long-term knowledge memory using keyword matching. \
         Use this to recall previously stored facts, preferences, or insights. \
         IMPORTANT: query must be a space-separated list of RELEVANT KEYWORDS (not a question or sentence). \
         Use multiple synonymous or related terms to increase recall. \
         Example: instead of 'what is the user location', use 'user location address city residence'. \
         Supports optional time-range filtering via since/until (Unix ms)."
    }

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

    fn parameters_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Space-separated KEYWORDS for memory search (NOT a natural language question). Use multiple related terms for better recall, e.g. 'address city location residence'."
                },
                "since": {
                    "type": "integer",
                    "description": "Start of time range (Unix milliseconds)."
                },
                "until": {
                    "type": "integer",
                    "description": "End of time range (Unix milliseconds)."
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results to return (default 10)."
                }
            },
            "required": ["query"]
        })
    }

    async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
        let query = args
            .get("query")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing required parameter: query"))?;

        let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;

        let entries = if args.get("since").is_some() || args.get("until").is_some() {
            let since = args.get("since").and_then(|v| v.as_i64()).unwrap_or(0);
            let until = args
                .get("until")
                .and_then(|v| v.as_i64())
                .unwrap_or(chrono::Utc::now().timestamp_millis());
            self.memory
                .recall_by_time(
                    since,
                    until,
                    Some(query),
                    limit,
                    Some(MemoryCategory::Knowledge),
                    None,
                )
                .await?
        } else {
            self.memory
                .recall(query, limit, Some(MemoryCategory::Knowledge), None)
                .await?
        };

        if entries.is_empty() {
            return Ok(ToolResult {
                success: true,
                output: "No matching memories found.".to_string(),
                error: None,
            });
        }

        let formatted = entries
            .iter()
            .map(|e| {
                let session = e
                    .session_id
                    .as_deref()
                    .map(|s| format!(" [session: {}]", s))
                    .unwrap_or_default();
                format!(
                    "- {} [{}]{} [importance: {:.1}]: {}",
                    e.key,
                    e.category.as_str(),
                    session,
                    e.importance,
                    e.content
                )
            })
            .collect::<Vec<_>>()
            .join("\n");

        Ok(ToolResult {
            success: true,
            output: format!("Found {} memories:\n{}", entries.len(), formatted),
            error: None,
        })
    }
}

// ── TimelineRecallTool ────────────────────────────────────────────────

pub struct TimelineRecallTool {
    memory: Arc<MemoryManager>,
}

impl TimelineRecallTool {
    pub fn new(memory: Arc<MemoryManager>) -> Self {
        Self { memory }
    }
}

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

    fn description(&self) -> &str {
        "Search and retrieve conversation summaries from timeline memory. \
         Use this to recall what was discussed in past sessions or earlier in the current session. \
         Optionally filter by session_id to scope to a specific conversation. \
         IMPORTANT: query must be a space-separated list of RELEVANT KEYWORDS (not a question or sentence)."
    }

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

    fn parameters_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Space-separated KEYWORDS for timeline search (NOT a natural language question). Use multiple related terms for better recall."
                },
                "session_id": {
                    "type": "string",
                    "description": "Filter to a specific session (format: channel:chat_id:dialog_id). Omit to search across all sessions."
                },
                "since": {
                    "type": "integer",
                    "description": "Start of time range (Unix milliseconds)."
                },
                "until": {
                    "type": "integer",
                    "description": "End of time range (Unix milliseconds)."
                },
                "limit": {
                    "type": "integer",
                    "description": "Max results to return (default 10)."
                }
            },
            "required": ["query"]
        })
    }

    async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
        let query = args
            .get("query")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing required parameter: query"))?;

        let session_id = args.get("session_id").and_then(|v| v.as_str());

        let limit = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(10) as usize;

        let entries = if args.get("since").is_some() || args.get("until").is_some() {
            let since = args.get("since").and_then(|v| v.as_i64()).unwrap_or(0);
            let until = args
                .get("until")
                .and_then(|v| v.as_i64())
                .unwrap_or(chrono::Utc::now().timestamp_millis());
            self.memory
                .recall_by_time(
                    since,
                    until,
                    Some(query),
                    limit,
                    Some(MemoryCategory::Timeline),
                    session_id,
                )
                .await?
        } else {
            self.memory
                .recall(query, limit, Some(MemoryCategory::Timeline), session_id)
                .await?
        };

        if entries.is_empty() {
            return Ok(ToolResult {
                success: true,
                output: "No matching timeline entries found.".to_string(),
                error: None,
            });
        }

        let formatted = entries
            .iter()
            .map(|e| {
                let session = e
                    .session_id
                    .as_deref()
                    .map(|s| format!(" [session: {}]", s))
                    .unwrap_or_default();
                format!(
                    "- {} [{}]{} [importance: {:.1}]: {}",
                    e.key,
                    e.category.as_str(),
                    session,
                    e.importance,
                    e.content
                )
            })
            .collect::<Vec<_>>()
            .join("\n");

        Ok(ToolResult {
            success: true,
            output: format!("Found {} timeline entries:\n{}", entries.len(), formatted),
            error: None,
        })
    }
}

// ── MemoryForgetTool ─────────────────────────────────────────────────

pub struct MemoryForgetTool {
    memory: Arc<MemoryManager>,
}

impl MemoryForgetTool {
    pub fn new(memory: Arc<MemoryManager>) -> Self {
        Self { memory }
    }
}

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

    fn description(&self) -> &str {
        "Delete a memory entry by its key. Use this when information is outdated, \
         incorrect, or the user asks to forget something."
    }

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

    fn parameters_schema(&self) -> serde_json::Value {
        json!({
            "type": "object",
            "properties": {
                "key": {
                    "type": "string",
                    "description": "The key of the memory entry to delete."
                }
            },
            "required": ["key"]
        })
    }

    async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
        let key = args
            .get("key")
            .and_then(|v| v.as_str())
            .ok_or_else(|| anyhow::anyhow!("Missing required parameter: key"))?;

        self.memory.forget(key).await?;

        Ok(ToolResult {
            success: true,
            output: format!("Memory deleted: {}", key),
            error: None,
        })
    }
}
