//! MCP tool handlers

use anyhow::Result;
use kogral_core::{
    models::{Edge, EdgeType, Graph, Node, NodeType, TaskStatus},
    storage::{filesystem::FilesystemStorage, Storage},
};
use serde_json::{json, Value};
use tracing::{error, info};

use crate::types::{Tool, ToolResult};
use crate::validation::{
    validate_enum, validate_limit, validate_node_id, validate_optional_string,
    validate_required_string, validate_strength, validate_string_array, MAX_ARRAY_ITEMS,
    MAX_SHORT_LENGTH, MAX_STRING_LENGTH,
};

/// Get all available tools
pub fn list_tools() -> Vec<Tool> {
    vec![
        Tool {
            name: "kogral/search".to_string(),
            description: "Search KOGRAL using text and/or semantic similarity".to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "Search query"
                    },
                    "type": {
                        "type": "string",
                        "enum": ["note", "decision", "guideline", "pattern", "journal", "execution", "all"],
                        "description": "Node type filter"
                    },
                    "project": {
                        "type": "string",
                        "description": "Limit to project graph"
                    },
                    "semantic": {
                        "type": "boolean",
                        "default": true,
                        "description": "Use semantic search (requires embeddings)"
                    },
                    "limit": {
                        "type": "integer",
                        "default": 10,
                        "description": "Maximum number of results"
                    }
                },
                "required": ["query"]
            }),
        },
        Tool {
            name: "kogral/add_note".to_string(),
            description: "Add a new note to KOGRAL".to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "title": {
                        "type": "string",
                        "description": "Note title"
                    },
                    "content": {
                        "type": "string",
                        "description": "Note content (markdown)"
                    },
                    "tags": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "Tags"
                    },
                    "relates_to": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "Related node IDs"
                    },
                    "project": {
                        "type": "string",
                        "description": "Project identifier"
                    }
                },
                "required": ["title", "content"]
            }),
        },
        Tool {
            name: "kogral/add_decision".to_string(),
            description: "Add an architectural decision record (ADR)".to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "title": {
                        "type": "string",
                        "description": "Decision title"
                    },
                    "context": {
                        "type": "string",
                        "description": "Problem context"
                    },
                    "decision": {
                        "type": "string",
                        "description": "Decision made"
                    },
                    "consequences": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "Consequences and impacts"
                    },
                    "tags": {
                        "type": "array",
                        "items": {"type": "string"},
                        "description": "Tags"
                    },
                    "project": {
                        "type": "string",
                        "description": "Project identifier"
                    }
                },
                "required": ["title", "decision"]
            }),
        },
        Tool {
            name: "kogral/link".to_string(),
            description: "Create a relationship between two nodes".to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "from": {
                        "type": "string",
                        "description": "Source node ID"
                    },
                    "to": {
                        "type": "string",
                        "description": "Target node ID"
                    },
                    "relation": {
                        "type": "string",
                        "enum": ["relates_to", "depends_on", "implements", "extends", "supersedes", "explains"],
                        "description": "Relationship type"
                    },
                    "strength": {
                        "type": "number",
                        "minimum": 0,
                        "maximum": 1,
                        "default": 1.0,
                        "description": "Relationship strength"
                    }
                },
                "required": ["from", "to", "relation"]
            }),
        },
        Tool {
            name: "kogral/get_guidelines".to_string(),
            description: "Get guidelines for current project with inheritance resolution"
                .to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "language": {
                        "type": "string",
                        "description": "Programming language (e.g., rust, nushell)"
                    },
                    "category": {
                        "type": "string",
                        "description": "Category (e.g., error-handling, testing)"
                    },
                    "include_base": {
                        "type": "boolean",
                        "default": true,
                        "description": "Include inherited base guidelines"
                    }
                }
            }),
        },
        Tool {
            name: "kogral/list_graphs".to_string(),
            description: "List available knowledge graphs".to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {}
            }),
        },
        Tool {
            name: "kogral/export".to_string(),
            description: "Export KOGRAL to external format".to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "format": {
                        "type": "string",
                        "enum": ["logseq", "markdown", "json", "summary"],
                        "description": "Export format"
                    },
                    "type": {
                        "type": "string",
                        "enum": ["note", "decision", "guideline", "pattern", "all"],
                        "description": "Node type filter"
                    }
                },
                "required": ["format"]
            }),
        },
        Tool {
            name: "kogral/find_blocks".to_string(),
            description: "Find blocks by tag, task status, or custom property".to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "tag": {
                        "type": "string",
                        "description": "Find blocks with this tag (e.g., 'card', 'important')"
                    },
                    "status": {
                        "type": "string",
                        "enum": ["TODO", "DOING", "DONE", "LATER", "NOW", "WAITING", "CANCELLED"],
                        "description": "Find blocks with this task status"
                    },
                    "property_key": {
                        "type": "string",
                        "description": "Custom property key"
                    },
                    "property_value": {
                        "type": "string",
                        "description": "Custom property value"
                    },
                    "limit": {
                        "type": "integer",
                        "default": 20,
                        "description": "Maximum number of blocks to return"
                    }
                }
            }),
        },
        Tool {
            name: "kogral/find_todos".to_string(),
            description: "Find all TODO blocks across KOGRAL".to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "limit": {
                        "type": "integer",
                        "default": 50,
                        "description": "Maximum number of TODOs to return"
                    }
                }
            }),
        },
        Tool {
            name: "kogral/find_cards".to_string(),
            description: "Find all flashcard blocks (#card tag) for spaced repetition".to_string(),
            input_schema: json!({
                "type": "object",
                "properties": {
                    "limit": {
                        "type": "integer",
                        "default": 30,
                        "description": "Maximum number of cards to return"
                    }
                }
            }),
        },
    ]
}

