//! Workflow definition types for approval workflows.
//!
//! Defines the structure of approval workflows including stages,
//! required approvers, timeouts, and escalation paths.

use serde::{Deserialize, Serialize};

use crate::evidence::EvidenceLevel;
use crate::scope::ScopeLevel;

/// Default minimum approvals required per stage.
const fn default_min_approvals() -> usize {
    1
}

/// A single approval stage in a workflow.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApprovalStage {
    /// Stage identifier (e.g., "security_review").
    pub name: String,

    /// Human-readable label (e.g., "Security Review").
    pub label: String,

    /// Teams or individuals who can approve this stage.
    #[serde(default)]
    pub required_approvers: Vec<String>,

    /// Minimum number of approvals needed to pass this stage.
    #[serde(default = "default_min_approvals")]
    pub min_approvals: usize,

    /// Optional timeout in hours for this stage.
    #[serde(default)]
    pub timeout_hours: Option<u32>,

    /// Optional stage to escalate to on timeout.
    #[serde(default)]
    pub escalate_to: Option<String>,

    /// Auto-approve if pattern has this evidence level or higher.
    #[serde(default)]
    pub auto_approve_evidence_level: Option<EvidenceLevel>,
}

impl ApprovalStage {
    /// Create a new approval stage.
    pub fn new(name: impl Into<String>, label: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            label: label.into(),
            required_approvers: Vec::new(),
            min_approvals: 1,
            timeout_hours: None,
            escalate_to: None,
            auto_approve_evidence_level: None,
        }
    }

    /// Set required approvers.
    pub fn with_approvers(mut self, approvers: Vec<String>) -> Self {
        self.required_approvers = approvers;
        self
    }

    /// Set minimum approvals.
    pub fn with_min_approvals(mut self, min: usize) -> Self {
        self.min_approvals = min;
        self
    }

    /// Set timeout in hours.
    pub fn with_timeout(mut self, hours: u32) -> Self {
        self.timeout_hours = Some(hours);
        self
    }

    /// Set escalation target.
    pub fn with_escalation(mut self, stage: impl Into<String>) -> Self {
        self.escalate_to = Some(stage.into());
        self
    }

    /// Set auto-approve evidence level.
    pub fn with_auto_approve_level(mut self, level: EvidenceLevel) -> Self {
        self.auto_approve_evidence_level = Some(level);
        self
    }

    /// Check if a user is an authorized approver for this stage.
    pub fn is_authorized(&self, approver: &str) -> bool {
        if self.required_approvers.is_empty() {
            // No restrictions, anyone can approve
            true
        } else {
            self.required_approvers.iter().any(|a| a == approver)
        }
    }

    /// Check if this stage should auto-approve based on evidence level.
    pub fn should_auto_approve(&self, evidence_level: EvidenceLevel) -> bool {
        self.auto_approve_evidence_level.map(|required| evidence_level >= required).unwrap_or(false)
    }
}

impl Default for ApprovalStage {
    fn default() -> Self {
        Self::new("default", "Default Stage")
    }
}

/// An approval workflow template.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApprovalWorkflow {
    /// Workflow identifier.
    pub name: String,

    /// Human-readable description.
    pub description: String,

    /// Ordered list of approval stages.
    #[serde(default)]
    pub stages: Vec<ApprovalStage>,

    /// Optional scope level this workflow applies to.
    #[serde(default)]
    pub applies_to_scope: Option<ScopeLevel>,

    /// Apply this workflow for patterns below this evidence level.
    #[serde(default)]
    pub applies_to_evidence_below: Option<EvidenceLevel>,

    /// Overall timeout for the entire workflow in hours.
    #[serde(default)]
    pub overall_timeout_hours: Option<u32>,
}

