use crate::registry::{ToolContext, ToolError, ToolResult, ToolRegistry};
use crate::cache::ToolCache;
use lumina_engine::EventBus;
use lumina_engine::event::{Event, EventType};
use std::sync::Arc;
use tokio::sync::Semaphore;
use tokio::time::{timeout, Duration, Instant};
use tracing::{error, info, warn};
use uuid::Uuid;

/// Premium concurrent tool executor with worker pool
/// 
/// Features:
/// - Parallel tool execution with configurable concurrency limits
/// - Worker pool for better resource management
/// - Priority-based execution
/// - Timeout and cancellation support
pub struct ConcurrentToolExecutor {
    registry: Arc<ToolRegistry>,
    cache: Arc<ToolCache>,
    event_bus: Arc<EventBus>,
    default_timeout_ms: u64,
    max_retries: u32,
    retry_delay_ms: u64,
    /// Semaphore for controlling concurrent tool executions
    semaphore: Arc<Semaphore>,
    max_concurrent: usize,
}

impl ConcurrentToolExecutor {
    pub fn new(
        registry: Arc<ToolRegistry>,
        cache: Arc<ToolCache>,
        event_bus: Arc<EventBus>,
        default_timeout_ms: u64,
        max_retries: u32,
        retry_delay_ms: u64,
        max_concurrent: usize,
    ) -> Self {
        Self {
            registry,
            cache,
            event_bus,
            default_timeout_ms,
            max_retries,
            retry_delay_ms,
            semaphore: Arc::new(Semaphore::new(max_concurrent)),
            max_concurrent,
        }
    }
    
    /// Execute a tool call with retries, caching, and concurrency control
    pub async fn execute(
        &self,
        tool_id: &str,
        parameters: serde_json::Value,
        context: &ToolContext,
    ) -> Result<ToolResult, ToolError> {
        // Acquire semaphore permit for concurrency control
        let _permit = self.semaphore.acquire().await
            .map_err(|_| ToolError::ExecutionError("Failed to acquire execution permit".to_string()))?;
        
        let execution_id = Uuid::new_v4();
        let start_time = Instant::now();
        
        // Check cache first
        if let Some(cached_result) = self.cache.get(tool_id, &parameters, context.seed).await {
            info!("Tool call cache hit: {} (execution: {})", tool_id, execution_id);
            
            let result = ToolResult {
                tool_id: tool_id.to_string(),
                execution_id,
                success: true,
                output: cached_result,
                error: None,
                execution_time_ms: start_time.elapsed().as_millis() as u64,
                cached: true,
            };
            
            // Emit completion event
            self.emit_tool_completed(&result, context).await;
            
            return Ok(result);
        }
        
        // Get tool from registry
        let tool = self
            .registry
            .get(tool_id)
            .await
            .ok_or_else(|| ToolError::NotFound(tool_id.to_string()))?;
        
        // Emit request event
        self.emit_tool_requested(tool_id, &execution_id, context).await;
        
        // Create a synthetic event for the tool execution
        let synthetic_event = Event::new(
            EventType::ToolCallRequested,
            context.agent_id.clone(),
            serde_json::json!({
                "tool_name": tool_id,
                "input": parameters,
            }),
            context.seed,
        );
        
        // Execute with retries
        let mut last_error = None;
        for attempt in 0..=self.max_retries {
            if attempt > 0 {
                info!(
                    "Retrying tool {} (attempt {}/{})",
                    tool_id,
                    attempt + 1,
                    self.max_retries + 1
                );
                
                // Emit retry event
                self.emit_tool_retrying(tool_id, &execution_id, attempt, context).await;
                
                // Wait before retry
                tokio::time::sleep(Duration::from_millis(self.retry_delay_ms)).await;
            }
            
            // Execute with timeout
            let execution_result = timeout(
                Duration::from_millis(self.default_timeout_ms),
                tool.execute(parameters.clone(), &synthetic_event),
            )
            .await;
            
            match execution_result {
                Ok(Ok(output)) => {
                    // Success - cache and return
                    let execution_time = start_time.elapsed().as_millis() as u64;
                    
                    // Cache the result
                    self.cache
                        .set(tool_id, &parameters, context.seed, output.clone())
                        .await;
                    
                    let result = ToolResult {
                        tool_id: tool_id.to_string(),
                        execution_id,
                        success: true,
                        output,
                        error: None,
                        execution_time_ms: execution_time,
                        cached: false,
                    };
                    
                    // Emit completion event
                    self.emit_tool_completed(&result, context).await;
                    
                    info!(
                        "Tool {} executed successfully in {}ms (concurrent)",
                        tool_id, execution_time
                    );
                    
                    return Ok(result);
                }
                Ok(Err(e)) => {
                    last_error = Some(e);
                    error!("Tool {} execution failed: {:?}", tool_id, last_error);
                }
                Err(_) => {
                    last_error = Some(ToolError::Timeout.to_string());
                    error!("Tool {} execution timed out", tool_id);
                }
            }
        }
        
        // All retries failed
        let execution_time = start_time.elapsed().as_millis() as u64;
        let error_msg = last_error
            .map(|e| format!("{}", e))
            .unwrap_or_else(|| "Unknown error".to_string());
        
        let result = ToolResult {
            tool_id: tool_id.to_string(),
            execution_id,
            success: false,
            output: serde_json::Value::Null,
            error: Some(error_msg.clone()),
            execution_time_ms: execution_time,
            cached: false,
        };
        
        // Emit failure event
        self.emit_tool_failed(&result, context).await;
        
        Err(ToolError::ExecutionError(error_msg))
    }
    
