// vapora-backend: Workflow state machine
// Phase 3: State management for workflow lifecycle

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum WorkflowStatus {
    Created,
    Planning,
    InProgress,
    Blocked,
    Completed,
    Failed,
    RolledBack,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub enum StepStatus {
    Pending,
    Running,
    Completed,
    Failed,
    Skipped,
    Blocked,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Workflow {
    pub id: String,
    pub title: String,
    pub status: WorkflowStatus,
    pub phases: Vec<Phase>,
    pub created_at: DateTime<Utc>,
    pub started_at: Option<DateTime<Utc>>,
    pub completed_at: Option<DateTime<Utc>>,
    pub estimated_completion: Option<DateTime<Utc>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Phase {
    pub id: String,
    pub name: String,
    pub status: StepStatus,
    pub steps: Vec<WorkflowStep>,
    pub parallel: bool,
    pub estimated_hours: f32,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkflowStep {
    pub id: String,
    pub name: String,
    pub agent_role: String,
    pub status: StepStatus,
    pub depends_on: Vec<String>,
    pub can_parallelize: bool,
    pub started_at: Option<DateTime<Utc>>,
    pub completed_at: Option<DateTime<Utc>>,
    pub result: Option<String>,
    pub error: Option<String>,
}

impl Default for WorkflowStep {
    fn default() -> Self {
        Self {
            id: String::new(),
            name: String::new(),
            agent_role: String::new(),
            status: StepStatus::Pending,
            depends_on: Vec::new(),
            can_parallelize: false,
            started_at: None,
            completed_at: None,
            result: None,
            error: None,
        }
    }
}

impl Workflow {
    /// Create a new workflow
    pub fn new(id: String, title: String, phases: Vec<Phase>) -> Self {
        Self {
            id,
            title,
            status: WorkflowStatus::Created,
            phases,
            created_at: Utc::now(),
            started_at: None,
            completed_at: None,
            estimated_completion: None,
        }
    }

    /// Check if transition is allowed
    pub fn can_transition(&self, to: &WorkflowStatus) -> bool {
        matches!(
            (&self.status, to),
            (WorkflowStatus::Created, WorkflowStatus::Planning)
                | (WorkflowStatus::Planning, WorkflowStatus::InProgress)
                | (WorkflowStatus::InProgress, WorkflowStatus::Completed)
                | (WorkflowStatus::InProgress, WorkflowStatus::Failed)
                | (WorkflowStatus::InProgress, WorkflowStatus::Blocked)
                | (WorkflowStatus::Blocked, WorkflowStatus::InProgress)
                | (WorkflowStatus::Failed, WorkflowStatus::RolledBack)
        )
    }

    /// Transition to new state
    pub fn transition(&mut self, to: WorkflowStatus) -> Result<(), String> {
        if !self.can_transition(&to) {
            return Err(format!(
                "Cannot transition from {:?} to {:?}",
                self.status, to
            ));
        }

        match &to {
            WorkflowStatus::InProgress => {
                self.started_at = Some(Utc::now());
            }
            WorkflowStatus::Completed | WorkflowStatus::Failed | WorkflowStatus::RolledBack => {
                self.completed_at = Some(Utc::now());
            }
            _ => {}
        }

        self.status = to;
        Ok(())
    }

    /// Check if all steps are completed
    pub fn all_steps_completed(&self) -> bool {
        self.phases.iter().all(|p| {
            p.steps
                .iter()
                .all(|s| matches!(s.status, StepStatus::Completed | StepStatus::Skipped))
        })
    }

    /// Check if any step has failed
    pub fn any_step_failed(&self) -> bool {
        self.phases.iter().any(|p| {
            p.steps
                .iter()
                .any(|s| matches!(s.status, StepStatus::Failed))
        })
    }

    /// Get workflow progress percentage
    pub fn progress_percent(&self) -> u32 {
        let total_steps: usize = self.phases.iter().map(|p| p.steps.len()).sum();
        if total_steps == 0 {
            return 0;
        }

        let completed_steps: usize = self
            .phases
            .iter()
            .flat_map(|p| &p.steps)
            .filter(|s| matches!(s.status, StepStatus::Completed | StepStatus::Skipped))
            .count();

        ((completed_steps as f64 / total_steps as f64) * 100.0) as u32
    }
}

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

    #[test]
    fn test_workflow_creation() {
        let workflow = Workflow::new("wf-1".to_string(), "Test Workflow".to_string(), vec![]);

        assert_eq!(workflow.id, "wf-1");
        assert_eq!(workflow.status, WorkflowStatus::Created);
        assert!(workflow.started_at.is_none());
    }

    #[test]
    fn test_valid_transitions() {
        let mut workflow = Workflow::new("wf-1".to_string(), "Test".to_string(), vec![]);

        assert!(workflow.transition(WorkflowStatus::Planning).is_ok());
        assert_eq!(workflow.status, WorkflowStatus::Planning);

        assert!(workflow.transition(WorkflowStatus::InProgress).is_ok());
        assert_eq!(workflow.status, WorkflowStatus::InProgress);
        assert!(workflow.started_at.is_some());

        assert!(workflow.transition(WorkflowStatus::Completed).is_ok());
        assert_eq!(workflow.status, WorkflowStatus::Completed);
        assert!(workflow.completed_at.is_some());
    }

    #[test]
    fn test_invalid_transition() {
        let mut workflow = Workflow::new("wf-1".to_string(), "Test".to_string(), vec![]);

        let result = workflow.transition(WorkflowStatus::Completed);
        assert!(result.is_err());
    }

    #[test]
    fn test_progress_calculation() {
        let mut workflow = Workflow::new(
            "wf-1".to_string(),
            "Test".to_string(),
            vec![Phase {
                id: "p1".to_string(),
                name: "Phase 1".to_string(),
                status: StepStatus::Running,
                parallel: false,
                estimated_hours: 2.0,
                steps: vec![
                    WorkflowStep {
                        id: "s1".to_string(),
                        status: StepStatus::Completed,
                        ..Default::default()
                    },
                    WorkflowStep {
                        id: "s2".to_string(),
                        status: StepStatus::Running,
                        ..Default::default()
                    },
                ],
            }],
        );

        assert_eq!(workflow.progress_percent(), 50);

        workflow.phases[0].steps[1].status = StepStatus::Completed;
        assert_eq!(workflow.progress_percent(), 100);
    }
}