impl ApprovalWorkflow {
    /// Create a new workflow.
    pub fn new(name: impl Into<String>, description: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            description: description.into(),
            stages: Vec::new(),
            applies_to_scope: None,
            applies_to_evidence_below: None,
            overall_timeout_hours: None,
        }
    }

    /// Add a stage to the workflow.
    pub fn add_stage(mut self, stage: ApprovalStage) -> Self {
        self.stages.push(stage);
        self
    }

    /// Set the scope level this workflow applies to.
    pub fn with_scope(mut self, scope: ScopeLevel) -> Self {
        self.applies_to_scope = Some(scope);
        self
    }

    /// Set the evidence level threshold.
    pub fn with_evidence_below(mut self, level: EvidenceLevel) -> Self {
        self.applies_to_evidence_below = Some(level);
        self
    }

    /// Set overall timeout.
    pub fn with_overall_timeout(mut self, hours: u32) -> Self {
        self.overall_timeout_hours = Some(hours);
        self
    }

    /// Get the first stage of the workflow.
    pub fn first_stage(&self) -> Option<&ApprovalStage> {
        self.stages.first()
    }

    /// Get the next stage after the current index.
    pub fn next_stage(&self, current_index: usize) -> Option<&ApprovalStage> {
        self.stages.get(current_index + 1)
    }

    /// Check if a stage index is the last stage.
    pub fn is_last_stage(&self, index: usize) -> bool {
        index >= self.stages.len().saturating_sub(1)
    }

    /// Get a stage by name.
    pub fn get_stage_by_name(&self, name: &str) -> Option<(usize, &ApprovalStage)> {
        self.stages.iter().enumerate().find(|(_, s)| s.name == name)
    }

    /// Get a stage by index.
    pub fn get_stage(&self, index: usize) -> Option<&ApprovalStage> {
        self.stages.get(index)
    }

    /// Check if this workflow applies to a pattern based on evidence level.
    pub fn applies_to_evidence(&self, level: EvidenceLevel) -> bool {
        match self.applies_to_evidence_below {
            Some(threshold) => level < threshold,
            None => true, // No threshold, applies to all
        }
    }

    /// Check if this workflow applies based on scope.
    pub fn applies_to_scope_level(&self, level: ScopeLevel) -> bool {
        match self.applies_to_scope {
            Some(required) => level == required,
            None => true, // No scope requirement, applies to all
        }
    }

    /// Check if the workflow should auto-approve at a given stage.
    pub fn should_auto_approve(&self, stage_index: usize, evidence_level: EvidenceLevel) -> bool {
        self.stages.get(stage_index).map(|s| s.should_auto_approve(evidence_level)).unwrap_or(false)
    }

    /// Validate the workflow configuration.
    pub fn validate(&self) -> Result<(), String> {
        if self.name.is_empty() {
            return Err("Workflow name cannot be empty".to_string());
        }

        if self.stages.is_empty() {
            return Err("Workflow must have at least one stage".to_string());
        }

        // Check that escalation targets exist
        for stage in &self.stages {
            if let Some(ref escalate_to) = stage.escalate_to {
                if self.get_stage_by_name(escalate_to).is_none() {
                    return Err(format!(
                        "Stage '{}' escalates to non-existent stage '{}'",
                        stage.name, escalate_to
                    ));
                }
            }
        }

        Ok(())
    }
}

impl Default for ApprovalWorkflow {
    fn default() -> Self {
        Self::new("default", "Default approval workflow")
            .add_stage(ApprovalStage::new("review", "Review").with_min_approvals(1))
    }
}

/// Create a standard review workflow.
///
/// Two stages: security review (48h) → architecture review (72h).
pub fn standard_review_workflow() -> ApprovalWorkflow {
    ApprovalWorkflow::new("standard_review", "Standard pattern review for production promotion")
        .with_overall_timeout(168) // 1 week
        .add_stage(
            ApprovalStage::new("security_review", "Security Review")
                .with_approvers(vec!["security-team".to_string()])
                .with_timeout(48)
                .with_escalation("architecture_review"),
        )
        .add_stage(
            ApprovalStage::new("architecture_review", "Architecture Review")
                .with_approvers(vec!["arch-team".to_string()])
                .with_timeout(72)
                .with_auto_approve_level(EvidenceLevel::Standard),
        )
}