    /// Execute multiple tools in parallel
    pub async fn execute_parallel(
        &self,
        tool_calls: Vec<(&str, serde_json::Value, ToolContext)>,
    ) -> Vec<Result<ToolResult, ToolError>> {
        let mut handles = Vec::new();
        
        for (tool_id, parameters, context) in tool_calls {
            let executor = self.clone();
            let handle = tokio::spawn(async move {
                executor.execute(tool_id, parameters, &context).await
            });
            handles.push(handle);
        }
        
        // Wait for all tools to complete
        let mut results = Vec::new();
        for handle in handles {
            match handle.await {
                Ok(result) => results.push(result),
                Err(e) => {
                    error!("Tool execution task panicked: {}", e);
                    results.push(Err(ToolError::ExecutionError(
                        format!("Task panicked: {}", e)
                    )));
                }
            }
        }
        
        results
    }
    
    /// Get current number of concurrent tool executions
    pub fn available_permits(&self) -> usize {
        self.semaphore.available_permits()
    }
    
    /// Get maximum concurrent tool executions
    pub fn max_concurrent(&self) -> usize {
        self.max_concurrent
    }
    
    async fn emit_tool_requested(
        &self,
        tool_id: &str,
        execution_id: &Uuid,
        context: &ToolContext,
    ) {
        let event = Event::new(
            EventType::ToolCallRequested,
            context.agent_id.clone(),
            serde_json::json!({
                "tool_id": tool_id,
                "execution_id": execution_id.to_string(),
            }),
            context.seed,
        );
        
        if let Err(e) = self.event_bus.publish(event).await {
            error!("Failed to emit tool requested event: {}", e);
        }
    }
    
    async fn emit_tool_retrying(
        &self,
        tool_id: &str,
        execution_id: &Uuid,
        attempt: u32,
        context: &ToolContext,
    ) {
        let event = Event::new(
            EventType::ToolCallRetrying,
            context.agent_id.clone(),
            serde_json::json!({
                "tool_id": tool_id,
                "execution_id": execution_id.to_string(),
                "attempt": attempt,
            }),
            context.seed,
        );
        
        if let Err(e) = self.event_bus.publish(event).await {
            error!("Failed to emit tool retry event: {}", e);
        }
    }
    
    async fn emit_tool_completed(&self, result: &ToolResult, context: &ToolContext) {
        let event = Event::new(
            EventType::ToolCallCompleted,
            context.agent_id.clone(),
            serde_json::json!({
                "tool_id": result.tool_id,
                "execution_id": result.execution_id.to_string(),
                "success": result.success,
                "cached": result.cached,
                "execution_time_ms": result.execution_time_ms,
            }),
            context.seed,
        );
        
        if let Err(e) = self.event_bus.publish(event).await {
            error!("Failed to emit tool completed event: {}", e);
        }
    }
    
    async fn emit_tool_failed(&self, result: &ToolResult, context: &ToolContext) {
        let event = Event::new(
            EventType::ToolCallFailed,
            context.agent_id.clone(),
            serde_json::json!({
                "tool_id": result.tool_id,
                "execution_id": result.execution_id.to_string(),
                "error": result.error,
            }),
            context.seed,
        );
        
        if let Err(e) = self.event_bus.publish(event).await {
            error!("Failed to emit tool failed event: {}", e);
        }
    }
}

impl Clone for ConcurrentToolExecutor {
    fn clone(&self) -> Self {
        Self {
            registry: self.registry.clone(),
            cache: self.cache.clone(),
            event_bus: self.event_bus.clone(),
            default_timeout_ms: self.default_timeout_ms,
            max_retries: self.max_retries,
            retry_delay_ms: self.retry_delay_ms,
            semaphore: self.semaphore.clone(),
            max_concurrent: self.max_concurrent,
        }
    }
}

