pub mod continuation;
pub mod email;
pub mod local_program;
pub mod questions;
use anyhow::Result;
use schemars::{schema::RootSchema, schema_for, JsonSchema};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::Value;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ToolChoice {
    /// `required` - Open AI
    /// `any` - Anthropic
    Any,
    /// `function` - Open AI
    /// `tool` - Anthropic
    Specific(String),
    /// `auto` - Open AI
    /// `auto` - Anthropic
    SelfSelect,
}

/// Typed tool interface
pub trait TypedTool {
    type Args: Serialize + DeserializeOwned + JsonSchema;
    type Output: Serialize + DeserializeOwned + JsonSchema;

    /// Name of the tool
    fn name(&self) -> &str;
    /// Description of what the tool does
    fn description(&self) -> &str;
    /// Execute the tool with the given arguments
    fn execute(&self, args: Self::Args) -> Result<Self::Output>;
}

/// Erased tool interface for storage
pub trait Tool {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn execute(&self, args: Value) -> Result<Value>;
    fn schema(&self) -> RootSchema;
}

// Automatic implementation to convert TypedTool to Tool
impl<T: TypedTool> Tool for T {
    fn name(&self) -> &str {
        self.name()
    }

    fn description(&self) -> &str {
        self.description()
    }

    fn execute(&self, args: Value) -> Result<Value> {
        let typed_args = serde_json::from_value(args)?;
        let result = self.execute(typed_args)?;
        Ok(serde_json::to_value(result)?)
    }
    fn schema(&self) -> RootSchema {
        schema_for!(T::Args)
    }
}

pub struct Toolkit {
    tools: Vec<Box<dyn Tool>>,
}

impl Toolkit {
    pub fn new() -> Self {
        Self { tools: vec![] }
    }

    pub fn add<T: Tool + 'static>(&mut self, tool: T) {
        self.tools.push(Box::new(tool));
    }

    pub fn get(&self, name: &str) -> Option<&dyn Tool> {
        self.tools.iter().find(|t| t.name() == name).map(|t| &**t)
    }

    pub fn tools(&self) -> &[Box<dyn Tool>] {
        &self.tools
    }
}

/// A tool that can be used by an LLM
pub trait ToolDefinition {
    fn name(&self) -> String;
    fn description(&self) -> String;
    fn args(&self) -> RootSchema;
}

pub trait ToolExecution {
    type Args: Serialize + DeserializeOwned + JsonSchema;
    type Return: Serialize + DeserializeOwned + JsonSchema;

    fn schema(&self) -> RootSchema;
    fn args_from_schema(&self, schema: Value) -> Result<Self::Args>;
    fn json_schema_to_args(&self, schema: RootSchema) -> Result<Self::Args>;
    fn args_from_json(&self, json: Value) -> Result<Self::Args>;
    fn execute(&self, args: Self::Args) -> Result<Self::Return>;
}
