//! ReAct agent types.

use std::sync::Arc;

use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// Callback for executing a named tool with JSON arguments.
pub type ToolExecutorFn = Arc<
    dyn Fn(
            String,
            serde_json::Value,
        ) -> std::pin::Pin<
            Box<dyn std::future::Future<Output = Result<serde_json::Value, String>> + Send>,
        > + Send
        + Sync,
>;

/// Configuration for a ReAct agent.
#[derive(Clone)]
pub struct ReactConfig {
    /// Maximum number of ReAct steps before giving up.
    pub max_steps: usize,
    /// Stop sequences that trigger early termination.
    pub stop_sequences: Vec<String>,
    /// Optional tool executor callback. If `None`, tool calls return an error.
    pub tool_executor: Option<ToolExecutorFn>,
}

impl Default for ReactConfig {
    fn default() -> Self {
        Self {
            max_steps: 10,
            stop_sequences: Vec::new(),
            tool_executor: None,
        }
    }
}

impl std::fmt::Debug for ReactConfig {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ReactConfig")
            .field("max_steps", &self.max_steps)
            .field("stop_sequences", &self.stop_sequences)
            .field(
                "tool_executor",
                &self
                    .tool_executor
                    .as_ref()
                    .map(|_| "...")
                    .unwrap_or("<none>"),
            )
            .finish()
    }
}

/// An action (tool call) requested by the ReAct agent.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Action {
    pub id: String,
    pub name: String,
    pub args: serde_json::Value,
}

impl Action {
    pub fn new(name: &str, args: serde_json::Value) -> Self {
        Self {
            id: Uuid::new_v4().to_string(),
            name: name.to_string(),
            args,
        }
    }
}

// ---------------------------------------------------------------------------
// Step events emitted during the ReAct loop
// ---------------------------------------------------------------------------

/// A single event emitted during a ReAct step.
///
/// These are yielded via the streaming callback so the caller (service layer)
/// can persist them to the database or forward them to a WebSocket client.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ReactStep {
    /// The AI's reasoning/thinking for this step.
    Thought { step: usize, thought: String },
    /// The AI requested a tool call.
    Action { step: usize, action: Action },
    /// Result returned by the executed tool.
    Observation { step: usize, observation: String },
    /// Final answer produced by the agent.
    Answer { step: usize, answer: String },
}
