//! WebSocket Events for Deployment Status Updates
//!
//! Publishes real-time deployment status updates to connected WebSocket clients

use std::sync::Arc;

use serde_json::json;
use uuid::Uuid;

use crate::handlers::websocket::{WebSocketEvent, WebSocketManager};

/// Deployment event types
#[derive(Debug, Clone, Copy)]
pub enum DeploymentEventType {
    /// Deployment plan created
    PlanCreated,
    /// Deployment submitted to orchestrator
    Submitted,
    /// Deployment started executing
    Started,
    /// Task completed
    TaskCompleted,
    /// Deployment completed successfully
    Completed,
    /// Deployment failed
    Failed,
    /// Status update (progress)
    StatusUpdate,
}

impl std::fmt::Display for DeploymentEventType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::PlanCreated => write!(f, "plan_created"),
            Self::Submitted => write!(f, "plan_submitted"),
            Self::Started => write!(f, "deployment_started"),
            Self::TaskCompleted => write!(f, "task_completed"),
            Self::Completed => write!(f, "deployment_completed"),
            Self::Failed => write!(f, "deployment_failed"),
            Self::StatusUpdate => write!(f, "status_update"),
        }
    }
}

/// Publish a deployment event to all connected clients (spawns background task)
pub fn publish_deployment_event(
    websocket_manager: Arc<WebSocketManager>,
    event_type: DeploymentEventType,
    plan_id: String,
    workflow_id: Option<String>,
    progress_percentage: u32,
    message: String,
) {
    let event = WebSocketEvent {
        event_type: event_type.to_string(),
        data: json!({
            "plan_id": plan_id,
            "workflow_id": workflow_id,
            "progress_percentage": progress_percentage,
            "message": message,
            "event_id": Uuid::new_v4().to_string(),
        }),
        timestamp: chrono::Utc::now(),
        target_user: None,
    };

    tokio::spawn(async move {
        websocket_manager.broadcast_event(event).await;
    });
}

/// Publish a deployment error event (spawns background task)
pub fn publish_deployment_error(
    websocket_manager: Arc<WebSocketManager>,
    plan_id: String,
    error_message: String,
) {
    let event = WebSocketEvent {
        event_type: DeploymentEventType::Failed.to_string(),
        data: json!({
            "plan_id": plan_id,
            "error": error_message,
            "event_id": Uuid::new_v4().to_string(),
        }),
        timestamp: chrono::Utc::now(),
        target_user: None,
    };

    tokio::spawn(async move {
        websocket_manager.broadcast_event(event).await;
    });
}

/// Poll orchestrator for status updates and publish via WebSocket
pub async fn sync_deployment_status(
    websocket_manager: Arc<WebSocketManager>,
    deployment_service: Arc<crate::services::iac_deployment::IacDeploymentService>,
    orchestrator_service: Arc<crate::services::orchestrator::OrchestratorService>,
    plan_id: String,
    workflow_id: String,
) {
    tokio::spawn(async move {
        // Poll orchestrator for status updates every 5 seconds
        let mut interval = tokio::time::interval(std::time::Duration::from_secs(5));

        loop {
            interval.tick().await;

            match orchestrator_service.get_workflow_status(&workflow_id).await {
                Ok(status) => {
                    // Update execution record in database
                    let execution = match deployment_service.get_execution(&plan_id).await {
                        Ok(exec) => exec,
                        Err(_) => break, // Stop polling if execution record is gone
                    };

                    let event_type = match status.status.as_str() {
                        "pending" | "submitted" => continue, // Skip initial states
                        "running" => DeploymentEventType::Started,
                        "completed" => DeploymentEventType::Completed,
                        "failed" => DeploymentEventType::Failed,
                        _ => DeploymentEventType::StatusUpdate,
                    };

                    // Update database
                    if let Err(e) = deployment_service
                        .update_execution_status(
                            &execution.id,
                            &status.status,
                            status.progress_percentage,
                            status.completed_tasks,
                            status.total_tasks,
                            Some(workflow_id.clone()),
                        )
                        .await
                    {
                        eprintln!("Failed to update execution status: {}", e);
                        break;
                    }

                    // Publish WebSocket event
                    publish_deployment_event(
                        websocket_manager.clone(),
                        event_type,
                        plan_id.clone(),
                        Some(workflow_id.clone()),
                        status.progress_percentage,
                        format!("{} - {}", event_type, status.status),
                    );

                    // Stop polling if completed or failed
                    if status.status == "completed" || status.status == "failed" {
                        break;
                    }
                }
                Err(e) => {
                    // Log error but continue polling
                    eprintln!("Failed to get workflow status: {}", e);
                }
            }
        }
    });
}

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

    #[test]
    fn test_event_type_display() {
        assert_eq!(DeploymentEventType::Submitted.to_string(), "plan_submitted");
        assert_eq!(
            DeploymentEventType::Started.to_string(),
            "deployment_started"
        );
        assert_eq!(
            DeploymentEventType::Completed.to_string(),
            "deployment_completed"
        );
        assert_eq!(DeploymentEventType::Failed.to_string(), "deployment_failed");
    }

    #[test]
    fn test_event_json_creation() {
        let event_data = json!({
            "plan_id": "plan-123",
            "workflow_id": "wf-456",
            "progress_percentage": 50,
            "message": "Deployment in progress",
        });

        assert_eq!(event_data["plan_id"], "plan-123");
        assert_eq!(event_data["progress_percentage"], 50);
    }
}
