//! Core types for knowledge lifecycle management.
//!
//! These types track the lifecycle state of patterns through deprecation,
//! supersession, and archival with full audit trail.

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

/// Lifecycle status of a learned pattern.
///
/// Patterns move through this state machine as they age, become obsolete,
/// or are replaced by better alternatives.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum KnowledgeStatus {
    /// Pattern is active and should trigger matches.
    ///
    /// This is the default state for all patterns.
    #[default]
    Active,

    /// Pattern is deprecated but still active.
    ///
    /// Deprecated patterns continue to FLAG during scans but include
    /// migration guidance pointing to the replacement pattern.
    Deprecated {
        /// Reason for deprecation.
        reason: String,

        /// Pattern ID that supersedes this one (if any).
        #[serde(skip_serializing_if = "Option::is_none")]
        superseded_by: Option<Uuid>,

        /// When this pattern should be fully removed.
        #[serde(skip_serializing_if = "Option::is_none")]
        sunset_date: Option<DateTime<Utc>>,

        /// URL or text with migration guidance.
        #[serde(skip_serializing_if = "Option::is_none")]
        migration_guide: Option<String>,
    },

    /// Pattern was replaced by another and is no longer active.
    ///
    /// Superseded patterns do not match during scans; they exist only
    /// for historical reference and linking.
    Superseded {
        /// The pattern that replaced this one.
        replaced_by: Uuid,

        /// When this pattern was superseded.
        superseded_at: DateTime<Utc>,
    },

    /// Pattern is permanently archived (soft deleted).
    ///
    /// Archived patterns are not used for matching and are hidden
    /// from default listings. They remain for audit purposes.
    Archived {
        /// When this pattern was archived.
        archived_at: DateTime<Utc>,

        /// Reason for archival.
        reason: String,
    },
}

impl KnowledgeStatus {
    /// Check if this status is active (pattern should match during scans).
    pub fn is_active(&self) -> bool {
        matches!(self, Self::Active | Self::Deprecated { .. })
    }

    /// Check if this status is deprecated.
    pub fn is_deprecated(&self) -> bool {
        matches!(self, Self::Deprecated { .. })
    }

    /// Check if this status is a terminal state (superseded or archived).
    pub fn is_terminal(&self) -> bool {
        matches!(self, Self::Superseded { .. } | Self::Archived { .. })
    }

    /// Get the status name as a string.
    pub fn status_name(&self) -> &'static str {
        match self {
            Self::Active => "active",
            Self::Deprecated { .. } => "deprecated",
            Self::Superseded { .. } => "superseded",
            Self::Archived { .. } => "archived",
        }
    }

    /// Get the sunset date if deprecated.
    pub fn sunset_date(&self) -> Option<DateTime<Utc>> {
        match self {
            Self::Deprecated { sunset_date, .. } => *sunset_date,
            _ => None,
        }
    }

    /// Get the superseding pattern ID if deprecated or superseded.
    pub fn superseded_by(&self) -> Option<Uuid> {
        match self {
            Self::Deprecated { superseded_by, .. } => *superseded_by,
            Self::Superseded { replaced_by, .. } => Some(*replaced_by),
            _ => None,
        }
    }

    /// Check if this pattern is past its sunset date.
    pub fn is_past_sunset(&self) -> bool {
        self.sunset_date().map(|date| date <= Utc::now()).unwrap_or(false)
    }

    /// Days until sunset date (negative if past).
    pub fn days_until_sunset(&self) -> Option<i64> {
        self.sunset_date().map(|date| (date - Utc::now()).num_days())
    }
}

impl std::fmt::Display for KnowledgeStatus {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::Active => write!(f, "Active"),
            Self::Deprecated { reason, sunset_date, .. } => {
                write!(f, "Deprecated: {}", reason)?;
                if let Some(date) = sunset_date {
                    write!(f, " (sunset: {})", date.format("%Y-%m-%d"))?;
                }
                Ok(())
            }
            Self::Superseded { replaced_by, superseded_at } => {
                write!(f, "Superseded by {} on {}", replaced_by, superseded_at.format("%Y-%m-%d"))
            }
            Self::Archived { archived_at, reason } => {
                write!(f, "Archived on {}: {}", archived_at.format("%Y-%m-%d"), reason)
            }
        }
    }
}

