//! Example: Implementing a custom Tool for Corvus
//!
//! This shows how to add a new tool the agent can use.
//! Tools are the agent's hands — they let it interact with the world.

use anyhow::Result;
use async_trait::async_trait;
use serde_json::{json, Value};
use url::Url;

/// Mirrors src/tools/traits.rs
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct ToolResult {
    pub success: bool,
    pub output: String,
    pub error: Option<String>,
}

#[async_trait]
pub trait Tool: Send + Sync {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn parameters_schema(&self) -> Value;
    async fn execute(&self, args: Value) -> Result<ToolResult>;
}

const ALLOWED_HOSTS: &[&str] = &["api.github.com", "example.com"];

/// Example: A tool that fetches a URL and returns the status code
pub struct HttpGetTool;

#[async_trait]
impl Tool for HttpGetTool {
    fn name(&self) -> &str {
        "http_get"
    }

    fn description(&self) -> &str {
        "Fetch a URL and return the HTTP status code and content length"
    }

    fn parameters_schema(&self) -> Value {
        json!({
            "type": "object",
            "properties": {
                "url": { "type": "string", "description": "URL to fetch" }
            },
            "required": ["url"]
        })
    }

    async fn execute(&self, args: Value) -> Result<ToolResult> {
        let url = args["url"]
            .as_str()
            .ok_or_else(|| anyhow::anyhow!("Missing 'url' parameter"))?;
        let parsed_url = Url::parse(url).map_err(|_| anyhow::anyhow!("Invalid URL"))?;

        if !matches!(parsed_url.scheme(), "http" | "https") {
            return Ok(ToolResult {
                success: false,
                output: String::new(),
                error: Some("Only HTTP and HTTPS URLs are supported".to_string()),
            });
        }

        let Some(host) = parsed_url.host_str() else {
            return Ok(ToolResult {
                success: false,
                output: String::new(),
                error: Some("URL must include a host".to_string()),
            });
        };

        if !ALLOWED_HOSTS.contains(&host) {
            return Ok(ToolResult {
                success: false,
                output: String::new(),
                error: Some(format!("Host {host} is not in the example allowlist")),
            });
        }

        // Host is restricted to ALLOWED_HOSTS above; Semgrep does not model that allowlist guard.
        match reqwest::get(parsed_url).await {
            // nosemgrep: rust.actix.ssrf.reqwest-taint.reqwest-taint
            Ok(resp) => {
                let status = resp.status().as_u16();
                let len = resp.content_length().unwrap_or(0);
                Ok(ToolResult {
                    success: status < 400,
                    output: format!("HTTP {status} — {len} bytes"),
                    error: None,
                })
            }
            Err(e) => Ok(ToolResult {
                success: false,
                output: String::new(),
                error: Some(format!("Request failed: {e}")),
            }),
        }
    }
}

fn main() {
    println!("This is an example — see CONTRIBUTING.md for integration steps.");
    println!("Register your tool in src/tools/mod.rs default_tools()");
}