/// Execute a tool call
pub async fn execute_tool(name: &str, arguments: Value) -> Result<ToolResult> {
    info!("Executing tool: {} with args: {}", name, arguments);

    match name {
        "kogral/search" => tool_search(arguments).await,
        "kogral/add_note" => tool_add_note(arguments).await,
        "kogral/add_decision" => tool_add_decision(arguments).await,
        "kogral/link" => tool_link(arguments).await,
        "kogral/get_guidelines" => tool_get_guidelines(arguments).await,
        "kogral/list_graphs" => tool_list_graphs(arguments).await,
        "kogral/export" => tool_export(arguments).await,
        "kogral/find_blocks" => tool_find_blocks(arguments).await,
        "kogral/find_todos" => tool_find_todos(arguments).await,
        "kogral/find_cards" => tool_find_cards(arguments).await,
        _ => {
            error!("Unknown tool: {}", name);
            Ok(ToolResult::error(format!("Unknown tool: {}", name)))
        }
    }
}

/// Search knowledge base
async fn tool_search(args: Value) -> Result<ToolResult> {
    let query = validate_required_string(args["query"].as_str(), "query", MAX_STRING_LENGTH)?;
    let limit = validate_limit(args["limit"].as_u64(), 10)?;
    let node_type = validate_optional_string(args["type"].as_str(), "type", MAX_SHORT_LENGTH)?;

    let storage = FilesystemStorage::new(std::path::PathBuf::from(".kogral"));
    let graph = storage.load_graph("default").await?;

    // Simple text search
    let results: Vec<&Node> = graph
        .nodes
        .values()
        .filter(|node| {
            let matches_query = node.title.to_lowercase().contains(&query.to_lowercase())
                || node.content.to_lowercase().contains(&query.to_lowercase())
                || node
                    .tags
                    .iter()
                    .any(|t| t.to_lowercase().contains(&query.to_lowercase()));

            let matches_type = match node_type.as_deref() {
                Some("note") => node.node_type == NodeType::Note,
                Some("decision") => node.node_type == NodeType::Decision,
                Some("guideline") => node.node_type == NodeType::Guideline,
                Some("pattern") => node.node_type == NodeType::Pattern,
                Some("journal") => node.node_type == NodeType::Journal,
                Some("execution") => node.node_type == NodeType::Execution,
                _ => true,
            };

            matches_query && matches_type
        })
        .take(limit)
        .collect();

    let mut output = format!("Found {} result(s):\n\n", results.len());
    for (i, node) in results.iter().enumerate() {
        output.push_str(&format!(
            "{}. **{}** ({})\n   Type: {:?} | Status: {:?}\n",
            i + 1,
            node.title,
            node.id,
            node.node_type,
            node.status
        ));
        if !node.tags.is_empty() {
            output.push_str(&format!("   Tags: {}\n", node.tags.join(", ")));
        }
        if !node.content.is_empty() {
            let preview: String = node
                .content
                .lines()
                .next()
                .unwrap_or("")
                .chars()
                .take(100)
                .collect();
            output.push_str(&format!("   {}\n", preview));
        }
        output.push('\n');
    }

    Ok(ToolResult::text(output))
}

