//! WebSocket connection management for real-time updates
//!
//! Provides utilities for connecting to the WebSocket endpoint and receiving
//! syntaxis events in real-time.

use crate::types::ProjectEvent;
use serde_json;
use std::sync::{Arc, Mutex};
use tokio::sync::broadcast;

/// WebSocket event receiver
///
/// Manages a WebSocket connection and broadcasts received events to subscribers.
/// This is designed to be used in a tokio runtime for async event handling.
#[derive(Debug, Clone)]
pub struct WebSocketEventReceiver {
    tx: broadcast::Sender<ProjectEvent>,
    url: String,
}

impl WebSocketEventReceiver {
    /// Create a new WebSocket event receiver
    ///
    /// # Arguments
    ///
    /// * `ws_url` - WebSocket URL (e.g., "ws://localhost:3000/ws")
    ///
    /// # Returns
    ///
    /// A new `WebSocketEventReceiver` instance
    pub fn new(ws_url: impl Into<String>) -> Self {
        let (tx, _) = broadcast::channel(100);
        Self {
            tx,
            url: ws_url.into(),
        }
    }

    /// Get a subscriber to receive events
    ///
    /// # Returns
    ///
    /// A broadcast receiver that yields `ProjectEvent` items
    pub fn subscribe(&self) -> broadcast::Receiver<ProjectEvent> {
        self.tx.subscribe()
    }

    /// Get the WebSocket URL
    pub fn url(&self) -> &str {
        &self.url
    }

    /// Get the sender for testing purposes
    #[cfg(test)]
    pub(crate) fn sender(&self) -> broadcast::Sender<ProjectEvent> {
        self.tx.clone()
    }
}

/// WebSocket connection state
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionState {
    /// Not connected
    Disconnected,
    /// Connecting to server
    Connecting,
    /// Successfully connected
    Connected,
    /// Connection error occurred
    Error,
}

/// WebSocket manager for handling connection lifecycle
///
/// Tracks connection state and manages reconnection logic.
#[derive(Debug, Clone)]
pub struct WebSocketManager {
    state: Arc<Mutex<ConnectionState>>,
    receiver: WebSocketEventReceiver,
}

impl WebSocketManager {
    /// Create a new WebSocket manager
    ///
    /// # Arguments
    ///
    /// * `ws_url` - WebSocket URL
    ///
    /// # Returns
    ///
    /// A new `WebSocketManager` instance
    pub fn new(ws_url: impl Into<String>) -> Self {
        Self {
            state: Arc::new(Mutex::new(ConnectionState::Disconnected)),
            receiver: WebSocketEventReceiver::new(ws_url),
        }
    }

    /// Get the current connection state
    pub fn state(&self) -> ConnectionState {
        self.state
            .lock()
            .map(|guard| *guard)
            .unwrap_or(ConnectionState::Error)
    }

    /// Set the connection state
    pub fn set_state(&self, new_state: ConnectionState) {
        if let Ok(mut guard) = self.state.lock() {
            *guard = new_state;
        }
    }

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

    /// Get the WebSocket URL
    pub fn url(&self) -> &str {
        self.receiver.url()
    }

    /// Get the receiver for direct access
    pub fn receiver(&self) -> &WebSocketEventReceiver {
        &self.receiver
    }
}

/// Parse a WebSocket message into a ProjectEvent
///
/// # Arguments
///
/// * `text` - JSON string containing the event
///
/// # Returns
///
/// Result containing the parsed `ProjectEvent` or error message
pub fn parse_websocket_message(text: &str) -> Result<ProjectEvent, String> {
    serde_json::from_str(text).map_err(|e| format!("Failed to parse event: {}", e))
}

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

    #[test]
    fn test_receiver_creation() {
        let receiver = WebSocketEventReceiver::new("ws://localhost:3000/ws");
        assert_eq!(receiver.url(), "ws://localhost:3000/ws");
    }

    #[test]
    fn test_subscribe() {
        let receiver = WebSocketEventReceiver::new("ws://localhost:3000/ws");
        let mut sub1 = receiver.subscribe();
        let mut sub2 = receiver.subscribe();

        // Both subscribers should receive the same event
        let event = ProjectEvent::ProjectCreated {
            project_id: "test-123".to_string(),
            project_name: "Test Project".to_string(),
        };

        receiver.sender().send(event.clone()).ok();

        assert_eq!(sub1.try_recv().ok(), Some(event.clone()));
        assert_eq!(sub2.try_recv().ok(), Some(event));
    }

    #[test]
    fn test_manager_state() {
        let manager = WebSocketManager::new("ws://localhost:3000/ws");
        assert_eq!(manager.state(), ConnectionState::Disconnected);

        manager.set_state(ConnectionState::Connecting);
        assert_eq!(manager.state(), ConnectionState::Connecting);

        manager.set_state(ConnectionState::Connected);
        assert_eq!(manager.state(), ConnectionState::Connected);
    }

    #[test]
    fn test_parse_valid_message() {
        let json =
            r#"{"type":"ProjectCreated","project_id":"abc-123","project_name":"My Project"}"#;
        let result = parse_websocket_message(json);
        assert!(result.is_ok());

        if let Ok(ProjectEvent::ProjectCreated {
            project_id,
            project_name,
        }) = result
        {
            assert_eq!(project_id, "abc-123");
            assert_eq!(project_name, "My Project");
        } else {
            panic!("Expected ProjectCreated event");
        }
    }

    #[test]
    fn test_parse_invalid_message() {
        let json = r#"{"invalid": "event"}"#;
        let result = parse_websocket_message(json);
        assert!(result.is_err());
    }

    #[test]
    fn test_manager_subscribe() {
        let manager = WebSocketManager::new("ws://localhost:3000/ws");
        let mut sub = manager.subscribe();

        let event = ProjectEvent::TaskCompleted {
            project_id: "proj-1".to_string(),
            task_id: "task-1".to_string(),
        };

        manager.receiver().sender().send(event.clone()).ok();

        assert_eq!(sub.try_recv().ok(), Some(event));
    }
}
