// vapora-backend: Audit trail system
// Phase 3: Track all workflow events and actions

use std::sync::Arc;

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

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEntry {
    pub id: String,
    pub timestamp: DateTime<Utc>,
    pub workflow_id: String,
    pub event_type: String,
    pub actor: String,
    pub details: serde_json::Value,
}

impl AuditEntry {
    pub fn new(
        workflow_id: String,
        event_type: String,
        actor: String,
        details: serde_json::Value,
    ) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            timestamp: Utc::now(),
            workflow_id,
            event_type,
            actor,
            details,
        }
    }
}

/// Audit trail maintains history of workflow events
pub struct AuditTrail {
    entries: Arc<RwLock<Vec<AuditEntry>>>,
}

impl AuditTrail {
    pub fn new() -> Self {
        Self {
            entries: Arc::new(RwLock::new(Vec::new())),
        }
    }

    /// Log a workflow event
    pub async fn log_event(
        &self,
        workflow_id: String,
        event_type: String,
        actor: String,
        details: serde_json::Value,
    ) {
        let entry = AuditEntry::new(workflow_id, event_type, actor, details);
        let mut entries = self.entries.write().await;
        entries.push(entry);
    }

    /// Get audit entries for a workflow
    pub async fn get_workflow_audit(&self, workflow_id: &str) -> Vec<AuditEntry> {
        let entries = self.entries.read().await;
        entries
            .iter()
            .filter(|e| e.workflow_id == workflow_id)
            .cloned()
            .collect()
    }

    /// Get all audit entries
    pub async fn get_all_entries(&self) -> Vec<AuditEntry> {
        let entries = self.entries.read().await;
        entries.clone()
    }

    /// Get entries by event type
    pub async fn get_by_event_type(&self, event_type: &str) -> Vec<AuditEntry> {
        let entries = self.entries.read().await;
        entries
            .iter()
            .filter(|e| e.event_type == event_type)
            .cloned()
            .collect()
    }

    /// Get entries by actor
    pub async fn get_by_actor(&self, actor: &str) -> Vec<AuditEntry> {
        let entries = self.entries.read().await;
        entries
            .iter()
            .filter(|e| e.actor == actor)
            .cloned()
            .collect()
    }

    /// Clear all entries (for testing)
    pub async fn clear(&self) {
        let mut entries = self.entries.write().await;
        entries.clear();
    }
}

impl Default for AuditTrail {
    fn default() -> Self {
        Self::new()
    }
}

/// Event types for audit trail
pub mod events {
    pub const WORKFLOW_CREATED: &str = "workflow_created";
    pub const WORKFLOW_STARTED: &str = "workflow_started";
    pub const WORKFLOW_COMPLETED: &str = "workflow_completed";
    pub const WORKFLOW_FAILED: &str = "workflow_failed";
    pub const WORKFLOW_ROLLED_BACK: &str = "workflow_rolled_back";
    pub const PHASE_STARTED: &str = "phase_started";
    pub const PHASE_COMPLETED: &str = "phase_completed";
    pub const STEP_STARTED: &str = "step_started";
    pub const STEP_COMPLETED: &str = "step_completed";
    pub const STEP_FAILED: &str = "step_failed";
}

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

    #[tokio::test]
    async fn test_audit_trail_creation() {
        let audit = AuditTrail::new();
        assert!(audit.get_all_entries().await.is_empty());
    }

    #[tokio::test]
    async fn test_log_event() {
        let audit = AuditTrail::new();

        audit
            .log_event(
                "wf-1".to_string(),
                events::WORKFLOW_STARTED.to_string(),
                "system".to_string(),
                serde_json::json!({"test": "data"}),
            )
            .await;

        let entries = audit.get_all_entries().await;
        assert_eq!(entries.len(), 1);
        assert_eq!(entries[0].workflow_id, "wf-1");
        assert_eq!(entries[0].event_type, events::WORKFLOW_STARTED);
    }

    #[tokio::test]
    async fn test_get_workflow_audit() {
        let audit = AuditTrail::new();

        audit
            .log_event(
                "wf-1".to_string(),
                events::WORKFLOW_STARTED.to_string(),
                "system".to_string(),
                serde_json::json!({}),
            )
            .await;

        audit
            .log_event(
                "wf-2".to_string(),
                events::WORKFLOW_STARTED.to_string(),
                "system".to_string(),
                serde_json::json!({}),
            )
            .await;

        let entries = audit.get_workflow_audit("wf-1").await;
        assert_eq!(entries.len(), 1);
        assert_eq!(entries[0].workflow_id, "wf-1");
    }

    #[tokio::test]
    async fn test_filter_by_event_type() {
        let audit = AuditTrail::new();

        audit
            .log_event(
                "wf-1".to_string(),
                events::WORKFLOW_STARTED.to_string(),
                "system".to_string(),
                serde_json::json!({}),
            )
            .await;

        audit
            .log_event(
                "wf-1".to_string(),
                events::WORKFLOW_COMPLETED.to_string(),
                "system".to_string(),
                serde_json::json!({}),
            )
            .await;

        let entries = audit.get_by_event_type(events::WORKFLOW_STARTED).await;
        assert_eq!(entries.len(), 1);
        assert_eq!(entries[0].event_type, events::WORKFLOW_STARTED);
    }

    #[tokio::test]
    async fn test_filter_by_actor() {
        let audit = AuditTrail::new();

        audit
            .log_event(
                "wf-1".to_string(),
                events::WORKFLOW_STARTED.to_string(),
                "user-1".to_string(),
                serde_json::json!({}),
            )
            .await;

        audit
            .log_event(
                "wf-2".to_string(),
                events::WORKFLOW_STARTED.to_string(),
                "user-2".to_string(),
                serde_json::json!({}),
            )
            .await;

        let entries = audit.get_by_actor("user-1").await;
        assert_eq!(entries.len(), 1);
        assert_eq!(entries[0].actor, "user-1");
    }
}
