pub mod bash;
pub mod calculator;
pub mod file_edit;
pub mod file_read;
pub mod file_write;
pub mod http_request;
pub mod memory_manage;
pub mod memory_search;
pub mod registry;
pub mod scheduler_manage;
pub mod session_send;
pub mod schema;
pub mod shell_session;
pub mod skill_activate;
pub mod skill_manage;
pub mod task;
pub mod time;
pub mod todo_write;
pub mod traits;
pub mod web_fetch;

pub use bash::BashTool;
pub use calculator::CalculatorTool;
pub use file_edit::FileEditTool;
pub use file_read::FileReadTool;
pub use file_write::FileWriteTool;
pub use http_request::HttpRequestTool;
pub use memory_manage::MemoryManageTool;
pub use memory_search::MemorySearchTool;
pub use registry::ToolRegistry;
pub use scheduler_manage::SchedulerManageTool;
pub use session_send::{
	NoopSessionMessageSender, SessionMessageSender, SessionSendOutcome, SessionSendRequest,
	SessionSendTool,
};
pub use schema::{CleaningStrategy, SchemaCleanr};
pub use shell_session::ShellSessionManager;
pub use skill_activate::SkillActivateTool;
pub use skill_manage::SkillManageTool;
pub use task::{
    DefaultSubAgentRuntime, InMemoryTaskRepository, SubAgentRuntime, SubAgentRuntimeConfig,
    SubagentCatalog, TaskError, TaskRepository, TaskTool,
};
pub use time::TimeTool;
pub use todo_write::TodoWriteTool;
pub use traits::{Tool, ToolContext, ToolResult};
pub use web_fetch::WebFetchTool;

/// Extract a string parameter from JSON args.
pub fn extract_string(args: &serde_json::Value, key: &str) -> Option<String> {
    args.get(key).and_then(|v| {
        if let Some(s) = v.as_str() {
            Some(s.to_string())
        } else if let Some(n) = v.as_number() {
            // Handle case where LLM sends a number but we need a string
            Some(n.to_string())
        } else {
            None
        }
    })
}

/// Extract an f64 parameter from JSON args, handling both numbers and strings.
pub fn extract_f64(args: &serde_json::Value, key: &str) -> Option<f64> {
    args.get(key).and_then(|v| {
        if let Some(n) = v.as_f64() {
            Some(n)
        } else if let Some(s) = v.as_str() {
            s.parse::<f64>().ok()
        } else {
            None
        }
    })
}

/// Extract an i64 parameter from JSON args, handling both numbers and strings.
pub fn extract_i64(args: &serde_json::Value, key: &str) -> Option<i64> {
    args.get(key).and_then(|v| {
        if let Some(n) = v.as_i64() {
            Some(n)
        } else if let Some(s) = v.as_str() {
            s.parse::<i64>().ok()
        } else {
            None
        }
    })
}

/// Extract a u64 parameter from JSON args, handling both numbers and strings.
pub fn extract_u64(args: &serde_json::Value, key: &str) -> Option<u64> {
    args.get(key).and_then(|v| {
        if let Some(n) = v.as_u64() {
            Some(n)
        } else if let Some(s) = v.as_str() {
            s.parse::<u64>().ok()
        } else {
            None
        }
    })
}

/// Extract a bool parameter from JSON args, handling both booleans and strings.
pub fn extract_bool(args: &serde_json::Value, key: &str) -> Option<bool> {
    args.get(key).and_then(|v| {
        if let Some(b) = v.as_bool() {
            Some(b)
        } else if let Some(s) = v.as_str() {
            match s.to_lowercase().as_str() {
                "true" | "1" | "yes" | "on" => Some(true),
                "false" | "0" | "no" | "off" => Some(false),
                _ => None,
            }
        } else {
            None
        }
    })
}

/// Extract a required string parameter, returning an error message if missing.
pub fn require_string(args: &serde_json::Value, key: &str) -> Result<String, String> {
    extract_string(args, key)
        .filter(|s| !s.trim().is_empty())
        .ok_or_else(|| format!("Missing required parameter: {}", key))
}

/// Extract a required f64 parameter, returning an error message if missing.
pub fn require_f64(args: &serde_json::Value, key: &str) -> Result<f64, String> {
    extract_f64(args, key)
        .ok_or_else(|| format!("Missing required parameter: {}", key))
}

/// Extract a required i64 parameter, returning an error message if missing.
pub fn require_i64(args: &serde_json::Value, key: &str) -> Result<i64, String> {
    extract_i64(args, key)
        .ok_or_else(|| format!("Missing required parameter: {}", key))
}

/// Extract a required u64 parameter, returning an error message if missing.
pub fn require_u64(args: &serde_json::Value, key: &str) -> Result<u64, String> {
    extract_u64(args, key)
        .ok_or_else(|| format!("Missing required parameter: {}", key))
}

/// Extract a required bool parameter, returning an error message if missing.
pub fn require_bool(args: &serde_json::Value, key: &str) -> Result<bool, String> {
    extract_bool(args, key)
        .ok_or_else(|| format!("Missing required parameter: {}", key))
}

/// Extract a string array parameter, handling both actual arrays and stringified JSON arrays.
pub fn extract_string_array(args: &serde_json::Value, key: &str) -> Option<Vec<String>> {
    args.get(key).and_then(|v| {
        if let Some(arr) = v.as_array() {
            Some(
                arr.iter()
                    .filter_map(|item| item.as_str())
                    .map(str::trim)
                    .filter(|s| !s.is_empty())
                    .map(ToOwned::to_owned)
                    .collect(),
            )
        } else if let Some(s) = v.as_str() {
            // Try to parse as JSON array string
            serde_json::from_str::<Vec<serde_json::Value>>(s)
                .ok()
                .map(|arr| {
                    arr.iter()
                        .filter_map(|item| item.as_str())
                        .map(str::trim)
                        .filter(|s| !s.is_empty())
                        .map(ToOwned::to_owned)
                        .collect()
                })
                .filter(|result: &Vec<String>| !result.is_empty())
        } else {
            None
        }
    })
}

/// Extract a required string array parameter.
pub fn require_string_array(args: &serde_json::Value, key: &str) -> Result<Vec<String>, String> {
    extract_string_array(args, key)
        .filter(|arr| !arr.is_empty())
        .ok_or_else(|| format!("Missing required parameter: {}", key))
}

/// Check if args is null and return an error result if so.
/// Returns the provided error message if args is null.
pub fn check_null_args(args: &serde_json::Value, tool_name: &str) -> Option<ToolResult> {
    if args.is_null() {
        return Some(ToolResult {
            success: false,
            output: String::new(),
            error: Some(format!(
                "Missing required parameters: {} expects a JSON object with required fields. Check the tool schema and provide the necessary arguments.",
                tool_name
            )),
        });
    }
    if !args.is_object() {
        return Some(ToolResult {
            success: false,
            output: String::new(),
            error: Some(format!(
                "Invalid parameters: {} expects a JSON object, got {}",
                tool_name,
                if args.is_array() { "an array" } else if args.is_string() { "a string" } else { "an unexpected type" }
            )),
        });
    }
    None
}
