/// Tool System - Event-driven tool dispatcher
/// 
/// The ToolSystem listens for ToolCallRequested events and dispatches
/// tool execution asynchronously. It emits ToolCallStarted, ToolCallCompleted,
/// ToolCallFailed, and ToolCallRetrying events.

use crate::registry::ToolRegistry;
use crate::cache::ToolCache;
use crate::tool::Tool;
use async_trait::async_trait;
use lumina_engine::{Event, EventBus, EventHandler, EventType, EventError};
use serde_json::Value;
use std::sync::Arc;
use tokio::time::{timeout, Duration, Instant};
use tracing::{error, info};
use uuid::Uuid;

/// Tool System - Event-driven tool execution dispatcher
/// 
/// This system:
/// 1. Listens for ToolCallRequested events
/// 2. Dispatches tool execution asynchronously
/// 3. Emits appropriate events (Started, Completed, Failed, Retrying)
/// 4. Handles retries, timeouts, and caching
pub struct ToolSystem {
    registry: Arc<ToolRegistry>,
    cache: Arc<ToolCache>,
    event_bus: Arc<EventBus>,
    default_timeout_ms: u64,
    max_retries: u32,
    retry_delay_ms: u64,
}

impl ToolSystem {
    /// Create a new ToolSystem
    /// 
    /// # Arguments
    /// * `registry` - Tool registry
    /// * `cache` - Tool result cache
    /// * `event_bus` - Event bus for emitting events
    /// * `default_timeout_ms` - Default timeout in milliseconds
    /// * `max_retries` - Maximum retry attempts
    /// * `retry_delay_ms` - Delay between retries in milliseconds
    pub fn new(
        registry: Arc<ToolRegistry>,
        cache: Arc<ToolCache>,
        event_bus: Arc<EventBus>,
        default_timeout_ms: u64,
        max_retries: u32,
        retry_delay_ms: u64,
    ) -> Self {
        Self {
            registry,
            cache,
            event_bus,
            default_timeout_ms,
            max_retries,
            retry_delay_ms,
        }
    }
    
    /// Handle a tool request event
    /// 
    /// This is the main entry point called by the event system.
    /// It extracts tool information from the event, executes the tool,
    /// and emits result events.
    /// 
    /// # Arguments
    /// * `event` - The ToolCallRequested event
    /// 
    /// # Returns
    /// * `Vec<Event>` - Events to emit (Started, Completed/Failed)
    pub async fn handle_tool_request(&self, event: &Event) -> Vec<Event> {
        let mut result_events = Vec::new();
        
        // Extract tool information from event payload
        let tool_name = match event.payload.get("tool_name") {
            Some(Value::String(name)) => name.as_str(),
            _ => {
                error!("ToolCallRequested event missing 'tool_name' in payload");
                return result_events;
            }
        };
        
        let input = event.payload.get("input").cloned().unwrap_or(Value::Null);
        let execution_id = Uuid::new_v4();
        
        // Emit ToolCallStarted event
        let started_event = Event::new(
            EventType::ToolCallStarted,
            "tool_system".to_string(),
            serde_json::json!({
                "tool_name": tool_name,
                "execution_id": execution_id.to_string(),
            }),
            event.seed,
        )
        .with_parent(event.id);
        
        result_events.push(started_event.clone());
        
        // Check cache first
        if let Some(cached_result) = self.cache.get(tool_name, &input, event.seed).await {
            info!("Tool call cache hit: {} (execution: {})", tool_name, execution_id);
            
            let completed_event = Event::new(
                EventType::ToolCallCompleted,
                "tool_system".to_string(),
                serde_json::json!({
                    "tool_name": tool_name,
                    "execution_id": execution_id.to_string(),
                    "output": cached_result,
                    "cached": true,
                }),
                event.seed,
            )
            .with_parent(started_event.id);
            
            result_events.push(completed_event);
            return result_events;
        }
        
        // Get tool from registry
        let tool = match self.registry.get(tool_name).await {
            Some(tool) => tool,
            None => {
                error!("Tool '{}' not found in registry", tool_name);
                
                let failed_event = Event::new(
                    EventType::ToolCallFailed,
                    "tool_system".to_string(),
                    serde_json::json!({
                        "tool_name": tool_name,
                        "execution_id": execution_id.to_string(),
                        "error": format!("Tool '{}' not found", tool_name),
                    }),
                    event.seed,
                )
                .with_parent(started_event.id);
                
                result_events.push(failed_event);
                return result_events;
            }
        };
        
        // Execute tool with retries
        let input_clone = input.clone();
        let execution_result = self.execute_with_retries(
            tool.as_ref(),
            input,
            event,
            &started_event,
            execution_id,
            tool_name,
        ).await;
        
        // Emit completion or failure event
        match execution_result {
            Ok(output) => {
                // Cache successful result
                self.cache.set(tool_name, &input_clone, event.seed, output.clone()).await;
                
                let completed_event = Event::new(
                    EventType::ToolCallCompleted,
                    "tool_system".to_string(),
                    serde_json::json!({
                        "tool_name": tool_name,
                        "execution_id": execution_id.to_string(),
                        "output": output,
                        "cached": false,
                    }),
                    event.seed,
                )
                .with_parent(started_event.id);
                
                result_events.push(completed_event);
            }
            Err(error_msg) => {
                let failed_event = Event::new(
                    EventType::ToolCallFailed,
                    "tool_system".to_string(),
                    serde_json::json!({
                        "tool_name": tool_name,
                        "execution_id": execution_id.to_string(),
                        "error": error_msg,
                    }),
                    event.seed,
                )
                .with_parent(started_event.id);
                
                result_events.push(failed_event);
            }
        }
        
        result_events
    }
    
