//! WebSocket support for real-time project events - re-exported from shared-api-lib
//!
//! For full documentation, see shared_api_lib::websocket

use axum::{
    extract::{
        ws::{WebSocket, WebSocketUpgrade},
        State,
    },
    response::IntoResponse,
};
use futures::{SinkExt, StreamExt};

pub use shared_api_lib::websocket::{EventBroadcaster, ProjectEvent};

use crate::state::AppState;

/// WebSocket handler for real-time project events
pub async fn websocket_handler(
    State(state): State<AppState>,
    ws: WebSocketUpgrade,
) -> impl IntoResponse {
    ws.on_upgrade(|socket| handle_socket(socket, state))
}

/// Handle individual WebSocket connection
async fn handle_socket(socket: WebSocket, state: AppState) {
    let (mut sender, mut receiver) = socket.split();

    // Subscribe to event broadcasts
    let mut rx = state.events.subscribe();

    // Create task to forward broadcast events to client
    let mut send_task = tokio::spawn(async move {
        while let Ok(event) = rx.recv().await {
            // Serialize event to JSON and send to client
            if let Ok(json) = serde_json::to_string(&event) {
                let msg = axum::extract::ws::Message::Text(json.into());
                if sender.send(msg).await.is_err() {
                    // Client disconnected
                    break;
                }
            }
        }
    });

    // Create task to handle incoming client messages (keep-alive)
    let mut recv_task = tokio::spawn(async move {
        while let Some(Ok(msg)) = receiver.next().await {
            // Handle ping/pong messages for keep-alive
            if matches!(msg, axum::extract::ws::Message::Ping(_)) {
                // Ping handled automatically by axum
                continue;
            }
            if matches!(msg, axum::extract::ws::Message::Close(_)) {
                // Client closing connection
                break;
            }
            // Ignore other messages (we only send, don't receive data)
        }
    });

    // Wait for either task to complete (connection close or send error)
    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);
    }
}
