//! WebSocket support for real-time event broadcasting
//!
//! Provides generic WebSocket capabilities for broadcasting events to connected clients.
//! See the handler documentation for implementation examples.

use serde::{Deserialize, Serialize};
use tokio::sync::broadcast;

/// WebSocket event types
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")]
pub enum ProjectEvent {
    /// Phase transition event
    #[serde(rename = "phase_transition")]
    PhaseTransition {
        /// Project ID
        project_id: String,
        /// Previous phase
        from_phase: String,
        /// New phase
        to_phase: String,
        /// Timestamp
        timestamp: String,
    },

    /// Tool enabled event
    #[serde(rename = "tool_enabled")]
    ToolEnabled {
        /// Project ID
        project_id: String,
        /// Tool name
        tool_name: String,
        /// Timestamp
        timestamp: String,
    },

    /// Tool disabled event
    #[serde(rename = "tool_disabled")]
    ToolDisabled {
        /// Project ID
        project_id: String,
        /// Tool name
        tool_name: String,
        /// Timestamp
        timestamp: String,
    },

    /// Project created event
    #[serde(rename = "project_created")]
    ProjectCreated {
        /// Project ID
        project_id: String,
        /// Project name
        project_name: String,
        /// Timestamp
        timestamp: String,
    },

    /// Project deleted event
    #[serde(rename = "project_deleted")]
    ProjectDeleted {
        /// Project ID
        project_id: String,
        /// Timestamp
        timestamp: String,
    },

    /// Task created event
    #[serde(rename = "task_created")]
    TaskCreated {
        /// Project ID
        project_id: String,
        /// Task ID
        task_id: String,
        /// Task description
        description: String,
        /// Timestamp
        timestamp: String,
    },

    /// Task updated event
    #[serde(rename = "task_updated")]
    TaskUpdated {
        /// Project ID
        project_id: String,
        /// Task ID
        task_id: String,
        /// Timestamp
        timestamp: String,
    },

    /// Task deleted event
    #[serde(rename = "task_deleted")]
    TaskDeleted {
        /// Project ID
        project_id: String,
        /// Task ID
        task_id: String,
        /// Timestamp
        timestamp: String,
    },

    /// Task assigned event
    #[serde(rename = "task_assigned")]
    TaskAssigned {
        /// Project ID
        project_id: String,
        /// Task ID
        task_id: String,
        /// Assignee user ID
        assignee_id: String,
        /// Assignee name
        assignee_name: String,
        /// Timestamp
        timestamp: String,
    },

    /// Comment added event
    #[serde(rename = "comment_added")]
    CommentAdded {
        /// Project ID
        project_id: String,
        /// Task ID
        task_id: String,
        /// Comment ID
        comment_id: String,
        /// User name
        user_name: String,
        /// Timestamp
        timestamp: String,
    },

    /// Attachment uploaded event
    #[serde(rename = "attachment_uploaded")]
    AttachmentUploaded {
        /// Project ID
        project_id: String,
        /// Task ID
        task_id: String,
        /// Attachment ID
        attachment_id: String,
        /// Filename
        filename: String,
        /// Timestamp
        timestamp: String,
    },

    /// Deadline set event
    #[serde(rename = "deadline_set")]
    DeadlineSet {
        /// Project ID
        project_id: String,
        /// Task ID
        task_id: String,
        /// Deadline
        deadline: String,
        /// Timestamp
        timestamp: String,
    },

    /// Dependency added event
    #[serde(rename = "dependency_added")]
    DependencyAdded {
        /// Project ID
        project_id: String,
        /// Task ID
        task_id: String,
        /// Depends on task ID
        depends_on_task_id: String,
        /// Dependency type
        dependency_type: String,
        /// Timestamp
        timestamp: String,
    },

    /// Milestone completed event
    #[serde(rename = "milestone_completed")]
    MilestoneCompleted {
        /// Project ID
        project_id: String,
        /// Milestone ID
        milestone_id: String,
        /// Milestone name
        milestone_name: String,
        /// Timestamp
        timestamp: String,
    },

    /// User authenticated event
    #[serde(rename = "user_authenticated")]
    UserAuthenticated {
        /// User ID
        user_id: String,
        /// User email
        user_email: String,
        /// Timestamp
        timestamp: String,
    },
}

/// WebSocket event broadcaster
#[derive(Debug, Clone)]
pub struct EventBroadcaster {
    /// Broadcast channel sender
    tx: broadcast::Sender<ProjectEvent>,
}

impl EventBroadcaster {
    /// Create new event broadcaster
    pub fn new(capacity: usize) -> Self {
        let (tx, _) = broadcast::channel(capacity);
        Self { tx }
    }

    /// Broadcast an event to all subscribers
    pub fn broadcast(&self, event: ProjectEvent) {
        // Ignore if no receivers
        let _ = self.tx.send(event);
    }

    /// Subscribe to events
    pub fn subscribe(&self) -> broadcast::Receiver<ProjectEvent> {
        self.tx.subscribe()
    }

    /// Get number of subscribers
    #[allow(dead_code)]
    pub fn subscriber_count(&self) -> usize {
        self.tx.receiver_count()
    }
}

