//! The `PeonTool` trait — Peon's replacement for `rig::tool::Tool`.
//!
//! The critical difference: `call()` receives a `&RequestContext`,
//! giving every tool access to the authenticated caller's identity
//! without relying on task-locals, global state, or LLM-controlled args.

use crate::context::RequestContext;
use crate::error::ToolError;
use serde::{Deserialize, Serialize};
use std::future::Future;
use std::pin::Pin;

/// A boxed, `Send`-compatible future.
///
/// Used as the return type for `PeonTool` async methods to ensure
/// object safety (`dyn PeonTool`) while staying runtime-agnostic.
pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

/// JSON Schema description of a tool, sent to the LLM so it knows
/// what tools are available and how to call them.
///
/// Compatible with the OpenAI function-calling format.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ToolDefinition {
    /// The unique name of the tool (e.g., "read_skill", "execute_script").
    pub name: String,
    /// A natural-language description of what the tool does.
    pub description: String,
    /// JSON Schema describing the tool's expected arguments.
    pub parameters: serde_json::Value,
}

/// A Peon tool with context-aware execution.
///
/// This is the core trait that all Peon tools implement. Unlike `rig::tool::Tool`,
/// this trait passes a `&RequestContext` to both `definition()` and `call()`,
/// enabling zero-trust permission enforcement without task-local hacks.
///
/// # Object Safety
///
/// This trait is object-safe — you can use `Box<dyn PeonTool>` to store
/// heterogeneous tools in a `Vec`. The async methods return `BoxFuture`
/// (a pinned, boxed future) to make this possible without `async-trait`.
///
/// # Example
///
/// ```rust,ignore
/// struct GreetTool;
///
/// impl PeonTool for GreetTool {
///     fn name(&self) -> &str { "greet" }
///
///     fn definition(&self, _ctx: &RequestContext) -> BoxFuture<'_, ToolDefinition> {
///         Box::pin(async {
///             ToolDefinition {
///                 name: "greet".into(),
///                 description: "Say hello".into(),
///                 parameters: serde_json::json!({"type": "object", "properties": {}}),
///             }
///         })
///     }
///
///     fn call(&self, _args: &str, ctx: &RequestContext) -> BoxFuture<'_, Result<String, ToolError>> {
///         let uid = ctx.uid().to_string();
///         Box::pin(async move {
///             Ok(format!("Hello, user {}!", uid))
///         })
///     }
/// }
/// ```
pub trait PeonTool: Send + Sync {
    /// The unique name of this tool.
    ///
    /// Must match the `name` field in the `ToolDefinition` returned by `definition()`.
    fn name(&self) -> &str;

    /// Return the JSON Schema definition for the LLM.
    ///
    /// The `RequestContext` is provided so the definition can be dynamically
    /// tailored — for example, a tool might restrict its `enum` values based
    /// on the calling user's permissions.
    fn definition(&self, ctx: &RequestContext) -> BoxFuture<'_, ToolDefinition>;

    /// Execute the tool.
    ///
    /// - `args`: JSON string of arguments generated by the LLM.
    /// - `ctx`: The immutable, request-scoped context (contains UID, metadata).
    ///
    /// The LLM controls `args` but **cannot** control `ctx`. This is the
    /// fundamental security guarantee of the Peon runtime.
    fn call(&self, args: &str, ctx: &RequestContext) -> BoxFuture<'_, Result<String, ToolError>>;
}