    /// Execute tool with retry logic
    async fn execute_with_retries(
        &self,
        tool: &dyn Tool,
        input: Value,
        original_event: &Event,
        started_event: &Event,
        execution_id: Uuid,
        tool_name: &str,
    ) -> Result<Value, String> {
        let mut last_error = None;
        
        for attempt in 0..=self.max_retries {
            if attempt > 0 {
                info!(
                    "Retrying tool '{}' (attempt {}/{})",
                    tool_name,
                    attempt + 1,
                    self.max_retries + 1
                );
                
                // Emit retry event
                let retry_event = Event::new(
                    EventType::ToolCallRetrying,
                    "tool_system".to_string(),
                    serde_json::json!({
                        "tool_name": tool_name,
                        "execution_id": execution_id.to_string(),
                        "attempt": attempt,
                    }),
                    original_event.seed,
                )
                .with_parent(started_event.id);
                
                // Publish retry event (fire and forget)
                let event_bus = self.event_bus.clone();
                tokio::spawn(async move {
                    if let Err(e) = event_bus.publish(retry_event).await {
                        error!("Failed to publish retry event: {}", e);
                    }
                });
                
                // Wait before retry
                tokio::time::sleep(Duration::from_millis(self.retry_delay_ms)).await;
            }
            
            // Execute with timeout
            let start_time = Instant::now();
            let execution_result = timeout(
                Duration::from_millis(self.default_timeout_ms),
                tool.execute(input.clone(), original_event),
            )
            .await;
            
            match execution_result {
                Ok(Ok(output)) => {
                    let execution_time = start_time.elapsed().as_millis() as u64;
                    info!(
                        "Tool '{}' executed successfully in {}ms (attempt {})",
                        tool_name,
                        execution_time,
                        attempt + 1
                    );
                    return Ok(output);
                }
                Ok(Err(e)) => {
                    last_error = Some(e);
                    error!("Tool '{}' execution failed: {}", tool_name, last_error.as_ref().unwrap());
                }
                Err(_) => {
                    last_error = Some(format!("Tool '{}' execution timed out after {}ms", tool_name, self.default_timeout_ms));
                    error!("{}", last_error.as_ref().unwrap());
                }
            }
        }
        
        // All retries failed
        Err(last_error.unwrap_or_else(|| "Unknown error".to_string()))
    }
}

/// ToolSystem as EventHandler
/// 
/// This allows ToolSystem to be registered with the EventBus
/// to automatically handle ToolCallRequested events.
#[async_trait]
impl EventHandler for ToolSystem {
    async fn handle(&self, event: &Event) -> Result<Vec<Event>, EventError> {
        if matches!(event.event_type, EventType::ToolCallRequested) {
            let result_events = self.handle_tool_request(event).await;
            Ok(result_events)
        } else {
            Ok(Vec::new())
        }
    }
    
    fn interested_events(&self) -> Vec<EventType> {
        vec![EventType::ToolCallRequested]
    }
}

