// vapora-backend: Workflow step executor
// Phase 3: Execute workflow steps with agent coordination

use std::sync::Arc;

use chrono::Utc;
use thiserror::Error;
use tracing::{debug, error, info};
use vapora_agents::coordinator::AgentCoordinator;

use crate::workflow::state::{StepStatus, WorkflowStep};

#[derive(Debug, Error)]
#[allow(dead_code)]
pub enum ExecutorError {
    #[error("Agent coordinator error: {0}")]
    CoordinatorError(String),

    #[error("Step execution failed: {0}")]
    ExecutionFailed(String),

    #[error("Step already running: {0}")]
    AlreadyRunning(String),

    #[error("Invalid step state: expected {expected}, got {actual}")]
    InvalidState { expected: String, actual: String },
}

/// Step executor handles execution of individual workflow steps
#[allow(dead_code)]
pub struct StepExecutor {
    coordinator: Arc<AgentCoordinator>,
}

impl StepExecutor {
    /// Create new step executor
    pub fn new(coordinator: Arc<AgentCoordinator>) -> Self {
        Self { coordinator }
    }

    /// Execute a single step
    pub async fn execute_step(&self, step: &mut WorkflowStep) -> Result<(), ExecutorError> {
        // Validate step is pending
        if !matches!(step.status, StepStatus::Pending) {
            return Err(ExecutorError::InvalidState {
                expected: "Pending".to_string(),
                actual: format!("{:?}", step.status),
            });
        }

        info!("Executing step: {} ({})", step.id, step.name);

        // Mark step as running
        step.status = StepStatus::Running;
        step.started_at = Some(Utc::now());

        // Assign task to agent
        let result = self
            .coordinator
            .assign_task(
                &step.agent_role,
                step.name.clone(),
                format!("Workflow step: {}", step.id),
                "{}".to_string(),
                80, // Default priority
            )
            .await;

        match result {
            Ok(task_id) => {
                step.status = StepStatus::Completed;
                step.result = Some(format!("Task {} assigned successfully", task_id));
                step.completed_at = Some(Utc::now());

                info!("Step {} completed successfully", step.id);
                Ok(())
            }
            Err(e) => {
                step.status = StepStatus::Failed;
                step.error = Some(e.to_string());
                step.completed_at = Some(Utc::now());

                error!("Step {} failed: {}", step.id, e);
                Err(ExecutorError::ExecutionFailed(e.to_string()))
            }
        }
    }

    /// Execute multiple steps in parallel
    pub async fn execute_parallel(
        &self,
        steps: &mut [WorkflowStep],
    ) -> Result<Vec<Result<(), ExecutorError>>, ExecutorError> {
        debug!("Executing {} steps in parallel", steps.len());

        let mut handles = Vec::new();

        for step in steps.iter_mut() {
            // Clone step data for async execution
            let mut step_clone = step.clone();
            let coordinator = Arc::clone(&self.coordinator);

            let handle = tokio::spawn(async move {
                let temp_executor = StepExecutor::new(coordinator);
                temp_executor.execute_step(&mut step_clone).await
            });

            handles.push((handle, step));
        }

        let mut results = Vec::new();

        // Wait for all tasks and update original steps
        for (handle, _original_step) in handles {
            let result = handle
                .await
                .map_err(|e| ExecutorError::ExecutionFailed(e.to_string()))?;

            // Note: In a real implementation, we would need to update the original step
            // with the results. For now, we just collect the results.
            results.push(result);
        }

        Ok(results)
    }

    /// Execute steps sequentially
    pub async fn execute_sequential(
        &self,
        steps: &mut [WorkflowStep],
    ) -> Result<(), ExecutorError> {
        debug!("Executing {} steps sequentially", steps.len());

        for step in steps.iter_mut() {
            // Execute step
            self.execute_step(step).await?;

            // If step failed, stop execution
            if matches!(step.status, StepStatus::Failed) {
                return Err(ExecutorError::ExecutionFailed(format!(
                    "Step {} failed",
                    step.id
                )));
            }
        }

        Ok(())
    }

    /// Check if step can be executed (all dependencies met)
    pub fn can_execute(&self, step: &WorkflowStep, completed_steps: &[String]) -> bool {
        step.depends_on
            .iter()
            .all(|dep| completed_steps.contains(dep))
    }

