//! Client event system — real-time notifications from the SDK.

/// Events emitted by the SDK to the UI layer.
#[derive(Debug, Clone)]
pub enum ClientEvent {
    /// Successfully connected to the server.
    Connected,

    /// Disconnected from the server.
    Disconnected { reason: String },

    /// Connection lost, attempting to reconnect.
    Reconnecting { attempt: u32 },

    /// Registration succeeded.
    Registered { username: String },

    /// Login succeeded.
    LoggedIn { username: String },

    /// Logged out.
    LoggedOut { username: String },

    /// Authentication succeeded.
    Authenticated { username: String },

    /// A new message was received in a conversation.
    MessageReceived {
        conversation_id: [u8; 16],
        sender_key: Vec<u8>,
        sender_name: Option<String>,
        body: String,
        timestamp_ms: u64,
    },

    /// A message was sent successfully.
    MessageSent {
        conversation_id: [u8; 16],
        seq: u64,
    },

    /// A new conversation was created or discovered.
    ConversationCreated {
        conversation_id: [u8; 16],
        display_name: String,
    },

    /// A member was added to a group conversation.
    MemberAdded {
        conversation_id: [u8; 16],
        member_key: Vec<u8>,
    },

    /// A member was removed from a group conversation.
    MemberRemoved {
        conversation_id: [u8; 16],
        member_key: Vec<u8>,
    },

    /// Server-push event received.
    PushEvent {
        event_type: u16,
        payload: Vec<u8>,
    },

    /// A message was queued in the offline outbox (send failed or disconnected).
    MessageQueued {
        outbox_id: i64,
        conversation_id: [u8; 16],
    },

    /// Outbox flush completed after reconnect.
    OutboxFlushed {
        sent: usize,
        failed: usize,
    },

    /// Gap detected in message sequence numbers.
    MessageGap {
        conversation_id: [u8; 16],
        expected_seq: u64,
        received_seq: u64,
    },

    /// An error occurred in the background.
    Error { message: String },
}

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

    #[test]
    fn event_broadcast_send_receive() {
        let (tx, mut rx) = broadcast::channel::<ClientEvent>(16);

        tx.send(ClientEvent::Connected).unwrap();
        tx.send(ClientEvent::Disconnected {
            reason: "test".into(),
        })
        .unwrap();

        let e1 = rx.try_recv().unwrap();
        assert!(matches!(e1, ClientEvent::Connected));

        let e2 = rx.try_recv().unwrap();
        assert!(matches!(e2, ClientEvent::Disconnected { reason } if reason == "test"));
    }

    #[test]
    fn event_broadcast_multiple_subscribers() {
        let (tx, mut rx1) = broadcast::channel::<ClientEvent>(16);
        let mut rx2 = tx.subscribe();

        tx.send(ClientEvent::Registered {
            username: "alice".into(),
        })
        .unwrap();

        let e1 = rx1.try_recv().unwrap();
        let e2 = rx2.try_recv().unwrap();

        assert!(matches!(e1, ClientEvent::Registered { username } if username == "alice"));
        assert!(matches!(e2, ClientEvent::Registered { username } if username == "alice"));
    }

    #[test]
    fn event_no_subscribers_does_not_panic() {
        let (tx, _) = broadcast::channel::<ClientEvent>(16);
        // Send with no active receiver — should return Err but not panic.
        let result = tx.send(ClientEvent::Connected);
        assert!(result.is_err()); // no receivers
    }

    #[test]
    fn event_clone_preserves_data() {
        let event = ClientEvent::MessageReceived {
            conversation_id: [1u8; 16],
            sender_key: vec![2u8; 32],
            sender_name: Some("bob".into()),
            body: "hello world".into(),
            timestamp_ms: 12345,
        };

        let cloned = event.clone();
        match cloned {
            ClientEvent::MessageReceived {
                conversation_id,
                sender_key,
                sender_name,
                body,
                timestamp_ms,
            } => {
                assert_eq!(conversation_id, [1u8; 16]);
                assert_eq!(sender_key, vec![2u8; 32]);
                assert_eq!(sender_name, Some("bob".to_string()));
                assert_eq!(body, "hello world");
                assert_eq!(timestamp_ms, 12345);
            }
            _ => panic!("wrong variant after clone"),
        }
    }

    #[test]
    fn event_debug_format() {
        let event = ClientEvent::Error {
            message: "something went wrong".into(),
        };
        let dbg = format!("{event:?}");
        assert!(dbg.contains("something went wrong"));
    }

    #[test]
    fn all_event_variants_are_clone() {
        // Verify all variants can be cloned without issue.
        let events: Vec<ClientEvent> = vec![
            ClientEvent::Connected,
            ClientEvent::Disconnected { reason: "r".into() },
            ClientEvent::Reconnecting { attempt: 1 },
            ClientEvent::Registered { username: "u".into() },
            ClientEvent::LoggedIn { username: "u".into() },
            ClientEvent::LoggedOut { username: "u".into() },
            ClientEvent::Authenticated { username: "u".into() },
            ClientEvent::MessageReceived {
                conversation_id: [0; 16],
                sender_key: vec![],
                sender_name: None,
                body: "b".into(),
                timestamp_ms: 0,
            },
            ClientEvent::MessageSent {
                conversation_id: [0; 16],
                seq: 0,
            },
            ClientEvent::ConversationCreated {
                conversation_id: [0; 16],
                display_name: "d".into(),
            },
            ClientEvent::MemberAdded {
                conversation_id: [0; 16],
                member_key: vec![],
            },
            ClientEvent::MemberRemoved {
                conversation_id: [0; 16],
                member_key: vec![],
            },
            ClientEvent::PushEvent {
                event_type: 0,
                payload: vec![],
            },
            ClientEvent::MessageQueued {
                outbox_id: 0,
                conversation_id: [0; 16],
            },
            ClientEvent::OutboxFlushed { sent: 0, failed: 0 },
            ClientEvent::MessageGap {
                conversation_id: [0; 16],
                expected_seq: 0,
                received_seq: 1,
            },
            ClientEvent::Error { message: "e".into() },
        ];
        for event in &events {
            let _ = event.clone();
        }
        assert_eq!(events.len(), 17);
    }
}