/// Add a note
async fn tool_add_note(args: Value) -> Result<ToolResult> {
    let title = validate_required_string(args["title"].as_str(), "title", MAX_STRING_LENGTH)?;
    let content = validate_required_string(args["content"].as_str(), "content", MAX_STRING_LENGTH)?;

    let mut node = Node::new(NodeType::Note, title);
    node.content = content;

    let tags = validate_string_array(
        args["tags"].as_array(),
        "tags",
        MAX_ARRAY_ITEMS,
        MAX_SHORT_LENGTH,
    )?;
    node.tags = tags;

    let relates_to = validate_string_array(
        args["relates_to"].as_array(),
        "relates_to",
        MAX_ARRAY_ITEMS,
        MAX_SHORT_LENGTH,
    )?;
    node.relates_to = relates_to;

    if let Some(project) =
        validate_optional_string(args["project"].as_str(), "project", MAX_SHORT_LENGTH)?
    {
        node.project = Some(project);
    }

    let mut storage = FilesystemStorage::new(std::path::PathBuf::from(".kogral"));
    let mut graph = storage
        .load_graph("default")
        .await
        .unwrap_or_else(|_| Graph::new("default".to_string()));

    graph
        .add_node(node.clone())
        .map_err(|e| anyhow::anyhow!(e))?;
    storage.save_graph(&graph).await?;

    Ok(ToolResult::text(format!(
        "✓ Note added successfully\n  ID: {}\n  Title: {}",
        node.id, node.title
    )))
}

/// Add an ADR
async fn tool_add_decision(args: Value) -> Result<ToolResult> {
    let title = validate_required_string(args["title"].as_str(), "title", MAX_STRING_LENGTH)?;
    let decision =
        validate_required_string(args["decision"].as_str(), "decision", MAX_STRING_LENGTH)?;

    let mut node = Node::new(NodeType::Decision, title);
    node.content = decision.clone();

    // Store decision-specific data in metadata
    let mut metadata = serde_json::Map::new();
    if let Some(context) =
        validate_optional_string(args["context"].as_str(), "context", MAX_STRING_LENGTH)?
    {
        metadata.insert("context".to_string(), json!(context));
    }
    metadata.insert("decision".to_string(), json!(decision));

    let consequences = validate_string_array(
        args["consequences"].as_array(),
        "consequences",
        MAX_ARRAY_ITEMS,
        MAX_STRING_LENGTH,
    )?;
    if !consequences.is_empty() {
        metadata.insert("consequences".to_string(), json!(consequences));
    }

    node.metadata = metadata.into_iter().collect();

    let tags = validate_string_array(
        args["tags"].as_array(),
        "tags",
        MAX_ARRAY_ITEMS,
        MAX_SHORT_LENGTH,
    )?;
    node.tags = tags;

    if let Some(project) =
        validate_optional_string(args["project"].as_str(), "project", MAX_SHORT_LENGTH)?
    {
        node.project = Some(project);
    }

    let mut storage = FilesystemStorage::new(std::path::PathBuf::from(".kogral"));
    let mut graph = storage
        .load_graph("default")
        .await
        .unwrap_or_else(|_| Graph::new("default".to_string()));

    graph
        .add_node(node.clone())
        .map_err(|e| anyhow::anyhow!(e))?;
    storage.save_graph(&graph).await?;

    Ok(ToolResult::text(format!(
        "✓ Decision added successfully\n  ID: {}\n  Title: {}",
        node.id, node.title
    )))
}