    /// Get coordinator reference
    pub fn coordinator(&self) -> Arc<AgentCoordinator> {
        Arc::clone(&self.coordinator)
    }
}

#[cfg(test)]
mod tests {
    use vapora_agents::config::{AgentConfig, RegistryConfig};
    use vapora_agents::registry::AgentRegistry;

    use super::*;

    fn create_test_step(id: &str, role: &str) -> WorkflowStep {
        WorkflowStep {
            id: id.to_string(),
            name: format!("Test step {}", id),
            agent_role: role.to_string(),
            status: StepStatus::Pending,
            depends_on: vec![],
            can_parallelize: true,
            started_at: None,
            completed_at: None,
            result: None,
            error: None,
        }
    }

    #[tokio::test]
    async fn test_executor_creation() {
        let registry = Arc::new(AgentRegistry::new(5));
        let config = AgentConfig {
            registry: RegistryConfig {
                max_agents_per_role: 5,
                health_check_interval: 30,
                agent_timeout: 300,
            },
            agents: vec![],
        };
        let coordinator = Arc::new(AgentCoordinator::new(config, registry).await.unwrap());
        let executor = StepExecutor::new(coordinator);

        // Verify executor is created successfully
        assert!(executor
            .coordinator()
            .registry()
            .get_agent("nonexistent")
            .is_none());
    }

    #[tokio::test]
    async fn test_can_execute_with_dependencies() {
        let registry = Arc::new(AgentRegistry::new(5));
        let config = AgentConfig {
            registry: RegistryConfig {
                max_agents_per_role: 5,
                health_check_interval: 30,
                agent_timeout: 300,
            },
            agents: vec![],
        };
        let coordinator = Arc::new(AgentCoordinator::new(config, registry).await.unwrap());
        let executor = StepExecutor::new(coordinator);

        let mut step = create_test_step("step1", "developer");
        step.depends_on = vec!["step0".to_string()];

        // Should not be able to execute without dependency
        assert!(!executor.can_execute(&step, &[]));

        // Should be able to execute with dependency met
        assert!(executor.can_execute(&step, &["step0".to_string()]));
    }

    #[tokio::test]
    async fn test_invalid_state_transition() {
        let registry = Arc::new(AgentRegistry::new(5));
        let config = AgentConfig {
            registry: RegistryConfig {
                max_agents_per_role: 5,
                health_check_interval: 30,
                agent_timeout: 300,
            },
            agents: vec![],
        };
        let coordinator = Arc::new(AgentCoordinator::new(config, registry).await.unwrap());
        let executor = StepExecutor::new(coordinator);

        let mut step = create_test_step("step1", "developer");
        step.status = StepStatus::Completed; // Already completed

        let result = executor.execute_step(&mut step).await;
        assert!(result.is_err());
        assert!(matches!(
            result.unwrap_err(),
            ExecutorError::InvalidState { .. }
        ));
    }

    #[tokio::test]
    async fn test_step_execution_updates_timestamps() {
        let registry = Arc::new(AgentRegistry::new(5));

        // Register an agent
        let agent = vapora_agents::registry::AgentMetadata::new(
            "developer".to_string(),
            "Test Developer".to_string(),
            "claude".to_string(),
            "claude-sonnet-4".to_string(),
            vec!["coding".to_string()],
        );
        registry.register_agent(agent).unwrap();

        let config = AgentConfig {
            registry: RegistryConfig {
                max_agents_per_role: 5,
                health_check_interval: 30,
                agent_timeout: 300,
            },
            agents: vec![],
        };
        let coordinator = Arc::new(AgentCoordinator::new(config, registry).await.unwrap());
        let executor = StepExecutor::new(coordinator);

        let mut step = create_test_step("step1", "developer");

        assert!(step.started_at.is_none());
        assert!(step.completed_at.is_none());

        let result = executor.execute_step(&mut step).await;

        // Should succeed since we have a registered agent
        assert!(result.is_ok());
        assert!(step.started_at.is_some());
        assert!(step.completed_at.is_some());
        assert_eq!(step.status, StepStatus::Completed);
    }
}