/// Handle WebSocket connections
///
/// WebSocket handler pattern for API implementations
///
/// Each API using this library should implement its own WebSocket handler:
/// ```ignore
/// use axum::extract::ws::{WebSocket, WebSocketUpgrade};
/// use axum::extract::State;
/// use axum::response::IntoResponse;
/// use shared_api_lib::state::AppState;
/// use futures::stream::StreamExt;
///
/// pub async fn websocket_handler<DB: Send + Sync>(
///     State(state): State<AppState<DB>>,
///     ws: WebSocketUpgrade,
/// ) -> impl IntoResponse {
///     ws.on_upgrade(|socket| handle_socket(socket, state))
/// }
///
/// async fn handle_socket<DB: Send + Sync>(socket: WebSocket, state: AppState<DB>) {
///     let (mut sender, mut receiver) = socket.split();
///
///     // Subscribe to event broadcasts
///     if let Some(events) = &state.events {
///         let mut rx = events.subscribe();
///
///         // Create task to forward events to client
///         let mut send_task = tokio::spawn(async move {
///             while let Ok(event) = rx.recv().await {
///                 if let Ok(json) = serde_json::to_string(&event) {
///                     let msg = Message::Text(json.into());
///                     if sender.send(msg).await.is_err() {
///                         break;
///                     }
///                 }
///             }
///         });
///
///         // Handle incoming messages
///         let mut recv_task = tokio::spawn(async move {
///             while let Some(Ok(msg)) = receiver.next().await {
///                 match msg {
///                     Message::Close(_) => break,
///                     _ => continue,
///                 }
///             }
///         });
///
///         tokio::select! {
///             _ = &mut send_task => recv_task.abort(),
///             _ = &mut recv_task => send_task.abort(),
///         }
///     }
/// }
/// ```
#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_phase_transition_event() {
        let event = ProjectEvent::PhaseTransition {
            project_id: "proj-1".to_string(),
            from_phase: "Creation".to_string(),
            to_phase: "Development".to_string(),
            timestamp: "2024-01-01T00:00:00Z".to_string(),
        };

        let json = serde_json::to_string(&event).unwrap();
        assert!(json.contains("phase_transition"));
        assert!(json.contains("proj-1"));
    }

    #[test]
    fn test_tool_enabled_event() {
        let event = ProjectEvent::ToolEnabled {
            project_id: "proj-1".to_string(),
            tool_name: "tracking".to_string(),
            timestamp: "2024-01-01T00:00:00Z".to_string(),
        };

        let json = serde_json::to_string(&event).unwrap();
        assert!(json.contains("tool_enabled"));
        assert!(json.contains("tracking"));
    }

    #[test]
    fn test_tool_disabled_event() {
        let event = ProjectEvent::ToolDisabled {
            project_id: "proj-1".to_string(),
            tool_name: "security".to_string(),
            timestamp: "2024-01-01T00:00:00Z".to_string(),
        };

        let json = serde_json::to_string(&event).unwrap();
        assert!(json.contains("tool_disabled"));
    }

    #[test]
    fn test_event_deserialization() {
        let json = r#"{"type":"phase_transition","project_id":"proj-1","from_phase":"Creation","to_phase":"Development","timestamp":"2024-01-01T00:00:00Z"}"#;
        let event: ProjectEvent = serde_json::from_str(json).unwrap();

        match event {
            ProjectEvent::PhaseTransition {
                project_id,
                from_phase,
                to_phase,
                ..
            } => {
                assert_eq!(project_id, "proj-1");
                assert_eq!(from_phase, "Creation");
                assert_eq!(to_phase, "Development");
            }
            _ => panic!("Wrong event type"),
        }
    }

    #[tokio::test]
    async fn test_event_broadcaster_creation() {
        let broadcaster = EventBroadcaster::new(100);
        assert_eq!(broadcaster.subscriber_count(), 0);
    }

    #[tokio::test]
    async fn test_event_broadcasting() {
        let broadcaster = EventBroadcaster::new(100);
        let mut rx = broadcaster.subscribe();

        let event = ProjectEvent::PhaseTransition {
            project_id: "proj-1".to_string(),
            from_phase: "Creation".to_string(),
            to_phase: "Development".to_string(),
            timestamp: "2024-01-01T00:00:00Z".to_string(),
        };

        broadcaster.broadcast(event.clone());

        let received = tokio::time::timeout(std::time::Duration::from_secs(1), rx.recv())
            .await
            .expect("timeout")
            .expect("recv error");

        assert_eq!(received, event);
    }

    #[test]
    fn test_event_serialization() {
        let events = vec![
            ProjectEvent::PhaseTransition {
                project_id: "proj-1".to_string(),
                from_phase: "Creation".to_string(),
                to_phase: "Development".to_string(),
                timestamp: "2024-01-01T00:00:00Z".to_string(),
            },
            ProjectEvent::ToolEnabled {
                project_id: "proj-1".to_string(),
                tool_name: "tracking".to_string(),
                timestamp: "2024-01-01T00:00:00Z".to_string(),
            },
            ProjectEvent::ProjectCreated {
                project_id: "proj-2".to_string(),
                project_name: "Test Project".to_string(),
                timestamp: "2024-01-01T00:00:00Z".to_string(),
            },
        ];

        for event in events {
            let json = serde_json::to_string(&event).unwrap();
            let deserialized: ProjectEvent = serde_json::from_str(&json).unwrap();
            assert_eq!(event, deserialized);
        }
    }
}
