//! Phase management for syntaxis

use crate::error::Result;
use crate::LifecycleError;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::VecDeque;

/// syntaxis phase
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum ProjectPhase {
    /// Initial creation phase
    Creation,
    /// Active development phase
    Devel,
    /// Update/enhancement phase
    Update,
    /// Code review phase
    Review,
    /// Status/health check phase
    Status,
    /// Publication/release phase
    Publish,
    /// Archive/deprecation phase
    Archive,
    /// MVP/prototype mode
    Mvp,
    /// Prototype mode
    Prototype,
}

impl std::fmt::Display for ProjectPhase {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Creation => write!(f, "creation"),
            Self::Devel => write!(f, "devel"),
            Self::Update => write!(f, "update"),
            Self::Review => write!(f, "review"),
            Self::Status => write!(f, "status"),
            Self::Publish => write!(f, "publish"),
            Self::Archive => write!(f, "archive"),
            Self::Mvp => write!(f, "mvp"),
            Self::Prototype => write!(f, "prototype"),
        }
    }
}

impl ProjectPhase {
    /// Parse phase from string
    #[allow(clippy::should_implement_trait)]
    pub fn from_str(s: &str) -> Result<Self> {
        match s.to_lowercase().as_str() {
            "creation" => Ok(Self::Creation),
            "devel" => Ok(Self::Devel),
            "update" => Ok(Self::Update),
            "review" => Ok(Self::Review),
            "status" => Ok(Self::Status),
            "publish" => Ok(Self::Publish),
            "archive" => Ok(Self::Archive),
            "mvp" => Ok(Self::Mvp),
            "prototype" => Ok(Self::Prototype),
            _ => Err(LifecycleError::config(format!("Unknown phase: {}", s))),
        }
    }

    /// All available phases
    pub fn all() -> &'static [ProjectPhase] {
        &[
            Self::Creation,
            Self::Devel,
            Self::Update,
            Self::Review,
            Self::Status,
            Self::Publish,
            Self::Archive,
            Self::Mvp,
            Self::Prototype,
        ]
    }

    /// Get next logical phases
    pub fn next_phases(&self) -> Vec<ProjectPhase> {
        match self {
            Self::Creation => vec![Self::Devel, Self::Mvp, Self::Prototype],
            Self::Devel => vec![Self::Review, Self::Update],
            Self::Update => vec![Self::Review, Self::Devel],
            Self::Review => vec![Self::Publish, Self::Devel],
            Self::Status => vec![Self::Devel, Self::Update],
            Self::Publish => vec![Self::Update, Self::Status],
            Self::Archive => vec![],
            Self::Mvp => vec![Self::Devel, Self::Archive],
            Self::Prototype => vec![Self::Devel, Self::Archive],
        }
    }

    /// Check if phase is a final state
    pub fn is_final(&self) -> bool {
        matches!(self, Self::Archive)
    }
}

/// Phase transition record
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PhaseTransition {
    /// Source phase
    pub from: ProjectPhase,
    /// Target phase
    pub to: ProjectPhase,
    /// Timestamp of transition
    pub timestamp: DateTime<Utc>,
    /// Whether checklist was completed
    pub checklist_completed: bool,
    /// Optional notes
    pub notes: String,
}

impl PhaseTransition {
    /// Create a new phase transition
    pub fn new(from: ProjectPhase, to: ProjectPhase, checklist_completed: bool) -> Self {
        Self {
            from,
            to,
            timestamp: Utc::now(),
            checklist_completed,
            notes: String::new(),
        }
    }

    /// Add notes to transition
    pub fn with_notes(mut self, notes: impl Into<String>) -> Self {
        self.notes = notes.into();
        self
    }
}

/// Phase manager
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PhaseManager {
    /// Current phase
    current_phase: ProjectPhase,
    /// Phase history
    history: VecDeque<PhaseTransition>,
    /// Allowed transitions (if None, all transitions allowed)
    allowed_transitions: Option<Vec<(ProjectPhase, ProjectPhase)>>,
}

impl PhaseManager {
    /// Create a new phase manager
    pub fn new(initial_phase: ProjectPhase) -> Self {
        Self {
            current_phase: initial_phase,
            history: VecDeque::new(),
            allowed_transitions: None,
        }
    }

    /// Get current phase
    pub fn current_phase(&self) -> ProjectPhase {
        self.current_phase
    }

    /// Set allowed transitions
    pub fn set_allowed_transitions(&mut self, transitions: Vec<(ProjectPhase, ProjectPhase)>) {
        self.allowed_transitions = Some(transitions);
    }

    /// Check if transition is allowed
    pub fn can_transition(&self, to: ProjectPhase) -> bool {
        // Archive is final state
        if self.current_phase.is_final() {
            return false;
        }

        // Check if transition is in allowed list
        if let Some(ref allowed) = self.allowed_transitions {
            return allowed
                .iter()
                .any(|(from, target)| *from == self.current_phase && *target == to);
        }

        // If no restrictions, check logical next phases
        self.current_phase.next_phases().contains(&to)
    }

    /// Transition to a new phase
    pub fn transition_to(&mut self, to: ProjectPhase, checklist_completed: bool) -> Result<()> {
        if !self.can_transition(to) {
            return Err(LifecycleError::invalid_transition(
                self.current_phase.to_string(),
                to.to_string(),
                "Transition not allowed",
            ));
        }

        let transition = PhaseTransition::new(self.current_phase, to, checklist_completed);
        self.history.push_back(transition);
        self.current_phase = to;

        Ok(())
    }

    /// Get phase history
    pub fn history(&self) -> Vec<&PhaseTransition> {
        self.history.iter().collect()
    }

    /// Get transition count
    pub fn transition_count(&self) -> usize {
        self.history.len()
    }

    /// Get time in current phase
    pub fn time_in_current_phase(&self) -> Option<std::time::Duration> {
        self.history
            .back()
            .map(|t| {
                let now = Utc::now();
                (now - t.timestamp).to_std().unwrap_or_default()
            })
            .or(None)
    }
}

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

    #[test]
    fn test_phase_next_phases() {
        let phases = ProjectPhase::Devel.next_phases();
        assert!(phases.contains(&ProjectPhase::Review));
        assert!(phases.contains(&ProjectPhase::Update));
    }

    #[test]
    fn test_phase_manager_transition() {
        let mut manager = PhaseManager::new(ProjectPhase::Creation);
        assert_eq!(manager.current_phase(), ProjectPhase::Creation);

        assert!(manager.can_transition(ProjectPhase::Devel));
        manager.transition_to(ProjectPhase::Devel, false).unwrap();
        assert_eq!(manager.current_phase(), ProjectPhase::Devel);
    }

    #[test]
    fn test_phase_manager_invalid_transition() {
        let manager = PhaseManager::new(ProjectPhase::Archive);
        assert!(!manager.can_transition(ProjectPhase::Devel));
    }

    #[test]
    fn test_phase_manager_history() {
        let mut manager = PhaseManager::new(ProjectPhase::Creation);
        manager.transition_to(ProjectPhase::Devel, true).unwrap();
        manager.transition_to(ProjectPhase::Review, false).unwrap();

        assert_eq!(manager.transition_count(), 2);
        assert_eq!(manager.history().len(), 2);
    }
}
