//! Storage-backed MCP tool handlers for intent graphs.
//!
//! Each function parses arguments from `serde_json::Value`, delegates to
//! `agileplus_sqlite::Storage`, and returns an `McpToolResponse`.

use crate::api::{mcp_error_response, mcp_json_response, McpToolResponse};
use crate::graph_ops::validate_graph_payload;
use agileplus_domain::intent_graph::IntentGraph;
use agileplus_sqlite::Storage;
use serde_json::{json, Value};

/// Create a new graph from the provided payload.
///
/// Expects `args`:
/// ```json
/// { "id": "<graph_id>", "graph": { ... } }
/// ```
pub fn create_graph(store: &Storage, args: Value) -> McpToolResponse {
    let id = match args.get("id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'id' argument".to_string()),
    };
    let graph: IntentGraph = match args
        .get("graph")
        .and_then(|v| serde_json::from_value(v.clone()).ok())
    {
        Some(g) => g,
        None => return mcp_error_response("Missing or invalid 'graph' argument".to_string()),
    };
    match store.save_graph(id, &graph) {
        Ok(()) => mcp_json_response(&json!({"id": id})),
        Err(e) => mcp_error_response(format!("Failed to save graph: {e}")),
    }
}

/// List all stored graph IDs.
pub fn list_graphs(store: &Storage, _args: Value) -> McpToolResponse {
    match store.list_graphs() {
        Ok(ids) => mcp_json_response(&json!({"ids": ids})),
        Err(e) => mcp_error_response(format!("Failed to list graphs: {e}")),
    }
}

/// Retrieve a stored graph by ID.
///
/// Expects `args`:
/// ```json
/// { "id": "<graph_id>" }
/// ```
pub fn get_graph(store: &Storage, args: Value) -> McpToolResponse {
    let id = match args.get("id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'id' argument".to_string()),
    };
    match store.load_graph(id) {
        Ok(Some(graph)) => mcp_json_response(&graph),
        Ok(None) => mcp_error_response("Graph not found".to_string()),
        Err(e) => mcp_error_response(format!("Failed to load graph: {e}")),
    }
}

/// Delete a stored graph by ID.
///
/// Expects `args`:
/// ```json
/// { "id": "<graph_id>" }
/// ```
pub fn delete_graph(store: &Storage, args: Value) -> McpToolResponse {
    let id = match args.get("id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'id' argument".to_string()),
    };
    match store.delete_graph(id) {
        Ok(deleted) => mcp_json_response(&json!({"deleted": deleted})),
        Err(e) => mcp_error_response(format!("Failed to delete graph: {e}")),
    }
}

/// Load a stored graph and run validation against it.
///
/// Expects `args`:
/// ```json
/// { "id": "<graph_id>" }
/// ```
pub fn validate_stored_graph(store: &Storage, args: Value) -> McpToolResponse {
    let id = match args.get("id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'id' argument".to_string()),
    };
    let graph = match store.load_graph(id) {
        Ok(Some(g)) => g,
        Ok(None) => return mcp_error_response("Graph not found".to_string()),
        Err(e) => return mcp_error_response(format!("Failed to load graph: {e}")),
    };
    match validate_graph_payload(&graph) {
        Ok(report) => mcp_json_response(&report),
        Err(e) => mcp_error_response(format!("Validation error: {e}")),
    }
}

/// Add a tag to a graph.
///
/// Expects `args`:
/// ```json
/// { "graph_id": "<graph_id>", "tag": "<tag>" }
/// ```
pub fn add_tag(store: &Storage, args: Value) -> McpToolResponse {
    let graph_id = match args.get("graph_id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'graph_id' argument".to_string()),
    };
    let tag = match args.get("tag").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'tag' argument".to_string()),
    };
    match store.add_tag(graph_id, tag) {
        Ok(inserted) => mcp_json_response(&json!({"inserted": inserted})),
        Err(e) => mcp_error_response(format!("Failed to add tag: {e}")),
    }
}

/// Remove a tag from a graph.
///
/// Expects `args`:
/// ```json
/// { "graph_id": "<graph_id>", "tag": "<tag>" }
/// ```
pub fn remove_tag(store: &Storage, args: Value) -> McpToolResponse {
    let graph_id = match args.get("graph_id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'graph_id' argument".to_string()),
    };
    let tag = match args.get("tag").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'tag' argument".to_string()),
    };
    match store.remove_tag(graph_id, tag) {
        Ok(removed) => mcp_json_response(&json!({"removed": removed})),
        Err(e) => mcp_error_response(format!("Failed to remove tag: {e}")),
    }
}

/// List tags attached to a graph.
///
/// Expects `args`:
/// ```json
/// { "graph_id": "<graph_id>" }
/// ```
pub fn list_tags(store: &Storage, args: Value) -> McpToolResponse {
    let graph_id = match args.get("graph_id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'graph_id' argument".to_string()),
    };
    match store.list_tags(graph_id) {
        Ok(tags) => mcp_json_response(&json!({"tags": tags})),
        Err(e) => mcp_error_response(format!("Failed to list tags: {e}")),
    }
}

/// Add a note to a graph.
///
/// Expects `args`:
/// ```json
/// { "graph_id": "<graph_id>", "body": "<note body>" }
/// ```
pub fn add_note(store: &Storage, args: Value) -> McpToolResponse {
    let graph_id = match args.get("graph_id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'graph_id' argument".to_string()),
    };
    let body = match args.get("body").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'body' argument".to_string()),
    };
    match store.add_note(graph_id, body) {
        Ok(note_id) => mcp_json_response(&json!({"note_id": note_id})),
        Err(e) => mcp_error_response(format!("Failed to add note: {e}")),
    }
}

/// List notes attached to a graph.
///
/// Expects `args`:
/// ```json
/// { "graph_id": "<graph_id>" }
/// ```
pub fn list_notes(store: &Storage, args: Value) -> McpToolResponse {
    let graph_id = match args.get("graph_id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'graph_id' argument".to_string()),
    };
    match store.list_notes(graph_id) {
        Ok(notes) => mcp_json_response(&json!({"notes": notes})),
        Err(e) => mcp_error_response(format!("Failed to list notes: {e}")),
    }
}

/// Delete a note from a graph.
///
/// Expects `args`:
/// ```json
/// { "graph_id": "<graph_id>", "note_id": <integer> }
/// ```
pub fn delete_note(store: &Storage, args: Value) -> McpToolResponse {
    let graph_id = match args.get("graph_id").and_then(|v| v.as_str()) {
        Some(v) => v,
        None => return mcp_error_response("Missing 'graph_id' argument".to_string()),
    };
    let note_id = match args.get("note_id").and_then(|v| v.as_i64()) {
        Some(v) => v,
        None => return mcp_error_response("Missing or invalid 'note_id' argument".to_string()),
    };
    match store.delete_note(graph_id, note_id) {
        Ok(deleted) => mcp_json_response(&json!({"deleted": deleted})),
        Err(e) => mcp_error_response(format!("Failed to delete note: {e}")),
    }
}
