// vapora-backend: Workflow engine
// Phase 3: Orchestrate workflow execution with state management

use std::collections::HashMap;
use std::sync::Arc;

use thiserror::Error;
use tokio::sync::RwLock;
use tracing::{debug, error, info, warn};

use crate::workflow::executor::{ExecutorError, StepExecutor};
use crate::workflow::scheduler::{Scheduler, SchedulerError};
use crate::workflow::state::{StepStatus, Workflow, WorkflowStatus};

#[derive(Debug, Error)]
#[allow(dead_code)]
pub enum EngineError {
    #[error("Workflow not found: {0}")]
    WorkflowNotFound(String),

    #[error("Workflow state error: {0}")]
    StateError(String),

    #[error("Scheduler error: {0}")]
    SchedulerError(#[from] SchedulerError),

    #[error("Executor error: {0}")]
    ExecutorError(#[from] ExecutorError),

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

    #[error("Workflow validation failed: {0}")]
    ValidationError(String),
}

/// Workflow engine orchestrates workflow execution
#[allow(dead_code)]
pub struct WorkflowEngine {
    workflows: Arc<RwLock<HashMap<String, Workflow>>>,
    executor: Arc<StepExecutor>,
}

impl WorkflowEngine {
    /// Create new workflow engine
    pub fn new(executor: StepExecutor) -> Self {
        Self {
            workflows: Arc::new(RwLock::new(HashMap::new())),
            executor: Arc::new(executor),
        }
    }

    /// Register a workflow
    pub async fn register_workflow(&self, workflow: Workflow) -> Result<(), EngineError> {
        let mut workflows = self.workflows.write().await;

        if workflows.contains_key(&workflow.id) {
            return Err(EngineError::AlreadyRunning(workflow.id.clone()));
        }

        // Validate workflow structure
        for phase in &workflow.phases {
            Scheduler::validate_workflow(&phase.steps)?;
        }

        info!("Registered workflow: {} ({})", workflow.id, workflow.title);
        workflows.insert(workflow.id.clone(), workflow);
        Ok(())
    }

    /// Execute a workflow
    pub async fn execute_workflow(&self, workflow_id: &str) -> Result<Workflow, EngineError> {
        info!("Starting workflow execution: {}", workflow_id);

        // Get and transition workflow to planning
        {
            let mut workflows = self.workflows.write().await;
            let workflow = workflows
                .get_mut(workflow_id)
                .ok_or_else(|| EngineError::WorkflowNotFound(workflow_id.to_string()))?;

            workflow
                .transition(WorkflowStatus::Planning)
                .map_err(EngineError::StateError)?;
        }

        // Plan execution
        self.plan_workflow(workflow_id).await?;

        // Transition to in progress
        {
            let mut workflows = self.workflows.write().await;
            let workflow = workflows.get_mut(workflow_id).unwrap();

            workflow
                .transition(WorkflowStatus::InProgress)
                .map_err(EngineError::StateError)?;
        }

        // Execute phases
        self.execute_phases(workflow_id).await?;

        // Determine final state
        let final_workflow = {
            let mut workflows = self.workflows.write().await;
            let workflow = workflows.get_mut(workflow_id).unwrap();

            if workflow.all_steps_completed() {
                workflow
                    .transition(WorkflowStatus::Completed)
                    .map_err(EngineError::StateError)?;
                info!("Workflow {} completed successfully", workflow_id);
            } else if workflow.any_step_failed() {
                workflow
                    .transition(WorkflowStatus::Failed)
                    .map_err(EngineError::StateError)?;
                error!("Workflow {} failed", workflow_id);
            }

            workflow.clone()
        };

        Ok(final_workflow)
    }

    /// Plan workflow execution
    async fn plan_workflow(&self, workflow_id: &str) -> Result<(), EngineError> {
        debug!("Planning workflow: {}", workflow_id);

        let workflows = self.workflows.read().await;
        let workflow = workflows
            .get(workflow_id)
            .ok_or_else(|| EngineError::WorkflowNotFound(workflow_id.to_string()))?;

        // Validate all phases
        for phase in &workflow.phases {
            let execution_order = Scheduler::resolve_dependencies(&phase.steps)?;
            debug!(
                "Phase {} has {} execution levels",
                phase.id,
                execution_order.len()
            );
        }

        Ok(())
    }

    /// Execute all phases in workflow
    async fn execute_phases(&self, workflow_id: &str) -> Result<(), EngineError> {
        let phase_count = {
            let workflows = self.workflows.read().await;
            let workflow = workflows.get(workflow_id).unwrap();
            workflow.phases.len()
        };

        for phase_idx in 0..phase_count {
            self.execute_phase(workflow_id, phase_idx).await?;

            // Check if phase failed
            let phase_failed = {
                let workflows = self.workflows.read().await;
                let workflow = workflows.get(workflow_id).unwrap();
                workflow.phases[phase_idx]
                    .steps
                    .iter()
                    .any(|s| matches!(s.status, StepStatus::Failed))
            };

            if phase_failed {
                warn!("Phase {} failed, stopping workflow", phase_idx);
                break;
            }
        }

        Ok(())
    }

    /// Execute a single phase
    async fn execute_phase(&self, workflow_id: &str, phase_idx: usize) -> Result<(), EngineError> {
        let (phase_id, is_parallel) = {
            let workflows = self.workflows.read().await;
            let workflow = workflows.get(workflow_id).unwrap();
            let phase = &workflow.phases[phase_idx];
            (phase.id.clone(), phase.parallel)
        };

        info!("Executing phase: {} (parallel: {})", phase_id, is_parallel);

        if is_parallel {
            self.execute_phase_parallel(workflow_id, phase_idx).await?;
        } else {
            self.execute_phase_sequential(workflow_id, phase_idx)
                .await?;
        }

        Ok(())
    }

    /// Execute phase with parallel steps
    async fn execute_phase_parallel(
        &self,
        workflow_id: &str,
        phase_idx: usize,
    ) -> Result<(), EngineError> {
        // Get execution levels
        let execution_levels = {
            let workflows = self.workflows.read().await;
            let workflow = workflows.get(workflow_id).unwrap();
            let phase = &workflow.phases[phase_idx];
            Scheduler::resolve_dependencies(&phase.steps)?
        };

        // Execute each level
        for level_steps in execution_levels {
            debug!("Executing parallel level with {} steps", level_steps.len());

            // Execute all steps in this level in parallel
            for step_id in level_steps {
                // Get step index
                let step_idx = {
                    let workflows = self.workflows.read().await;
                    let workflow = workflows.get(workflow_id).unwrap();
                    let phase = &workflow.phases[phase_idx];
                    phase.steps.iter().position(|s| s.id == step_id)
                };

                if let Some(idx) = step_idx {
                    self.execute_step(workflow_id, phase_idx, idx).await?;
                }
            }
        }

        Ok(())
    }

    /// Execute phase with sequential steps
    async fn execute_phase_sequential(
        &self,
        workflow_id: &str,
        phase_idx: usize,
    ) -> Result<(), EngineError> {
        // Get execution order
        let execution_order = {
            let workflows = self.workflows.read().await;
            let workflow = workflows.get(workflow_id).unwrap();
            let phase = &workflow.phases[phase_idx];
            Scheduler::get_sequential_order(&phase.steps)?
        };

        // Execute steps in order
        for step_id in execution_order {
            let step_idx = {
                let workflows = self.workflows.read().await;
                let workflow = workflows.get(workflow_id).unwrap();
                let phase = &workflow.phases[phase_idx];
                phase.steps.iter().position(|s| s.id == step_id)
            };

            if let Some(idx) = step_idx {
                self.execute_step(workflow_id, phase_idx, idx).await?;

                // Check if step failed
                let step_failed = {
                    let workflows = self.workflows.read().await;
                    let workflow = workflows.get(workflow_id).unwrap();
                    matches!(
                        workflow.phases[phase_idx].steps[idx].status,
                        StepStatus::Failed
                    )
                };

                if step_failed {
                    return Err(EngineError::ExecutorError(ExecutorError::ExecutionFailed(
                        "Step failed".to_string(),
                    )));
                }
            }
        }

        Ok(())
    }

    /// Execute a single step
    async fn execute_step(
        &self,
        workflow_id: &str,
        phase_idx: usize,
        step_idx: usize,
    ) -> Result<(), EngineError> {
        let mut step = {
            let workflows = self.workflows.read().await;
            let workflow = workflows.get(workflow_id).unwrap();
            workflow.phases[phase_idx].steps[step_idx].clone()
        };

        // Execute step
        self.executor.execute_step(&mut step).await?;

        // Update workflow with step results
        {
            let mut workflows = self.workflows.write().await;
            let workflow = workflows.get_mut(workflow_id).unwrap();
            workflow.phases[phase_idx].steps[step_idx] = step;
        }

        Ok(())
    }

    /// Get workflow by ID
    pub async fn get_workflow(&self, workflow_id: &str) -> Option<Workflow> {
        let workflows = self.workflows.read().await;
        workflows.get(workflow_id).cloned()
    }

    /// List all workflows
    pub async fn list_workflows(&self) -> Vec<Workflow> {
        let workflows = self.workflows.read().await;
        workflows.values().cloned().collect()
    }

    /// Rollback a failed workflow
    pub async fn rollback_workflow(&self, workflow_id: &str) -> Result<(), EngineError> {
        let mut workflows = self.workflows.write().await;

        let workflow = workflows
            .get_mut(workflow_id)
            .ok_or_else(|| EngineError::WorkflowNotFound(workflow_id.to_string()))?;

        if !matches!(workflow.status, WorkflowStatus::Failed) {
            return Err(EngineError::StateError(
                "Can only rollback failed workflows".to_string(),
            ));
        }

        workflow
            .transition(WorkflowStatus::RolledBack)
            .map_err(EngineError::StateError)?;

        info!("Workflow {} rolled back", workflow_id);
        Ok(())
    }

    /// Remove workflow from engine
    pub async fn remove_workflow(&self, workflow_id: &str) -> Result<(), EngineError> {
        let mut workflows = self.workflows.write().await;

        workflows
            .remove(workflow_id)
            .ok_or_else(|| EngineError::WorkflowNotFound(workflow_id.to_string()))?;

        info!("Removed workflow: {}", workflow_id);
        Ok(())
    }
}

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

    use super::*;
    use crate::workflow::state::{Phase, WorkflowStep};

    fn create_test_workflow() -> Workflow {
        Workflow::new(
            "test-wf-1".to_string(),
            "Test Workflow".to_string(),
            vec![Phase {
                id: "phase1".to_string(),
                name: "Phase 1".to_string(),
                status: StepStatus::Pending,
                parallel: false,
                estimated_hours: 1.0,
                steps: vec![WorkflowStep {
                    id: "step1".to_string(),
                    name: "Step 1".to_string(),
                    agent_role: "developer".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_engine_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);
        let engine = WorkflowEngine::new(executor);

        assert!(engine.list_workflows().await.is_empty());
    }

    #[tokio::test]
    async fn test_register_workflow() {
        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 engine = WorkflowEngine::new(executor);

        let workflow = create_test_workflow();
        let result = engine.register_workflow(workflow).await;

        assert!(result.is_ok());
        assert_eq!(engine.list_workflows().await.len(), 1);
    }

    #[tokio::test]
    async fn test_workflow_not_found() {
        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 engine = WorkflowEngine::new(executor);

        let result = engine.get_workflow("nonexistent").await;
        assert!(result.is_none());
    }

    #[tokio::test]
    async fn test_rollback_requires_failed_state() {
        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 engine = WorkflowEngine::new(executor);

        let workflow = create_test_workflow();
        let id = workflow.id.clone();
        engine.register_workflow(workflow).await.unwrap();

        let result = engine.rollback_workflow(&id).await;
        assert!(result.is_err());
    }
}