/// Create a link between nodes
async fn tool_link(args: Value) -> Result<ToolResult> {
    let from = validate_node_id(args["from"].as_str(), "from")?;
    let to = validate_node_id(args["to"].as_str(), "to")?;
    let relation_str = validate_enum(
        args["relation"].as_str(),
        "relation",
        &[
            "relates_to",
            "depends_on",
            "implements",
            "extends",
            "supersedes",
            "explains",
        ],
    )?;
    let strength = validate_strength(args["strength"].as_f64())?;

    let relation = match relation_str.as_str() {
        "relates_to" => EdgeType::RelatesTo,
        "depends_on" => EdgeType::DependsOn,
        "implements" => EdgeType::Implements,
        "extends" => EdgeType::Extends,
        "supersedes" => EdgeType::Supersedes,
        "explains" => EdgeType::Explains,
        _ => unreachable!("validate_enum should have prevented invalid relation"),
    };

    let mut storage = FilesystemStorage::new(std::path::PathBuf::from(".kogral"));
    let mut graph = storage.load_graph("default").await?;

    let mut edge = Edge::new(from.clone(), to.clone(), relation);
    edge.strength = strength;

    graph.add_edge(edge).map_err(|e| anyhow::anyhow!(e))?;
    storage.save_graph(&graph).await?;

    Ok(ToolResult::text(format!(
        "✓ Relationship created: {} --{:?}--> {}",
        from, relation, to
    )))
}

/// Get guidelines
async fn tool_get_guidelines(args: Value) -> Result<ToolResult> {
    let language =
        validate_optional_string(args["language"].as_str(), "language", MAX_SHORT_LENGTH)?;
    let category =
        validate_optional_string(args["category"].as_str(), "category", MAX_SHORT_LENGTH)?;

    let storage = FilesystemStorage::new(std::path::PathBuf::from(".kogral"));
    let graph = storage.load_graph("default").await?;

    let guidelines: Vec<&Node> = graph
        .nodes
        .values()
        .filter(|node| {
            if node.node_type != NodeType::Guideline {
                return false;
            }

            let lang_match = language
                .as_ref()
                .map(|l| {
                    node.metadata
                        .get("language")
                        .and_then(|v| v.as_str())
                        .map(|lang| lang == l)
                        .unwrap_or(false)
                })
                .unwrap_or(true);

            let cat_match = category
                .as_ref()
                .map(|c| {
                    node.metadata
                        .get("category")
                        .and_then(|v| v.as_str())
                        .map(|cat| cat == c)
                        .unwrap_or(false)
                })
                .unwrap_or(true);

            lang_match && cat_match
        })
        .collect();

    let mut output = format!("Found {} guideline(s):\n\n", guidelines.len());
    for guideline in guidelines {
        output.push_str(&format!("## {}\n", guideline.title));
        if let Some(lang) = guideline.metadata.get("language").and_then(|v| v.as_str()) {
            output.push_str(&format!("**Language:** {}\n", lang));
        }
        if let Some(cat) = guideline.metadata.get("category").and_then(|v| v.as_str()) {
            output.push_str(&format!("**Category:** {}\n", cat));
        }
        output.push_str(&format!("\n{}\n\n---\n\n", guideline.content));
    }

    Ok(ToolResult::text(output))
}

/// List available graphs
async fn tool_list_graphs(_args: Value) -> Result<ToolResult> {
    // For now, return default graph
    Ok(ToolResult::text("Available graphs:\n- default".to_string()))
}

/// Export knowledge base
async fn tool_export(args: Value) -> Result<ToolResult> {
    let format = validate_enum(
        args["format"].as_str(),
        "format",
        &["logseq", "markdown", "json", "summary"],
    )?;

    let storage = FilesystemStorage::new(std::path::PathBuf::from(".kogral"));
    let graph = storage.load_graph("default").await?;

    match format.as_str() {
        "json" => {
            let json = serde_json::to_string_pretty(&graph)?;
            Ok(ToolResult::text(json))
        }
        "summary" => {
            let mut output = "# Knowledge Base Summary\n\n".to_string();
            output.push_str(&format!("**Graph:** {}\n", graph.name));
            output.push_str(&format!("**Nodes:** {}\n", graph.nodes.len()));
            output.push_str(&format!("**Edges:** {}\n\n", graph.edges.len()));

            output.push_str("## Nodes by Type\n\n");
            let mut type_counts = std::collections::HashMap::new();
            for node in graph.nodes.values() {
                *type_counts
                    .entry(format!("{:?}", node.node_type))
                    .or_insert(0) += 1;
            }
            for (node_type, count) in type_counts {
                output.push_str(&format!("- **{}:** {}\n", node_type, count));
            }

            Ok(ToolResult::text(output))
        }
        _ => unreachable!("validate_enum should have prevented invalid format"),
    }
}