/// Complete lifecycle metadata for a pattern.
///
/// Wraps the current status with creation timestamp and optional
/// additional metadata.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KnowledgeLifecycle {
    /// Current lifecycle status.
    #[serde(flatten)]
    pub status: KnowledgeStatus,

    /// When this lifecycle record was created.
    ///
    /// This is the creation of the lifecycle tracking, not the pattern itself.
    #[serde(default = "Utc::now")]
    pub lifecycle_created_at: DateTime<Utc>,

    /// When the status was last changed.
    #[serde(default = "Utc::now")]
    pub last_status_change: DateTime<Utc>,
}

impl Default for KnowledgeLifecycle {
    fn default() -> Self {
        let now = Utc::now();
        Self {
            status: KnowledgeStatus::default(),
            lifecycle_created_at: now,
            last_status_change: now,
        }
    }
}

impl KnowledgeLifecycle {
    /// Create a new lifecycle with Active status.
    pub fn new() -> Self {
        Self::default()
    }

    /// Create a lifecycle with a specific initial status.
    pub fn with_status(status: KnowledgeStatus) -> Self {
        let now = Utc::now();
        Self { status, lifecycle_created_at: now, last_status_change: now }
    }

    /// Update the status and record the change time.
    pub fn update_status(&mut self, new_status: KnowledgeStatus) {
        self.status = new_status;
        self.last_status_change = Utc::now();
    }

    /// Check if this lifecycle is active (pattern should match during scans).
    pub fn is_active(&self) -> bool {
        self.status.is_active()
    }

    /// Check if this lifecycle is deprecated.
    pub fn is_deprecated(&self) -> bool {
        self.status.is_deprecated()
    }

    /// Check if this lifecycle is in a terminal state (superseded or archived).
    pub fn is_terminal(&self) -> bool {
        self.status.is_terminal()
    }
}

/// Audit record for a status transition.
///
/// Every status change is recorded with full context for compliance
/// and debugging purposes.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StatusTransition {
    /// Unique identifier for this transition.
    pub id: Uuid,

    /// ID of the pattern whose status changed.
    pub pattern_id: Uuid,

    /// Status before the transition.
    pub from_status: KnowledgeStatus,

    /// Status after the transition.
    pub to_status: KnowledgeStatus,

    /// Who initiated this transition (username or system).
    pub initiated_by: String,

    /// When the transition occurred.
    pub timestamp: DateTime<Utc>,

    /// Optional comment explaining the transition.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub comment: Option<String>,
}

impl StatusTransition {
    /// Create a new status transition.
    pub fn new(
        pattern_id: Uuid,
        from_status: KnowledgeStatus,
        to_status: KnowledgeStatus,
        initiated_by: impl Into<String>,
        comment: Option<String>,
    ) -> Self {
        Self {
            id: Uuid::new_v4(),
            pattern_id,
            from_status,
            to_status,
            initiated_by: initiated_by.into(),
            timestamp: Utc::now(),
            comment,
        }
    }

