// Per-agent execution loop with channel-based task distribution
// Phase 5.5: Persistence of execution history to KG for learning
// Each agent has dedicated executor managing its state machine

use std::sync::Arc;

use chrono::Utc;
use tokio::sync::mpsc;
use tracing::{debug, info, warn};
use vapora_knowledge_graph::{ExecutionRecord, KGPersistence, PersistedExecution};
use vapora_llm_router::EmbeddingProvider;

use super::state_machine::{Agent, ExecutionResult, Idle};
use crate::messages::TaskAssignment;
use crate::registry::AgentMetadata;

/// Per-agent executor handling task processing with persistence (Phase 5.5)
pub struct AgentExecutor {
    agent: Agent<Idle>,
    task_rx: mpsc::Receiver<TaskAssignment>,
    kg_persistence: Option<Arc<KGPersistence>>,
    embedding_provider: Option<Arc<dyn EmbeddingProvider>>,
}

impl AgentExecutor {
    /// Create new executor for an agent (Phase 4)
    pub fn new(metadata: AgentMetadata, task_rx: mpsc::Receiver<TaskAssignment>) -> Self {
        Self {
            agent: Agent::new(metadata),
            task_rx,
            kg_persistence: None,
            embedding_provider: None,
        }
    }

    /// Create executor with persistence (Phase 5.5)
    pub fn with_persistence(
        metadata: AgentMetadata,
        task_rx: mpsc::Receiver<TaskAssignment>,
        kg_persistence: Arc<KGPersistence>,
        embedding_provider: Arc<dyn EmbeddingProvider>,
    ) -> Self {
        Self {
            agent: Agent::new(metadata),
            task_rx,
            kg_persistence: Some(kg_persistence),
            embedding_provider: Some(embedding_provider),
        }
    }

    /// Run executor loop, processing tasks until channel closes
    pub async fn run(mut self) {
        info!(
            "AgentExecutor started for agent: {}",
            self.agent.metadata.id
        );
        let agent_id = self.agent.metadata.id.clone();

        while let Some(task) = self.task_rx.recv().await {
            debug!("Received task: {}", task.id);

            // Transition: Idle → Assigned
            let agent_assigned = self.agent.assign_task(task.clone());

            // Transition: Assigned → Executing
            let agent_executing = agent_assigned.start_execution();
            let execution_start = Utc::now();

            // Execute task (placeholder - in real use, call LLM via vapora-llm-router)
            let result = ExecutionResult {
                output: "Task executed successfully".to_string(),
                input_tokens: 100,
                output_tokens: 50,
                duration_ms: 500,
            };

            // Transition: Executing → Completed
            let completed_agent = agent_executing.complete(result.clone());

            // Handle result - transition Completed → Idle
            self.agent = completed_agent.reset();

            // Phase 5.5: Persist execution to Knowledge Graph (after state transition)
            self.persist_execution_internal(&task, &result, execution_start, &agent_id)
                .await;

            info!("Task {} completed", task.id);
        }

        info!("AgentExecutor stopped for agent: {}", agent_id);
    }

    /// Persist execution record to KG database (Phase 5.5)
    async fn persist_execution_internal(
        &self,
        task: &TaskAssignment,
        result: &ExecutionResult,
        execution_start: chrono::DateTime<Utc>,
        agent_id: &str,
    ) {
        if let Some(ref kg_persistence) = self.kg_persistence {
            if let Some(ref embedding_provider) = self.embedding_provider {
                // Generate embedding for task description
                let embedding = match embedding_provider.embed(&task.description).await {
                    Ok(emb) => emb,
                    Err(e) => {
                        warn!("Failed to generate embedding for task {}: {}", task.id, e);
                        // Use zero vector as fallback
                        vec![0.0; 1536]
                    }
                };

                // Create execution record for KG
                let execution_record = ExecutionRecord {
                    id: task.id.clone(),
                    task_id: task.id.clone(),
                    agent_id: agent_id.to_string(),
                    agent_role: None,
                    task_type: task.required_role.clone(),
                    description: task.description.clone(),
                    duration_ms: result.duration_ms,
                    input_tokens: result.input_tokens,
                    output_tokens: result.output_tokens,
                    cost_cents: 0,
                    provider: "unknown".to_string(),
                    success: true,
                    error: None,
                    solution: Some(result.output.clone()),
                    root_cause: None,
                    timestamp: execution_start,
                };

                // Convert to persisted format
                let persisted =
                    PersistedExecution::from_execution_record(&execution_record, embedding);

                // Persist to SurrealDB
                if let Err(e) = kg_persistence.persist_execution(persisted).await {
                    warn!("Failed to persist execution: {}", e);
                } else {
                    debug!("Persisted execution {} to KG", task.id);
                }

                // Record analytics event
                if let Err(e) = kg_persistence
                    .record_event(
                        "task_completed",
                        agent_id,
                        "duration_ms",
                        result.duration_ms as f64,
                    )
                    .await
                {
                    warn!("Failed to record event: {}", e);
                }

                // Record token usage event
                if let Err(e) = kg_persistence
                    .record_event(
                        "token_usage",
                        agent_id,
                        "tokens",
                        (result.input_tokens + result.output_tokens) as f64,
                    )
                    .await
                {
                    warn!("Failed to record token event: {}", e);
                }
            } else {
                warn!(
                    "KG persistence available but no embedding provider for task {}",
                    task.id
                );
            }
        }
    }
}

/// Internal state for executor result handling
pub enum ExecutorState {
    Completed(Agent<super::state_machine::Completed>),
    Failed(Agent<super::state_machine::Failed>, String),
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::registry::AgentStatus;

    #[tokio::test]
    async fn test_executor_creation() {
        let metadata = AgentMetadata {
            id: "test-executor".to_string(),
            role: "developer".to_string(),
            name: "Test Executor".to_string(),
            version: "0.1.0".to_string(),
            status: AgentStatus::Active,
            capabilities: vec!["code_generation".to_string()],
            llm_provider: "claude".to_string(),
            llm_model: "claude-sonnet-4".to_string(),
            max_concurrent_tasks: 3,
            current_tasks: 0,
            created_at: Utc::now(),
            last_heartbeat: Utc::now(),
            uptime_percentage: 100.0,
            total_tasks_completed: 0,
        };

        let (_tx, rx) = mpsc::channel(10);
        let executor = AgentExecutor::new(metadata.clone(), rx);

        assert_eq!(executor.agent.metadata.id, "test-executor");
        assert!(executor.kg_persistence.is_none());
    }

    #[test]
    fn test_executor_persistence_disabled_by_default() {
        let metadata = AgentMetadata {
            id: "test-no-persist".to_string(),
            role: "reviewer".to_string(),
            name: "Test No Persist".to_string(),
            version: "0.1.0".to_string(),
            status: AgentStatus::Active,
            capabilities: vec!["review".to_string()],
            llm_provider: "openai".to_string(),
            llm_model: "gpt-4".to_string(),
            max_concurrent_tasks: 5,
            current_tasks: 0,
            created_at: Utc::now(),
            last_heartbeat: Utc::now(),
            uptime_percentage: 99.5,
            total_tasks_completed: 100,
        };

        let (_tx, rx) = mpsc::channel(10);
        let executor = AgentExecutor::new(metadata, rx);

        assert!(!executor.agent.metadata.role.is_empty());
        assert_eq!(executor.embedding_provider.is_some(), false);
    }
}