/// Find blocks by tag, status, or property
#[allow(clippy::excessive_nesting)]
async fn tool_find_blocks(args: Value) -> Result<ToolResult> {
    let tag = validate_optional_string(args["tag"].as_str(), "tag", MAX_SHORT_LENGTH)?;
    let status = validate_optional_string(args["status"].as_str(), "status", MAX_SHORT_LENGTH)?;
    let property_key = validate_optional_string(
        args["property_key"].as_str(),
        "property_key",
        MAX_SHORT_LENGTH,
    )?;
    let property_value = validate_optional_string(
        args["property_value"].as_str(),
        "property_value",
        MAX_STRING_LENGTH,
    )?;
    let limit = validate_limit(args["limit"].as_u64(), 20)?;

    let storage = FilesystemStorage::new(std::path::PathBuf::from(".kogral"));
    let mut graph = storage.load_graph("default").await?;

    let mut output = String::new();
    let mut total_blocks = 0;

    // Find by tag
    if let Some(tag_name) = &tag {
        // First, extract all block info and node IDs
        let block_results = graph.find_blocks_by_tag(tag_name);
        let mut node_blocks: Vec<(String, Vec<String>)> = Vec::new();

        for (node_id, blocks) in block_results {
            let contents: Vec<String> = blocks.iter().map(|b| b.content.clone()).collect();
            node_blocks.push((node_id, contents));
        }

        // Now access graph to get node titles
        output.push_str(&format!("## Blocks with tag #{}:\n\n", tag_name));
        for (node_id, contents) in node_blocks.into_iter().take(limit) {
            if let Some(node) = graph.get_node(&node_id) {
                output.push_str(&format!("**{}** ({})\n", node.title, node_id));
                for content in contents {
                    output.push_str(&format!("  - {}\n", content));
                    total_blocks += 1;
                }
                output.push('\n');
            }
        }
    }

    // Find by status
    if let Some(status_str) = &status {
        let task_status = match status_str.as_str() {
            "TODO" => Some(TaskStatus::Todo),
            "DOING" => Some(TaskStatus::Doing),
            "DONE" => Some(TaskStatus::Done),
            "LATER" => Some(TaskStatus::Later),
            "NOW" => Some(TaskStatus::Now),
            "WAITING" => Some(TaskStatus::Waiting),
            "CANCELLED" => Some(TaskStatus::Cancelled),
            _ => None,
        };

        if let Some(task_status_value) = task_status {
            let block_results = graph.find_blocks_by_status(task_status_value);
            let mut node_blocks: Vec<(String, Vec<String>)> = Vec::new();

            for (node_id, blocks) in block_results {
                let contents: Vec<String> = blocks.iter().map(|b| b.content.clone()).collect();
                node_blocks.push((node_id, contents));
            }

            output.push_str(&format!("## Blocks with status {}:\n\n", status_str));
            for (node_id, contents) in node_blocks.into_iter().take(limit) {
                if let Some(node) = graph.get_node(&node_id) {
                    output.push_str(&format!("**{}** ({})\n", node.title, node_id));
                    for content in contents {
                        output.push_str(&format!("  - {} {}\n", status_str, content));
                        total_blocks += 1;
                    }
                    output.push('\n');
                }
            }
        }
    }

    // Find by custom property
    if let (Some(key), Some(value)) = (&property_key, &property_value) {
        let block_results = graph.find_blocks_by_property(key, value);
        let mut node_blocks: Vec<(String, Vec<String>)> = Vec::new();

        for (node_id, blocks) in block_results {
            let contents: Vec<String> = blocks.iter().map(|b| b.content.clone()).collect();
            node_blocks.push((node_id, contents));
        }

        output.push_str(&format!("## Blocks with {}:: {}:\n\n", key, value));
        for (node_id, contents) in node_blocks.into_iter().take(limit) {
            if let Some(node) = graph.get_node(&node_id) {
                output.push_str(&format!("**{}** ({})\n", node.title, node_id));
                for content in contents {
                    output.push_str(&format!("  - {}\n", content));
                    total_blocks += 1;
                }
                output.push('\n');
            }
        }
    }

    if total_blocks == 0 {
        output = "No blocks found matching criteria.".to_string();
    } else {
        output = format!("Found {} block(s)\n\n{}", total_blocks, output);
    }

    Ok(ToolResult::text(output))
}

