// vapora-backend: Workflow YAML parser
// Phase 3: Parse workflow definitions from YAML

use std::fs;

use serde::{Deserialize, Serialize};
use thiserror::Error;

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

#[derive(Debug, Error)]
pub enum ParserError {
    #[error("Failed to read file: {0}")]
    FileError(#[from] std::io::Error),

    #[error("Failed to parse YAML: {0}")]
    YamlError(#[from] serde_yaml::Error),

    #[error("Invalid workflow definition: {0}")]
    ValidationError(String),
}

#[derive(Debug, Deserialize, Serialize)]
pub struct WorkflowYaml {
    pub workflow: WorkflowDef,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct WorkflowDef {
    pub id: String,
    pub title: String,
    pub phases: Vec<PhaseDef>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct PhaseDef {
    pub id: String,
    pub name: String,
    #[serde(default)]
    pub parallel: bool,
    #[serde(default = "default_estimated_hours")]
    pub estimated_hours: f32,
    pub steps: Vec<StepDef>,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct StepDef {
    pub id: String,
    pub name: String,
    pub agent: String,
    #[serde(default)]
    pub depends_on: Vec<String>,
    #[serde(default)]
    pub parallelizable: bool,
}

fn default_estimated_hours() -> f32 {
    1.0
}

pub struct WorkflowParser;

impl WorkflowParser {
    /// Parse workflow from YAML file
    pub fn parse_file(path: &str) -> Result<Workflow, ParserError> {
        let content = fs::read_to_string(path)?;
        Self::parse_string(&content)
    }

    /// Parse workflow from YAML string
    pub fn parse_string(yaml: &str) -> Result<Workflow, ParserError> {
        let workflow_yaml: WorkflowYaml = serde_yaml::from_str(yaml)?;
        Self::validate_and_convert(workflow_yaml)
    }

    /// Validate and convert YAML definition to runtime Workflow
    fn validate_and_convert(yaml: WorkflowYaml) -> Result<Workflow, ParserError> {
        let def = yaml.workflow;

        // Validate workflow has phases
        if def.phases.is_empty() {
            return Err(ParserError::ValidationError(
                "Workflow must have at least one phase".to_string(),
            ));
        }

        // Convert phases
        let mut phases = Vec::new();
        for phase_def in def.phases {
            // Validate phase has steps
            if phase_def.steps.is_empty() {
                return Err(ParserError::ValidationError(format!(
                    "Phase '{}' must have at least one step",
                    phase_def.id
                )));
            }

            // Convert steps
            let steps: Vec<WorkflowStep> = phase_def
                .steps
                .into_iter()
                .map(|step_def| WorkflowStep {
                    id: step_def.id,
                    name: step_def.name,
                    agent_role: step_def.agent,
                    status: StepStatus::Pending,
                    depends_on: step_def.depends_on,
                    can_parallelize: step_def.parallelizable,
                    started_at: None,
                    completed_at: None,
                    result: None,
                    error: None,
                })
                .collect();

            // Validate dependencies exist
            Self::validate_dependencies(&steps)?;

            phases.push(Phase {
                id: phase_def.id,
                name: phase_def.name,
                status: StepStatus::Pending,
                steps,
                parallel: phase_def.parallel,
                estimated_hours: phase_def.estimated_hours,
            });
        }

        Ok(Workflow::new(def.id, def.title, phases))
    }

    /// Validate that all step dependencies exist
    fn validate_dependencies(steps: &[WorkflowStep]) -> Result<(), ParserError> {
        let step_ids: std::collections::HashSet<_> = steps.iter().map(|s| &s.id).collect();

        for step in steps {
            for dep in &step.depends_on {
                if !step_ids.contains(dep) {
                    return Err(ParserError::ValidationError(format!(
                        "Step '{}' depends on non-existent step '{}'",
                        step.id, dep
                    )));
                }
            }
        }

        Ok(())
    }
}

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

    #[test]
    fn test_parse_workflow_yaml() {
        let yaml = r#"
workflow:
  id: feature-auth
  title: Implement MFA
  phases:
    - id: phase_1
      name: Design
      parallel: false
      estimated_hours: 2.0
      steps:
        - id: step_1_1
          name: Architect Design
          agent: architect
          depends_on: []
          parallelizable: false
    - id: phase_2
      name: Implementation
      parallel: true
      estimated_hours: 8.0
      steps:
        - id: step_2_1
          name: Backend API
          agent: developer
          depends_on: []
          parallelizable: true
        - id: step_2_2
          name: Frontend UI
          agent: developer
          depends_on: []
          parallelizable: true
"#;

        let result = WorkflowParser::parse_string(yaml);
        assert!(result.is_ok());

        let workflow = result.unwrap();
        assert_eq!(workflow.id, "feature-auth");
        assert_eq!(workflow.title, "Implement MFA");
        assert_eq!(workflow.phases.len(), 2);
        assert_eq!(workflow.phases[0].steps.len(), 1);
        assert_eq!(workflow.phases[1].steps.len(), 2);
        assert!(workflow.phases[1].parallel);
    }

    #[test]
    fn test_empty_phases_error() {
        let yaml = r#"
workflow:
  id: test
  title: Test
  phases: []
"#;

        let result = WorkflowParser::parse_string(yaml);
        assert!(result.is_err());
    }

    #[test]
    fn test_empty_steps_error() {
        let yaml = r#"
workflow:
  id: test
  title: Test
  phases:
    - id: phase_1
      name: Phase
      steps: []
"#;

        let result = WorkflowParser::parse_string(yaml);
        assert!(result.is_err());
    }

    #[test]
    fn test_invalid_dependency() {
        let yaml = r#"
workflow:
  id: test
  title: Test
  phases:
    - id: phase_1
      name: Phase
      steps:
        - id: step_1
          name: Step 1
          agent: developer
          depends_on: [nonexistent]
"#;

        let result = WorkflowParser::parse_string(yaml);
        assert!(result.is_err());
    }

    #[test]
    fn test_valid_dependencies() {
        let yaml = r#"
workflow:
  id: test
  title: Test
  phases:
    - id: phase_1
      name: Phase
      steps:
        - id: step_1
          name: Step 1
          agent: developer
          depends_on: []
        - id: step_2
          name: Step 2
          agent: developer
          depends_on: [step_1]
"#;

        let result = WorkflowParser::parse_string(yaml);
        assert!(result.is_ok());

        let workflow = result.unwrap();
        assert_eq!(workflow.phases[0].steps[1].depends_on, vec!["step_1"]);
    }
}
