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

/// Agent event from the system
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentEvent {
    pub id: String,
    pub agent_id: String,
    pub event_type: EventType,
    pub duration_ms: Option<u64>,
    pub input_tokens: Option<u64>,
    pub output_tokens: Option<u64>,
    pub task_id: Option<String>,
    pub error: Option<String>,
    pub timestamp: DateTime<Utc>,
}

/// Type of agent event
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub enum EventType {
    TaskStarted,
    TaskProgress,
    TaskCompleted,
    TaskFailed,
    HeartbeatSent,
    ErrorOccurred,
    ExecutionWarning,
}

impl EventType {
    pub fn as_str(&self) -> &str {
        match self {
            EventType::TaskStarted => "task_started",
            EventType::TaskProgress => "task_progress",
            EventType::TaskCompleted => "task_completed",
            EventType::TaskFailed => "task_failed",
            EventType::HeartbeatSent => "heartbeat",
            EventType::ErrorOccurred => "error",
            EventType::ExecutionWarning => "warning",
        }
    }
}

/// Aggregated statistics from events
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EventAggregation {
    pub window_start: DateTime<Utc>,
    pub window_end: DateTime<Utc>,
    pub event_type: EventType,
    pub total_events: u64,
    pub distinct_agents: u32,
    pub avg_duration_ms: f64,
    pub error_count: u64,
    pub success_count: u64,
}

/// Alert triggered by analytics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Alert {
    pub id: String,
    pub level: AlertLevel,
    pub message: String,
    pub affected_agents: Vec<String>,
    pub affected_tasks: Vec<String>,
    pub triggered_at: DateTime<Utc>,
    pub resolution: Option<String>,
}

/// Alert severity level
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub enum AlertLevel {
    Info,
    Warning,
    Critical,
}

impl EventType {
    pub fn is_error(&self) -> bool {
        matches!(self, EventType::TaskFailed | EventType::ErrorOccurred)
    }

    pub fn is_success(&self) -> bool {
        matches!(self, EventType::TaskCompleted)
    }
}

impl AgentEvent {
    pub fn new_task_started(agent_id: String, task_id: String) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            agent_id,
            event_type: EventType::TaskStarted,
            duration_ms: None,
            input_tokens: None,
            output_tokens: None,
            task_id: Some(task_id),
            error: None,
            timestamp: Utc::now(),
        }
    }

    pub fn new_task_completed(
        agent_id: String,
        task_id: String,
        duration_ms: u64,
        input_tokens: u64,
        output_tokens: u64,
    ) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            agent_id,
            event_type: EventType::TaskCompleted,
            duration_ms: Some(duration_ms),
            input_tokens: Some(input_tokens),
            output_tokens: Some(output_tokens),
            task_id: Some(task_id),
            error: None,
            timestamp: Utc::now(),
        }
    }

    pub fn new_task_failed(agent_id: String, task_id: String, error: String) -> Self {
        Self {
            id: uuid::Uuid::new_v4().to_string(),
            agent_id,
            event_type: EventType::TaskFailed,
            duration_ms: None,
            input_tokens: None,
            output_tokens: None,
            task_id: Some(task_id),
            error: Some(error),
            timestamp: Utc::now(),
        }
    }
}

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

    #[test]
    fn test_event_type_is_error() {
        assert!(EventType::TaskFailed.is_error());
        assert!(EventType::ErrorOccurred.is_error());
        assert!(!EventType::TaskCompleted.is_error());
    }

    #[test]
    fn test_create_events() {
        let completed = AgentEvent::new_task_completed(
            "agent-1".to_string(),
            "task-1".to_string(),
            1000,
            100,
            50,
        );
        assert_eq!(completed.event_type, EventType::TaskCompleted);
        assert_eq!(completed.duration_ms, Some(1000));

        let failed = AgentEvent::new_task_failed(
            "agent-1".to_string(),
            "task-2".to_string(),
            "timeout".to_string(),
        );
        assert_eq!(failed.event_type, EventType::TaskFailed);
        assert!(failed.error.is_some());
    }
}