/// Find all TODO blocks
async fn tool_find_todos(args: Value) -> Result<ToolResult> {
    let limit = validate_limit(args["limit"].as_u64(), 50)?;

    let storage = FilesystemStorage::new(std::path::PathBuf::from(".kogral"));
    let mut graph = storage.load_graph("default").await?;

    let todo_results = graph.find_all_todos();
    let mut node_blocks: Vec<(String, Vec<(String, String)>)> = Vec::new();

    for (node_id, blocks) in todo_results {
        let block_data: Vec<(String, String)> = blocks
            .iter()
            .map(|b| {
                let status = b
                    .properties
                    .status
                    .map(|s| s.to_string())
                    .unwrap_or_else(|| "TODO".to_string());
                (status, b.content.clone())
            })
            .collect();
        node_blocks.push((node_id, block_data));
    }

    let total_todos: usize = node_blocks.iter().map(|(_, blocks)| blocks.len()).sum();
    let mut output = format!("Found {} TODO block(s):\n\n", total_todos);

    let mut count = 0;
    for (node_id, blocks) in node_blocks {
        if count >= limit {
            break;
        }

        if let Some(node) = graph.get_node(&node_id) {
            output.push_str(&format!("## {} ({})\n", node.title, node_id));

            for (status, content) in blocks {
                if count >= limit {
                    break;
                }
                output.push_str(&format!("- {} {}\n", status, content));
                count += 1;
            }
            output.push('\n');
        }
    }

    if total_todos == 0 {
        output = "No TODO blocks found.".to_string();
    }

    Ok(ToolResult::text(output))
}

/// Find all flashcard blocks (#card)
async fn tool_find_cards(args: Value) -> Result<ToolResult> {
    let limit = validate_limit(args["limit"].as_u64(), 30)?;

    let storage = FilesystemStorage::new(std::path::PathBuf::from(".kogral"));
    let mut graph = storage.load_graph("default").await?;

    let card_results = graph.find_blocks_by_tag("card");
    let mut node_blocks: Vec<(String, Vec<(String, Vec<String>)>)> = Vec::new();

    for (node_id, blocks) in card_results {
        let card_data: Vec<(String, Vec<String>)> = blocks
            .iter()
            .map(|b| {
                let children: Vec<String> = b.children.iter().map(|c| c.content.clone()).collect();
                (b.content.clone(), children)
            })
            .collect();
        node_blocks.push((node_id, card_data));
    }

    let total_cards: usize = node_blocks.iter().map(|(_, cards)| cards.len()).sum();
    let mut output = format!("Found {} flashcard(s):\n\n", total_cards);

    let mut count = 0;
    for (node_id, cards) in node_blocks {
        if count >= limit {
            break;
        }

        if let Some(node) = graph.get_node(&node_id) {
            output.push_str(&format!("## {} ({})\n", node.title, node_id));

            for (content, children) in cards {
                if count >= limit {
                    break;
                }
                output.push_str(&format!("- {} #card\n", content));

                // Show nested blocks (answer part of flashcard)
                for child in children {
                    output.push_str(&format!("  - {}\n", child));
                }

                count += 1;
            }
            output.push('\n');
        }
    }

    if total_cards == 0 {
        output = "No flashcards (#card) found.".to_string();
    }

    Ok(ToolResult::text(output))
}