/// Create a fast-track workflow for high-evidence patterns.
///
/// Single stage with auto-approval for Standard evidence or higher.
pub fn fast_track_workflow() -> ApprovalWorkflow {
    ApprovalWorkflow::new("fast_track", "Fast-track for high-evidence patterns")
        .with_overall_timeout(24)
        .add_stage(
            ApprovalStage::new("quick_review", "Quick Review")
                .with_min_approvals(1)
                .with_timeout(24)
                .with_auto_approve_level(EvidenceLevel::Standard),
        )
}

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

    #[test]
    fn test_approval_stage_creation() {
        let stage = ApprovalStage::new("security", "Security Review")
            .with_approvers(vec!["sec-team".to_string()])
            .with_timeout(48);

        assert_eq!(stage.name, "security");
        assert_eq!(stage.label, "Security Review");
        assert_eq!(stage.timeout_hours, Some(48));
        assert!(stage.is_authorized("sec-team"));
        assert!(!stage.is_authorized("other"));
    }

    #[test]
    fn test_stage_auto_approve() {
        let stage =
            ApprovalStage::new("review", "Review").with_auto_approve_level(EvidenceLevel::Standard);

        assert!(stage.should_auto_approve(EvidenceLevel::Standard));
        assert!(stage.should_auto_approve(EvidenceLevel::ProductSpec));
        assert!(!stage.should_auto_approve(EvidenceLevel::Research));
        assert!(!stage.should_auto_approve(EvidenceLevel::Commit));
    }

    #[test]
    fn test_workflow_creation() {
        let workflow = ApprovalWorkflow::new("test", "Test workflow")
            .add_stage(ApprovalStage::new("stage1", "Stage 1"))
            .add_stage(ApprovalStage::new("stage2", "Stage 2"));

        assert_eq!(workflow.stages.len(), 2);
        assert!(workflow.first_stage().is_some());
        assert_eq!(workflow.first_stage().unwrap().name, "stage1");
    }

    #[test]
    fn test_workflow_navigation() {
        let workflow = ApprovalWorkflow::new("test", "Test")
            .add_stage(ApprovalStage::new("s1", "S1"))
            .add_stage(ApprovalStage::new("s2", "S2"))
            .add_stage(ApprovalStage::new("s3", "S3"));

        assert!(!workflow.is_last_stage(0));
        assert!(!workflow.is_last_stage(1));
        assert!(workflow.is_last_stage(2));

        assert_eq!(workflow.next_stage(0).unwrap().name, "s2");
        assert_eq!(workflow.next_stage(1).unwrap().name, "s3");
        assert!(workflow.next_stage(2).is_none());
    }

    #[test]
    fn test_workflow_validation() {
        let empty = ApprovalWorkflow::new("empty", "").stages;
        let workflow =
            ApprovalWorkflow { name: "test".into(), stages: empty, ..Default::default() };
        assert!(workflow.validate().is_err());

        let workflow = ApprovalWorkflow::new("", "").add_stage(ApprovalStage::new("s1", "S1"));
        assert!(workflow.validate().is_err());

        let workflow = ApprovalWorkflow::new("test", "Test")
            .add_stage(ApprovalStage::new("s1", "S1").with_escalation("nonexistent"));
        assert!(workflow.validate().is_err());

        let workflow =
            ApprovalWorkflow::new("test", "Test").add_stage(ApprovalStage::new("s1", "S1"));
        assert!(workflow.validate().is_ok());
    }

    #[test]
    fn test_standard_review_workflow() {
        let workflow = standard_review_workflow();
        assert!(workflow.validate().is_ok());
        assert_eq!(workflow.stages.len(), 2);
        assert_eq!(workflow.first_stage().unwrap().name, "security_review");
    }

    #[test]
    fn test_fast_track_workflow() {
        let workflow = fast_track_workflow();
        assert!(workflow.validate().is_ok());
        assert_eq!(workflow.stages.len(), 1);
        assert!(workflow.should_auto_approve(0, EvidenceLevel::Standard));
    }

    #[test]
    fn test_serialization_roundtrip() {
        let workflow = standard_review_workflow();
        let json = serde_json::to_string(&workflow).expect("serialize");
        let parsed: ApprovalWorkflow = serde_json::from_str(&json).expect("deserialize");

        assert_eq!(parsed.name, workflow.name);
        assert_eq!(parsed.stages.len(), workflow.stages.len());
    }
}
