use std::sync::Arc;

use motosan_agent_tool::Tool;

pub mod bash;
pub mod context;
pub mod edit;
pub mod find;
pub mod grep;
pub mod ls;
pub mod read;
pub mod write;

pub use bash::BashTool;
pub use context::{SharedCancelToken, ToolCtx, ToolProgressChunk};
pub use edit::EditTool;
pub use find::FindTool;
pub use grep::GrepTool;
pub use ls::LsTool;
pub use read::ReadTool;
pub use write::WriteTool;

pub fn builtin_tools(ctx: ToolCtx) -> Vec<Arc<dyn Tool>> {
    let ctx = Arc::new(ctx);
    vec![
        Arc::new(ReadTool::new(Arc::clone(&ctx))) as Arc<dyn Tool>,
        Arc::new(BashTool::new(Arc::clone(&ctx))) as Arc<dyn Tool>,
        Arc::new(WriteTool::new(Arc::clone(&ctx))) as Arc<dyn Tool>,
        Arc::new(EditTool::new(Arc::clone(&ctx))) as Arc<dyn Tool>,
        Arc::new(GrepTool::new(Arc::clone(&ctx))) as Arc<dyn Tool>,
        Arc::new(FindTool::new(Arc::clone(&ctx))) as Arc<dyn Tool>,
        Arc::new(LsTool::new(Arc::clone(&ctx))) as Arc<dyn Tool>,
    ]
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::permissions::NoOpPermissionGate;
    use tokio::sync::mpsc;

    fn test_ctx() -> ToolCtx {
        let (tx, _rx) = mpsc::channel(8);
        let cwd = std::env::current_dir().unwrap_or_else(|_| std::path::PathBuf::from("."));
        ToolCtx::new(cwd, Arc::new(NoOpPermissionGate), tx)
    }

    #[test]
    fn builtin_tools_contains_all_seven() {
        let tools = builtin_tools(test_ctx());
        let names = tools.iter().map(|tool| tool.def().name).collect::<Vec<_>>();
        assert_eq!(tools.len(), 7);
        for expected in ["read", "bash", "write", "edit", "grep", "find", "ls"] {
            assert!(names.contains(&expected.to_string()), "missing {expected}");
        }
    }
}
