//! `tools/list` MCP method — returns the static schema for every tool the server exposes.

use serde_json::Value;

mod agent_tools;
mod story_tools;
mod system_tools;

/// Return the full list of MCP tool definitions (name, description, inputSchema).
///
/// Used by API-based runtimes (Gemini, OpenAI) that need tool schemas
/// without going through the network.
pub fn list_tools() -> Vec<Value> {
    let mut tools = Vec::new();
    tools.extend(story_tools::story_tools());
    tools.extend(agent_tools::agent_tools());
    tools.extend(system_tools::system_tools());
    tools
}

/// Wrap `list_tools()` in a JSON-RPC response (test-only helper).
#[cfg(test)]
pub(crate) fn handle_tools_list(
    id: Option<Value>,
) -> crate::http::gateway::jsonrpc::JsonRpcResponse {
    use serde_json::json;
    crate::http::gateway::jsonrpc::JsonRpcResponse::success(id, json!({ "tools": list_tools() }))
}

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

    #[test]
    fn tools_list_returns_all_tools() {
        let resp = handle_tools_list(Some(json!(2)));
        let result = resp.result.unwrap();
        let tools = result["tools"].as_array().unwrap();
        let names: Vec<&str> = tools.iter().map(|t| t["name"].as_str().unwrap()).collect();
        assert!(names.contains(&"create_story"));
        assert!(names.contains(&"validate_stories"));
        assert!(names.contains(&"list_upcoming"));
        assert!(names.contains(&"get_story_todos"));
        assert!(names.contains(&"record_tests"));
        assert!(names.contains(&"ensure_acceptance"));
        assert!(names.contains(&"start_agent"));
        assert!(names.contains(&"stop_agent"));
        assert!(names.contains(&"list_agents"));
        assert!(names.contains(&"get_agent_config"));
        assert!(names.contains(&"reload_agent_config"));
        assert!(names.contains(&"get_agent_output"));
        assert!(names.contains(&"wait_for_agent"));
        assert!(names.contains(&"get_agent_remaining_turns_and_budget"));
        assert!(names.contains(&"create_worktree"));
        assert!(names.contains(&"list_worktrees"));
        assert!(names.contains(&"remove_worktree"));
        assert!(names.contains(&"get_editor_command"));
        assert!(!names.contains(&"report_completion"));
        assert!(names.contains(&"accept_story"));
        assert!(names.contains(&"check_criterion"));
        assert!(names.contains(&"add_criterion"));
        assert!(names.contains(&"update_story"));
        assert!(names.contains(&"create_spike"));
        assert!(names.contains(&"create_bug"));
        assert!(names.contains(&"list_bugs"));
        assert!(names.contains(&"close_bug"));
        assert!(names.contains(&"create_refactor"));
        assert!(names.contains(&"list_refactors"));
        assert!(names.contains(&"merge_agent_work"));
        assert!(names.contains(&"get_merge_status"));
        assert!(names.contains(&"move_story_to_merge"));
        assert!(names.contains(&"report_merge_failure"));
        assert!(names.contains(&"request_qa"));
        assert!(names.contains(&"approve_qa"));
        assert!(names.contains(&"reject_qa"));
        assert!(names.contains(&"launch_qa_app"));
        assert!(names.contains(&"get_server_logs"));
        assert!(names.contains(&"prompt_permission"));
        assert!(names.contains(&"get_pipeline_status"));
        assert!(names.contains(&"pipeline_query"));
        assert!(names.contains(&"get_token_usage"));
        assert!(names.contains(&"move_story"));
        assert!(names.contains(&"unblock_story"));
        assert!(names.contains(&"delete_story"));
        assert!(names.contains(&"run_command"));
        assert!(names.contains(&"run_tests"));
        assert!(names.contains(&"get_test_result"));
        assert!(names.contains(&"run_build"));
        assert!(names.contains(&"run_lint"));
        assert!(names.contains(&"git_status"));
        assert!(names.contains(&"git_diff"));
        assert!(names.contains(&"git_add"));
        assert!(names.contains(&"git_commit"));
        assert!(names.contains(&"git_log"));
        assert!(names.contains(&"show"));
        assert!(names.contains(&"loc_file"));
        assert!(names.contains(&"dump_crdt"));
        assert!(names.contains(&"get_version"));
        assert!(names.contains(&"remove_criterion"));
        assert!(names.contains(&"mesh_status"));
        assert!(names.contains(&"run_check"));
        assert!(names.contains(&"cleanup_worktrees"));
        assert!(names.contains(&"create_epic"));
        assert!(names.contains(&"list_epics"));
        assert!(names.contains(&"show_epic"));
        assert!(names.contains(&"freeze_story"));
        assert!(names.contains(&"unfreeze_story"));
        assert!(names.contains(&"find_orphaned_items"));
        assert!(names.contains(&"recover_half_written_items"));
        assert!(names.contains(&"schedule_event_trigger"));
        assert!(names.contains(&"list_event_triggers"));
        assert!(names.contains(&"cancel_event_trigger"));
        assert!(names.contains(&"schedule_timer"));
        assert!(names.contains(&"list_timers"));
        assert!(names.contains(&"cancel_timer"));
        assert!(names.contains(&"convert_item_type"));
        assert!(names.contains(&"edit"));
        assert!(names.contains(&"write"));
        assert!(names.contains(&"gc"));
        assert_eq!(tools.len(), 86);
    }

    #[test]
    fn tools_list_schemas_have_required_fields() {
        let resp = handle_tools_list(Some(json!(1)));
        let tools = resp.result.unwrap()["tools"].as_array().unwrap().clone();
        for tool in &tools {
            assert!(tool["name"].is_string(), "tool missing name");
            assert!(tool["description"].is_string(), "tool missing description");
            assert!(tool["inputSchema"].is_object(), "tool missing inputSchema");
            assert_eq!(tool["inputSchema"]["type"], "object");
        }
    }
}