    /// Get a human-readable description of this transition.
    pub fn description(&self) -> String {
        format!(
            "{} transitioned from {} to {}",
            self.pattern_id,
            self.from_status.status_name(),
            self.to_status.status_name()
        )
    }
}

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

    #[test]
    fn test_knowledge_status_default() {
        assert!(matches!(KnowledgeStatus::default(), KnowledgeStatus::Active));
    }

    #[test]
    fn test_knowledge_status_is_active() {
        assert!(KnowledgeStatus::Active.is_active());

        let deprecated = KnowledgeStatus::Deprecated {
            reason: "Test".to_string(),
            superseded_by: None,
            sunset_date: None,
            migration_guide: None,
        };
        assert!(deprecated.is_active()); // Deprecated patterns still match

        let superseded =
            KnowledgeStatus::Superseded { replaced_by: Uuid::new_v4(), superseded_at: Utc::now() };
        assert!(!superseded.is_active());

        let archived =
            KnowledgeStatus::Archived { archived_at: Utc::now(), reason: "Test".to_string() };
        assert!(!archived.is_active());
    }

    #[test]
    fn test_knowledge_status_is_terminal() {
        assert!(!KnowledgeStatus::Active.is_terminal());

        let deprecated = KnowledgeStatus::Deprecated {
            reason: "Test".to_string(),
            superseded_by: None,
            sunset_date: None,
            migration_guide: None,
        };
        assert!(!deprecated.is_terminal());

        let superseded =
            KnowledgeStatus::Superseded { replaced_by: Uuid::new_v4(), superseded_at: Utc::now() };
        assert!(superseded.is_terminal());

        let archived =
            KnowledgeStatus::Archived { archived_at: Utc::now(), reason: "Test".to_string() };
        assert!(archived.is_terminal());
    }

    #[test]
    fn test_knowledge_status_sunset_date() {
        let future = Utc::now() + chrono::Duration::days(30);
        let deprecated = KnowledgeStatus::Deprecated {
            reason: "Test".to_string(),
            superseded_by: None,
            sunset_date: Some(future),
            migration_guide: None,
        };

        assert_eq!(deprecated.sunset_date(), Some(future));
        assert!(!deprecated.is_past_sunset());
        assert!(deprecated.days_until_sunset().unwrap_or(0) > 0);

        let past = Utc::now() - chrono::Duration::days(1);
        let past_deprecated = KnowledgeStatus::Deprecated {
            reason: "Test".to_string(),
            superseded_by: None,
            sunset_date: Some(past),
            migration_guide: None,
        };
        assert!(past_deprecated.is_past_sunset());
        assert!(past_deprecated.days_until_sunset().unwrap_or(1) < 0);
    }

    #[test]
    fn test_knowledge_status_serde() {
        let active = KnowledgeStatus::Active;
        let json = serde_json::to_string(&active).expect("serialize");
        assert!(json.contains("\"status\":\"active\""));

        let deprecated = KnowledgeStatus::Deprecated {
            reason: "Outdated".to_string(),
            superseded_by: Some(Uuid::nil()),
            sunset_date: None,
            migration_guide: Some("https://example.com".to_string()),
        };
        let json = serde_json::to_string(&deprecated).expect("serialize");
        assert!(json.contains("\"status\":\"deprecated\""));
        assert!(json.contains("\"reason\":\"Outdated\""));
        assert!(json.contains("\"superseded_by\":\"00000000-0000-0000-0000-000000000000\""));
    }

    #[test]
    fn test_knowledge_lifecycle_default() {
        let lifecycle = KnowledgeLifecycle::default();
        assert!(lifecycle.is_active());
        assert!(!lifecycle.is_deprecated());
        assert!(!lifecycle.is_terminal());
    }

    #[test]
    fn test_knowledge_lifecycle_update_status() {
        let mut lifecycle = KnowledgeLifecycle::new();
        assert!(lifecycle.is_active());

        let deprecated = KnowledgeStatus::Deprecated {
            reason: "Test deprecation".to_string(),
            superseded_by: None,
            sunset_date: None,
            migration_guide: None,
        };
        lifecycle.update_status(deprecated);

        assert!(lifecycle.is_deprecated());
        assert!(lifecycle.is_active()); // Still matches
    }

    #[test]
    fn test_status_transition_creation() {
        let pattern_id = Uuid::new_v4();
        let from = KnowledgeStatus::Active;
        let to = KnowledgeStatus::Deprecated {
            reason: "Outdated".to_string(),
            superseded_by: None,
            sunset_date: None,
            migration_guide: None,
        };

        let transition = StatusTransition::new(
            pattern_id,
            from,
            to,
            "admin",
            Some("Deprecating old pattern".to_string()),
        );

        assert_eq!(transition.pattern_id, pattern_id);
        assert_eq!(transition.initiated_by, "admin");
        assert!(transition.comment.is_some());
    }

    #[test]
    fn test_status_transition_description() {
        let pattern_id = Uuid::new_v4();
        let transition = StatusTransition::new(
            pattern_id,
            KnowledgeStatus::Active,
            KnowledgeStatus::Archived { archived_at: Utc::now(), reason: "Done".to_string() },
            "system",
            None,
        );

        let desc = transition.description();
        assert!(desc.contains("active"));
        assert!(desc.contains("archived"));
    }
}
