hive_btle/
hive_mesh.rs

1//! HiveMesh - Unified mesh management facade
2//!
3//! This module provides the main entry point for HIVE BLE mesh operations.
4//! It composes peer management, document sync, and observer notifications
5//! into a single interface that platform implementations can use.
6//!
7//! ## Usage
8//!
9//! ```ignore
10//! use hive_btle::hive_mesh::{HiveMesh, HiveMeshConfig};
11//! use hive_btle::observer::{HiveEvent, HiveObserver};
12//! use hive_btle::NodeId;
13//! use std::sync::Arc;
14//!
15//! // Create mesh configuration
16//! let config = HiveMeshConfig::new(NodeId::new(0x12345678), "ALPHA-1", "DEMO");
17//!
18//! // Create mesh instance
19//! let mesh = HiveMesh::new(config);
20//!
21//! // Add observer for events
22//! struct MyObserver;
23//! impl HiveObserver for MyObserver {
24//!     fn on_event(&self, event: HiveEvent) {
25//!         println!("Event: {:?}", event);
26//!     }
27//! }
28//! mesh.add_observer(Arc::new(MyObserver));
29//!
30//! // Platform BLE callbacks
31//! mesh.on_ble_discovered("device-uuid", Some("HIVE_DEMO-AABBCCDD"), -65, Some("DEMO"), now_ms);
32//! mesh.on_ble_connected("device-uuid", now_ms);
33//! mesh.on_ble_data_received("device-uuid", &data, now_ms);
34//!
35//! // Periodic maintenance
36//! if let Some(sync_data) = mesh.tick(now_ms) {
37//!     // Broadcast sync_data to connected peers
38//! }
39//! ```
40
41#[cfg(not(feature = "std"))]
42use alloc::{string::String, sync::Arc, vec::Vec};
43#[cfg(feature = "std")]
44use std::sync::Arc;
45
46use crate::document::{ENCRYPTED_MARKER, KEY_EXCHANGE_MARKER, PEER_E2EE_MARKER};
47use crate::document_sync::DocumentSync;
48use crate::observer::{DisconnectReason, HiveEvent, HiveObserver, SecurityViolationKind};
49use crate::peer::{HivePeer, PeerManagerConfig};
50use crate::peer_manager::PeerManager;
51use crate::security::{
52    KeyExchangeMessage, MeshEncryptionKey, PeerEncryptedMessage, PeerSessionManager, SessionState,
53};
54use crate::sync::crdt::{EventType, PeripheralType};
55use crate::NodeId;
56
57#[cfg(feature = "std")]
58use crate::observer::ObserverManager;
59
60/// Configuration for HiveMesh
61#[derive(Debug, Clone)]
62pub struct HiveMeshConfig {
63    /// Our node ID
64    pub node_id: NodeId,
65
66    /// Our callsign (e.g., "ALPHA-1")
67    pub callsign: String,
68
69    /// Mesh ID to filter peers (e.g., "DEMO")
70    pub mesh_id: String,
71
72    /// Peripheral type for this device
73    pub peripheral_type: PeripheralType,
74
75    /// Peer management configuration
76    pub peer_config: PeerManagerConfig,
77
78    /// Sync interval in milliseconds (how often to broadcast state)
79    pub sync_interval_ms: u64,
80
81    /// Whether to auto-broadcast on emergency/ack
82    pub auto_broadcast_events: bool,
83
84    /// Optional shared secret for mesh-wide encryption (32 bytes)
85    ///
86    /// When set, all documents are encrypted using ChaCha20-Poly1305 before
87    /// transmission and decrypted upon receipt. All nodes in the mesh must
88    /// share the same secret to communicate.
89    pub encryption_secret: Option<[u8; 32]>,
90
91    /// Strict encryption mode - reject unencrypted documents when encryption is enabled
92    ///
93    /// When true and encryption is enabled, any unencrypted documents received
94    /// will be rejected and trigger a SecurityViolation event. This prevents
95    /// downgrade attacks where an adversary sends unencrypted malicious documents.
96    ///
97    /// Default: false (backward compatible - accepts unencrypted for gradual rollout)
98    pub strict_encryption: bool,
99}
100
101impl HiveMeshConfig {
102    /// Create a new configuration with required fields
103    pub fn new(node_id: NodeId, callsign: &str, mesh_id: &str) -> Self {
104        Self {
105            node_id,
106            callsign: callsign.into(),
107            mesh_id: mesh_id.into(),
108            peripheral_type: PeripheralType::SoldierSensor,
109            peer_config: PeerManagerConfig::with_mesh_id(mesh_id),
110            sync_interval_ms: 5000,
111            auto_broadcast_events: true,
112            encryption_secret: None,
113            strict_encryption: false,
114        }
115    }
116
117    /// Enable mesh-wide encryption with a shared secret
118    ///
119    /// All documents will be encrypted using ChaCha20-Poly1305 before
120    /// transmission. All mesh participants must use the same secret.
121    pub fn with_encryption(mut self, secret: [u8; 32]) -> Self {
122        self.encryption_secret = Some(secret);
123        self
124    }
125
126    /// Set peripheral type
127    pub fn with_peripheral_type(mut self, ptype: PeripheralType) -> Self {
128        self.peripheral_type = ptype;
129        self
130    }
131
132    /// Set sync interval
133    pub fn with_sync_interval(mut self, interval_ms: u64) -> Self {
134        self.sync_interval_ms = interval_ms;
135        self
136    }
137
138    /// Set peer timeout
139    pub fn with_peer_timeout(mut self, timeout_ms: u64) -> Self {
140        self.peer_config.peer_timeout_ms = timeout_ms;
141        self
142    }
143
144    /// Set max peers (for embedded systems)
145    pub fn with_max_peers(mut self, max: usize) -> Self {
146        self.peer_config.max_peers = max;
147        self
148    }
149
150    /// Enable strict encryption mode
151    ///
152    /// When enabled (and encryption is also enabled), any unencrypted documents
153    /// received will be rejected and trigger a `SecurityViolation` event.
154    /// This prevents downgrade attacks.
155    ///
156    /// Note: This only has effect when encryption is enabled via `with_encryption()`.
157    pub fn with_strict_encryption(mut self) -> Self {
158        self.strict_encryption = true;
159        self
160    }
161}
162
163/// Main facade for HIVE BLE mesh operations
164///
165/// Composes peer management, document sync, and observer notifications.
166/// Platform implementations call into this from their BLE callbacks.
167#[cfg(feature = "std")]
168pub struct HiveMesh {
169    /// Configuration
170    config: HiveMeshConfig,
171
172    /// Peer manager
173    peer_manager: PeerManager,
174
175    /// Document sync
176    document_sync: DocumentSync,
177
178    /// Observer manager
179    observers: ObserverManager,
180
181    /// Last sync broadcast time (u32 wraps every ~49 days, sufficient for intervals)
182    last_sync_ms: std::sync::atomic::AtomicU32,
183
184    /// Last cleanup time
185    last_cleanup_ms: std::sync::atomic::AtomicU32,
186
187    /// Optional mesh-wide encryption key (derived from shared secret)
188    encryption_key: Option<MeshEncryptionKey>,
189
190    /// Optional per-peer E2EE session manager
191    peer_sessions: std::sync::Mutex<Option<PeerSessionManager>>,
192}
193
194#[cfg(feature = "std")]
195impl HiveMesh {
196    /// Create a new HiveMesh instance
197    pub fn new(config: HiveMeshConfig) -> Self {
198        let peer_manager = PeerManager::new(config.node_id, config.peer_config.clone());
199        let document_sync = DocumentSync::with_peripheral_type(
200            config.node_id,
201            &config.callsign,
202            config.peripheral_type,
203        );
204
205        // Derive encryption key from shared secret if configured
206        let encryption_key = config
207            .encryption_secret
208            .map(|secret| MeshEncryptionKey::from_shared_secret(&config.mesh_id, &secret));
209
210        Self {
211            config,
212            peer_manager,
213            document_sync,
214            observers: ObserverManager::new(),
215            last_sync_ms: std::sync::atomic::AtomicU32::new(0),
216            last_cleanup_ms: std::sync::atomic::AtomicU32::new(0),
217            encryption_key,
218            peer_sessions: std::sync::Mutex::new(None),
219        }
220    }
221
222    // ==================== Encryption ====================
223
224    /// Check if mesh-wide encryption is enabled
225    pub fn is_encryption_enabled(&self) -> bool {
226        self.encryption_key.is_some()
227    }
228
229    /// Check if strict encryption mode is enabled
230    ///
231    /// Returns true only if both encryption and strict_encryption are enabled.
232    pub fn is_strict_encryption_enabled(&self) -> bool {
233        self.config.strict_encryption && self.encryption_key.is_some()
234    }
235
236    /// Enable mesh-wide encryption with a shared secret
237    ///
238    /// Derives a ChaCha20-Poly1305 key from the secret using HKDF-SHA256.
239    /// All mesh participants must use the same secret to communicate.
240    pub fn enable_encryption(&mut self, secret: &[u8; 32]) {
241        self.encryption_key = Some(MeshEncryptionKey::from_shared_secret(
242            &self.config.mesh_id,
243            secret,
244        ));
245    }
246
247    /// Disable mesh-wide encryption
248    pub fn disable_encryption(&mut self) {
249        self.encryption_key = None;
250    }
251
252    /// Encrypt document bytes for transmission
253    ///
254    /// Returns the encrypted bytes with ENCRYPTED_MARKER prefix, or the
255    /// original bytes if encryption is disabled.
256    fn encrypt_document(&self, plaintext: &[u8]) -> Vec<u8> {
257        match &self.encryption_key {
258            Some(key) => {
259                // Encrypt and prepend marker
260                match key.encrypt_to_bytes(plaintext) {
261                    Ok(ciphertext) => {
262                        let mut buf = Vec::with_capacity(2 + ciphertext.len());
263                        buf.push(ENCRYPTED_MARKER);
264                        buf.push(0x00); // reserved
265                        buf.extend_from_slice(&ciphertext);
266                        buf
267                    }
268                    Err(e) => {
269                        log::error!("Encryption failed: {}", e);
270                        // Fall back to unencrypted on error (shouldn't happen)
271                        plaintext.to_vec()
272                    }
273                }
274            }
275            None => plaintext.to_vec(),
276        }
277    }
278
279    /// Decrypt document bytes received from peer
280    ///
281    /// Returns the decrypted bytes if encrypted and valid, or the original
282    /// bytes if not encrypted. Returns None if decryption fails.
283    ///
284    /// In strict encryption mode (when both encryption and strict_encryption are enabled),
285    /// unencrypted documents are rejected and trigger a SecurityViolation event.
286    fn decrypt_document<'a>(
287        &self,
288        data: &'a [u8],
289        source_hint: Option<&str>,
290    ) -> Option<std::borrow::Cow<'a, [u8]>> {
291        // Check for encrypted marker
292        if data.len() >= 2 && data[0] == ENCRYPTED_MARKER {
293            // Encrypted document
294            let _reserved = data[1];
295            let encrypted_payload = &data[2..];
296
297            match &self.encryption_key {
298                Some(key) => match key.decrypt_from_bytes(encrypted_payload) {
299                    Ok(plaintext) => Some(std::borrow::Cow::Owned(plaintext)),
300                    Err(e) => {
301                        log::warn!("Decryption failed (wrong key or corrupted): {}", e);
302                        self.notify(HiveEvent::SecurityViolation {
303                            kind: SecurityViolationKind::DecryptionFailed,
304                            source: source_hint.map(String::from),
305                        });
306                        None
307                    }
308                },
309                None => {
310                    log::warn!("Received encrypted document but encryption not enabled");
311                    None
312                }
313            }
314        } else {
315            // Unencrypted document
316            // Check strict encryption mode
317            if self.config.strict_encryption && self.encryption_key.is_some() {
318                log::warn!(
319                    "Rejected unencrypted document in strict encryption mode (source: {:?})",
320                    source_hint
321                );
322                self.notify(HiveEvent::SecurityViolation {
323                    kind: SecurityViolationKind::UnencryptedInStrictMode,
324                    source: source_hint.map(String::from),
325                });
326                None
327            } else {
328                // Permissive mode: accept unencrypted for backward compatibility
329                Some(std::borrow::Cow::Borrowed(data))
330            }
331        }
332    }
333
334    // ==================== Per-Peer E2EE ====================
335
336    /// Enable per-peer E2EE capability
337    ///
338    /// Creates a new identity key for this node. This allows establishing
339    /// encrypted sessions with specific peers where only the sender and
340    /// recipient can read messages (other mesh members cannot).
341    pub fn enable_peer_e2ee(&self) {
342        let mut sessions = self.peer_sessions.lock().unwrap();
343        if sessions.is_none() {
344            *sessions = Some(PeerSessionManager::new(self.config.node_id));
345            log::info!(
346                "Per-peer E2EE enabled for node {:08X}",
347                self.config.node_id.as_u32()
348            );
349        }
350    }
351
352    /// Disable per-peer E2EE capability
353    ///
354    /// Clears all peer sessions and disables E2EE.
355    pub fn disable_peer_e2ee(&self) {
356        let mut sessions = self.peer_sessions.lock().unwrap();
357        *sessions = None;
358        log::info!("Per-peer E2EE disabled");
359    }
360
361    /// Check if per-peer E2EE is enabled
362    pub fn is_peer_e2ee_enabled(&self) -> bool {
363        self.peer_sessions.lock().unwrap().is_some()
364    }
365
366    /// Get our E2EE public key (for sharing with peers)
367    ///
368    /// Returns None if per-peer E2EE is not enabled.
369    pub fn peer_e2ee_public_key(&self) -> Option<[u8; 32]> {
370        self.peer_sessions
371            .lock()
372            .unwrap()
373            .as_ref()
374            .map(|s| s.our_public_key())
375    }
376
377    /// Initiate E2EE session with a specific peer
378    ///
379    /// Returns the key exchange message bytes to send to the peer.
380    /// The message should be broadcast/sent to the peer.
381    /// Returns None if per-peer E2EE is not enabled.
382    pub fn initiate_peer_e2ee(&self, peer_node_id: NodeId, now_ms: u64) -> Option<Vec<u8>> {
383        let mut sessions = self.peer_sessions.lock().unwrap();
384        let session_mgr = sessions.as_mut()?;
385
386        let key_exchange = session_mgr.initiate_session(peer_node_id, now_ms);
387        let mut buf = Vec::with_capacity(2 + 37);
388        buf.push(KEY_EXCHANGE_MARKER);
389        buf.push(0x00); // reserved
390        buf.extend_from_slice(&key_exchange.encode());
391
392        log::info!(
393            "Initiated E2EE session with peer {:08X}",
394            peer_node_id.as_u32()
395        );
396        Some(buf)
397    }
398
399    /// Check if we have an established E2EE session with a peer
400    pub fn has_peer_e2ee_session(&self, peer_node_id: NodeId) -> bool {
401        self.peer_sessions
402            .lock()
403            .unwrap()
404            .as_ref()
405            .is_some_and(|s| s.has_session(peer_node_id))
406    }
407
408    /// Get E2EE session state with a peer
409    pub fn peer_e2ee_session_state(&self, peer_node_id: NodeId) -> Option<SessionState> {
410        self.peer_sessions
411            .lock()
412            .unwrap()
413            .as_ref()
414            .and_then(|s| s.session_state(peer_node_id))
415    }
416
417    /// Send an E2EE encrypted message to a specific peer
418    ///
419    /// Returns the encrypted message bytes to send, or None if no session exists.
420    /// The message should be sent directly to the peer (not broadcast).
421    pub fn send_peer_e2ee(
422        &self,
423        peer_node_id: NodeId,
424        plaintext: &[u8],
425        now_ms: u64,
426    ) -> Option<Vec<u8>> {
427        let mut sessions = self.peer_sessions.lock().unwrap();
428        let session_mgr = sessions.as_mut()?;
429
430        match session_mgr.encrypt_for_peer(peer_node_id, plaintext, now_ms) {
431            Ok(encrypted) => {
432                let mut buf = Vec::with_capacity(2 + encrypted.encode().len());
433                buf.push(PEER_E2EE_MARKER);
434                buf.push(0x00); // reserved
435                buf.extend_from_slice(&encrypted.encode());
436                Some(buf)
437            }
438            Err(e) => {
439                log::warn!(
440                    "Failed to encrypt for peer {:08X}: {:?}",
441                    peer_node_id.as_u32(),
442                    e
443                );
444                None
445            }
446        }
447    }
448
449    /// Close E2EE session with a peer
450    pub fn close_peer_e2ee(&self, peer_node_id: NodeId) {
451        let mut sessions = self.peer_sessions.lock().unwrap();
452        if let Some(session_mgr) = sessions.as_mut() {
453            session_mgr.close_session(peer_node_id);
454            self.notify(HiveEvent::PeerE2eeClosed { peer_node_id });
455            log::info!(
456                "Closed E2EE session with peer {:08X}",
457                peer_node_id.as_u32()
458            );
459        }
460    }
461
462    /// Get count of active E2EE sessions
463    pub fn peer_e2ee_session_count(&self) -> usize {
464        self.peer_sessions
465            .lock()
466            .unwrap()
467            .as_ref()
468            .map(|s| s.session_count())
469            .unwrap_or(0)
470    }
471
472    /// Get count of established E2EE sessions
473    pub fn peer_e2ee_established_count(&self) -> usize {
474        self.peer_sessions
475            .lock()
476            .unwrap()
477            .as_ref()
478            .map(|s| s.established_count())
479            .unwrap_or(0)
480    }
481
482    /// Handle incoming key exchange message
483    ///
484    /// Called internally when we receive a KEY_EXCHANGE_MARKER message.
485    /// Returns the response key exchange bytes to send back, or None if invalid.
486    fn handle_key_exchange(&self, data: &[u8], now_ms: u64) -> Option<Vec<u8>> {
487        if data.len() < 2 || data[0] != KEY_EXCHANGE_MARKER {
488            return None;
489        }
490
491        let payload = &data[2..];
492        let msg = KeyExchangeMessage::decode(payload)?;
493
494        let mut sessions = self.peer_sessions.lock().unwrap();
495        let session_mgr = sessions.as_mut()?;
496
497        let (response, established) = session_mgr.handle_key_exchange(&msg, now_ms)?;
498
499        if established {
500            self.notify(HiveEvent::PeerE2eeEstablished {
501                peer_node_id: msg.sender_node_id,
502            });
503            log::info!(
504                "E2EE session established with peer {:08X}",
505                msg.sender_node_id.as_u32()
506            );
507        }
508
509        // Return response key exchange
510        let mut buf = Vec::with_capacity(2 + 37);
511        buf.push(KEY_EXCHANGE_MARKER);
512        buf.push(0x00);
513        buf.extend_from_slice(&response.encode());
514        Some(buf)
515    }
516
517    /// Handle incoming E2EE encrypted message
518    ///
519    /// Called internally when we receive a PEER_E2EE_MARKER message.
520    /// Decrypts and notifies observers of the received message.
521    fn handle_peer_e2ee_message(&self, data: &[u8], now_ms: u64) -> Option<Vec<u8>> {
522        if data.len() < 2 || data[0] != PEER_E2EE_MARKER {
523            return None;
524        }
525
526        let payload = &data[2..];
527        let msg = PeerEncryptedMessage::decode(payload)?;
528
529        let mut sessions = self.peer_sessions.lock().unwrap();
530        let session_mgr = sessions.as_mut()?;
531
532        match session_mgr.decrypt_from_peer(&msg, now_ms) {
533            Ok(plaintext) => {
534                // Notify observers of the decrypted message
535                self.notify(HiveEvent::PeerE2eeMessageReceived {
536                    from_node: msg.sender_node_id,
537                    data: plaintext.clone(),
538                });
539                Some(plaintext)
540            }
541            Err(e) => {
542                log::warn!(
543                    "Failed to decrypt E2EE message from {:08X}: {:?}",
544                    msg.sender_node_id.as_u32(),
545                    e
546                );
547                None
548            }
549        }
550    }
551
552    // ==================== Configuration ====================
553
554    /// Get our node ID
555    pub fn node_id(&self) -> NodeId {
556        self.config.node_id
557    }
558
559    /// Get our callsign
560    pub fn callsign(&self) -> &str {
561        &self.config.callsign
562    }
563
564    /// Get the mesh ID
565    pub fn mesh_id(&self) -> &str {
566        &self.config.mesh_id
567    }
568
569    /// Get the device name for BLE advertising
570    pub fn device_name(&self) -> String {
571        format!(
572            "HIVE_{}-{:08X}",
573            self.config.mesh_id,
574            self.config.node_id.as_u32()
575        )
576    }
577
578    // ==================== Observer Management ====================
579
580    /// Add an observer for mesh events
581    pub fn add_observer(&self, observer: Arc<dyn HiveObserver>) {
582        self.observers.add(observer);
583    }
584
585    /// Remove an observer
586    pub fn remove_observer(&self, observer: &Arc<dyn HiveObserver>) {
587        self.observers.remove(observer);
588    }
589
590    // ==================== User Actions ====================
591
592    /// Send an emergency alert
593    ///
594    /// Returns the document bytes to broadcast to all peers.
595    /// If encryption is enabled, the document is encrypted.
596    pub fn send_emergency(&self, timestamp: u64) -> Vec<u8> {
597        let data = self.document_sync.send_emergency(timestamp);
598        self.notify(HiveEvent::MeshStateChanged {
599            peer_count: self.peer_manager.peer_count(),
600            connected_count: self.peer_manager.connected_count(),
601        });
602        self.encrypt_document(&data)
603    }
604
605    /// Send an ACK response
606    ///
607    /// Returns the document bytes to broadcast to all peers.
608    /// If encryption is enabled, the document is encrypted.
609    pub fn send_ack(&self, timestamp: u64) -> Vec<u8> {
610        let data = self.document_sync.send_ack(timestamp);
611        self.notify(HiveEvent::MeshStateChanged {
612            peer_count: self.peer_manager.peer_count(),
613            connected_count: self.peer_manager.connected_count(),
614        });
615        self.encrypt_document(&data)
616    }
617
618    /// Clear the current event (emergency or ack)
619    pub fn clear_event(&self) {
620        self.document_sync.clear_event();
621    }
622
623    /// Check if emergency is active
624    pub fn is_emergency_active(&self) -> bool {
625        self.document_sync.is_emergency_active()
626    }
627
628    /// Check if ACK is active
629    pub fn is_ack_active(&self) -> bool {
630        self.document_sync.is_ack_active()
631    }
632
633    /// Get current event type
634    pub fn current_event(&self) -> Option<EventType> {
635        self.document_sync.current_event()
636    }
637
638    // ==================== Emergency Management (Document-Based) ====================
639
640    /// Start a new emergency event with ACK tracking
641    ///
642    /// Creates an emergency event that tracks ACKs from all known peers.
643    /// Pass the list of known peer node IDs to track.
644    /// Returns the document bytes to broadcast.
645    /// If encryption is enabled, the document is encrypted.
646    pub fn start_emergency(&self, timestamp: u64, known_peers: &[u32]) -> Vec<u8> {
647        let data = self.document_sync.start_emergency(timestamp, known_peers);
648        self.notify(HiveEvent::MeshStateChanged {
649            peer_count: self.peer_manager.peer_count(),
650            connected_count: self.peer_manager.connected_count(),
651        });
652        self.encrypt_document(&data)
653    }
654
655    /// Start a new emergency using all currently known peers
656    ///
657    /// Convenience method that automatically includes all discovered peers.
658    pub fn start_emergency_with_known_peers(&self, timestamp: u64) -> Vec<u8> {
659        let peers: Vec<u32> = self
660            .peer_manager
661            .get_peers()
662            .iter()
663            .map(|p| p.node_id.as_u32())
664            .collect();
665        self.start_emergency(timestamp, &peers)
666    }
667
668    /// Record our ACK for the current emergency
669    ///
670    /// Returns the document bytes to broadcast, or None if no emergency is active.
671    /// If encryption is enabled, the document is encrypted.
672    pub fn ack_emergency(&self, timestamp: u64) -> Option<Vec<u8>> {
673        let result = self.document_sync.ack_emergency(timestamp);
674        if result.is_some() {
675            self.notify(HiveEvent::MeshStateChanged {
676                peer_count: self.peer_manager.peer_count(),
677                connected_count: self.peer_manager.connected_count(),
678            });
679        }
680        result.map(|data| self.encrypt_document(&data))
681    }
682
683    /// Clear the current emergency event
684    pub fn clear_emergency(&self) {
685        self.document_sync.clear_emergency();
686    }
687
688    /// Check if there's an active emergency
689    pub fn has_active_emergency(&self) -> bool {
690        self.document_sync.has_active_emergency()
691    }
692
693    /// Get emergency status info
694    ///
695    /// Returns (source_node, timestamp, acked_count, pending_count) if emergency is active.
696    pub fn get_emergency_status(&self) -> Option<(u32, u64, usize, usize)> {
697        self.document_sync.get_emergency_status()
698    }
699
700    /// Check if a specific peer has ACKed the current emergency
701    pub fn has_peer_acked(&self, peer_id: u32) -> bool {
702        self.document_sync.has_peer_acked(peer_id)
703    }
704
705    /// Check if all peers have ACKed the current emergency
706    pub fn all_peers_acked(&self) -> bool {
707        self.document_sync.all_peers_acked()
708    }
709
710    // ==================== BLE Callbacks (Platform -> Mesh) ====================
711
712    /// Called when a BLE device is discovered
713    ///
714    /// Returns `Some(HivePeer)` if this is a new HIVE peer on our mesh.
715    pub fn on_ble_discovered(
716        &self,
717        identifier: &str,
718        name: Option<&str>,
719        rssi: i8,
720        mesh_id: Option<&str>,
721        now_ms: u64,
722    ) -> Option<HivePeer> {
723        let (node_id, is_new) = self
724            .peer_manager
725            .on_discovered(identifier, name, rssi, mesh_id, now_ms)?;
726
727        let peer = self.peer_manager.get_peer(node_id)?;
728
729        if is_new {
730            self.notify(HiveEvent::PeerDiscovered { peer: peer.clone() });
731            self.notify_mesh_state_changed();
732        }
733
734        Some(peer)
735    }
736
737    /// Called when a BLE connection is established (outgoing)
738    ///
739    /// Returns the NodeId if this identifier is known.
740    pub fn on_ble_connected(&self, identifier: &str, now_ms: u64) -> Option<NodeId> {
741        let node_id = self.peer_manager.on_connected(identifier, now_ms)?;
742        self.notify(HiveEvent::PeerConnected { node_id });
743        self.notify_mesh_state_changed();
744        Some(node_id)
745    }
746
747    /// Called when a BLE connection is lost
748    pub fn on_ble_disconnected(
749        &self,
750        identifier: &str,
751        reason: DisconnectReason,
752    ) -> Option<NodeId> {
753        let (node_id, reason) = self.peer_manager.on_disconnected(identifier, reason)?;
754        self.notify(HiveEvent::PeerDisconnected { node_id, reason });
755        self.notify_mesh_state_changed();
756        Some(node_id)
757    }
758
759    /// Called when a BLE connection is lost, using NodeId directly
760    ///
761    /// Alternative to on_ble_disconnected() when only NodeId is known (e.g., ESP32).
762    pub fn on_peer_disconnected(&self, node_id: NodeId, reason: DisconnectReason) {
763        if self
764            .peer_manager
765            .on_disconnected_by_node_id(node_id, reason)
766        {
767            self.notify(HiveEvent::PeerDisconnected { node_id, reason });
768            self.notify_mesh_state_changed();
769        }
770    }
771
772    /// Called when a remote device connects to us (incoming connection)
773    ///
774    /// Use this when we're acting as a peripheral and a central connects to us.
775    pub fn on_incoming_connection(&self, identifier: &str, node_id: NodeId, now_ms: u64) -> bool {
776        let is_new = self
777            .peer_manager
778            .on_incoming_connection(identifier, node_id, now_ms);
779
780        if is_new {
781            if let Some(peer) = self.peer_manager.get_peer(node_id) {
782                self.notify(HiveEvent::PeerDiscovered { peer });
783            }
784        }
785
786        self.notify(HiveEvent::PeerConnected { node_id });
787        self.notify_mesh_state_changed();
788
789        is_new
790    }
791
792    /// Called when data is received from a peer
793    ///
794    /// Parses the document, merges it, and generates appropriate events.
795    /// If encryption is enabled, decrypts the document first.
796    /// Handles per-peer E2EE messages (KEY_EXCHANGE and PEER_E2EE markers).
797    /// Returns the source NodeId and whether the document contained an event.
798    pub fn on_ble_data_received(
799        &self,
800        identifier: &str,
801        data: &[u8],
802        now_ms: u64,
803    ) -> Option<DataReceivedResult> {
804        // Get node ID from identifier
805        let node_id = self.peer_manager.get_node_id(identifier)?;
806
807        // Check for per-peer E2EE messages first
808        if data.len() >= 2 {
809            match data[0] {
810                KEY_EXCHANGE_MARKER => {
811                    // Handle key exchange - returns response to send back
812                    let _response = self.handle_key_exchange(data, now_ms);
813                    // Return None as this isn't a document sync
814                    return None;
815                }
816                PEER_E2EE_MARKER => {
817                    // Handle encrypted peer message
818                    let _plaintext = self.handle_peer_e2ee_message(data, now_ms);
819                    // Return None as this isn't a document sync
820                    return None;
821                }
822                _ => {}
823            }
824        }
825
826        // Decrypt if encrypted (mesh-wide encryption)
827        let decrypted = self.decrypt_document(data, Some(identifier))?;
828
829        // Merge the document
830        let result = self.document_sync.merge_document(&decrypted)?;
831
832        // Record sync
833        self.peer_manager.record_sync(node_id, now_ms);
834
835        // Generate events based on what was received
836        if result.is_emergency() {
837            self.notify(HiveEvent::EmergencyReceived {
838                from_node: result.source_node,
839            });
840        } else if result.is_ack() {
841            self.notify(HiveEvent::AckReceived {
842                from_node: result.source_node,
843            });
844        }
845
846        if result.counter_changed {
847            self.notify(HiveEvent::DocumentSynced {
848                from_node: result.source_node,
849                total_count: result.total_count,
850            });
851        }
852
853        Some(DataReceivedResult {
854            source_node: result.source_node,
855            is_emergency: result.is_emergency(),
856            is_ack: result.is_ack(),
857            counter_changed: result.counter_changed,
858            emergency_changed: result.emergency_changed,
859            total_count: result.total_count,
860            event_timestamp: result.event.as_ref().map(|e| e.timestamp).unwrap_or(0),
861        })
862    }
863
864    /// Called when data is received but we don't have the identifier mapped
865    ///
866    /// Use this when receiving data from a peripheral we discovered.
867    /// If encryption is enabled, decrypts the document first.
868    /// Handles per-peer E2EE messages (KEY_EXCHANGE and PEER_E2EE markers).
869    pub fn on_ble_data_received_from_node(
870        &self,
871        node_id: NodeId,
872        data: &[u8],
873        now_ms: u64,
874    ) -> Option<DataReceivedResult> {
875        // Check for per-peer E2EE messages first
876        if data.len() >= 2 {
877            match data[0] {
878                KEY_EXCHANGE_MARKER => {
879                    let _response = self.handle_key_exchange(data, now_ms);
880                    return None;
881                }
882                PEER_E2EE_MARKER => {
883                    let _plaintext = self.handle_peer_e2ee_message(data, now_ms);
884                    return None;
885                }
886                _ => {}
887            }
888        }
889
890        // Decrypt if encrypted (mesh-wide encryption)
891        let source_hint = format!("node:{:08X}", node_id.as_u32());
892        let decrypted = self.decrypt_document(data, Some(&source_hint))?;
893
894        // Merge the document
895        let result = self.document_sync.merge_document(&decrypted)?;
896
897        // Record sync
898        self.peer_manager.record_sync(node_id, now_ms);
899
900        // Generate events based on what was received
901        if result.is_emergency() {
902            self.notify(HiveEvent::EmergencyReceived {
903                from_node: result.source_node,
904            });
905        } else if result.is_ack() {
906            self.notify(HiveEvent::AckReceived {
907                from_node: result.source_node,
908            });
909        }
910
911        if result.counter_changed {
912            self.notify(HiveEvent::DocumentSynced {
913                from_node: result.source_node,
914                total_count: result.total_count,
915            });
916        }
917
918        Some(DataReceivedResult {
919            source_node: result.source_node,
920            is_emergency: result.is_emergency(),
921            is_ack: result.is_ack(),
922            counter_changed: result.counter_changed,
923            emergency_changed: result.emergency_changed,
924            total_count: result.total_count,
925            event_timestamp: result.event.as_ref().map(|e| e.timestamp).unwrap_or(0),
926        })
927    }
928
929    /// Called when data is received without a known identifier
930    ///
931    /// This is the simplest data receive method - it extracts the source node_id
932    /// from the document itself. Use this when you don't track identifiers
933    /// (e.g., ESP32 NimBLE).
934    /// If encryption is enabled, decrypts the document first.
935    /// Handles per-peer E2EE messages (KEY_EXCHANGE and PEER_E2EE markers).
936    pub fn on_ble_data(
937        &self,
938        identifier: &str,
939        data: &[u8],
940        now_ms: u64,
941    ) -> Option<DataReceivedResult> {
942        // Check for per-peer E2EE messages first
943        if data.len() >= 2 {
944            match data[0] {
945                KEY_EXCHANGE_MARKER => {
946                    let _response = self.handle_key_exchange(data, now_ms);
947                    return None;
948                }
949                PEER_E2EE_MARKER => {
950                    let _plaintext = self.handle_peer_e2ee_message(data, now_ms);
951                    return None;
952                }
953                _ => {}
954            }
955        }
956
957        // Decrypt if encrypted (mesh-wide encryption)
958        let decrypted = self.decrypt_document(data, Some(identifier))?;
959
960        // Merge the document (extracts node_id internally)
961        let result = self.document_sync.merge_document(&decrypted)?;
962
963        // Record sync using the source_node from the merged document
964        self.peer_manager.record_sync(result.source_node, now_ms);
965
966        // Add the peer if not already known (creates peer entry from document data)
967        self.peer_manager
968            .on_incoming_connection(identifier, result.source_node, now_ms);
969
970        // Generate events based on what was received
971        if result.is_emergency() {
972            self.notify(HiveEvent::EmergencyReceived {
973                from_node: result.source_node,
974            });
975        } else if result.is_ack() {
976            self.notify(HiveEvent::AckReceived {
977                from_node: result.source_node,
978            });
979        }
980
981        if result.counter_changed {
982            self.notify(HiveEvent::DocumentSynced {
983                from_node: result.source_node,
984                total_count: result.total_count,
985            });
986        }
987
988        Some(DataReceivedResult {
989            source_node: result.source_node,
990            is_emergency: result.is_emergency(),
991            is_ack: result.is_ack(),
992            counter_changed: result.counter_changed,
993            emergency_changed: result.emergency_changed,
994            total_count: result.total_count,
995            event_timestamp: result.event.as_ref().map(|e| e.timestamp).unwrap_or(0),
996        })
997    }
998
999    // ==================== Periodic Maintenance ====================
1000
1001    /// Periodic tick - call this regularly (e.g., every second)
1002    ///
1003    /// Performs:
1004    /// - Stale peer cleanup
1005    /// - Periodic sync broadcast (if interval elapsed)
1006    ///
1007    /// Returns `Some(data)` if a sync broadcast is needed.
1008    pub fn tick(&self, now_ms: u64) -> Option<Vec<u8>> {
1009        use std::sync::atomic::Ordering;
1010
1011        // Use u32 for atomic storage (wraps every ~49 days, intervals still work)
1012        let now_ms_32 = now_ms as u32;
1013
1014        // Cleanup stale peers
1015        let last_cleanup = self.last_cleanup_ms.load(Ordering::Relaxed);
1016        let cleanup_elapsed = now_ms_32.wrapping_sub(last_cleanup);
1017        if cleanup_elapsed >= self.config.peer_config.cleanup_interval_ms as u32 {
1018            self.last_cleanup_ms.store(now_ms_32, Ordering::Relaxed);
1019            let removed = self.peer_manager.cleanup_stale(now_ms);
1020            for node_id in &removed {
1021                self.notify(HiveEvent::PeerLost { node_id: *node_id });
1022            }
1023            if !removed.is_empty() {
1024                self.notify_mesh_state_changed();
1025            }
1026        }
1027
1028        // Check if sync broadcast is needed
1029        let last_sync = self.last_sync_ms.load(Ordering::Relaxed);
1030        let sync_elapsed = now_ms_32.wrapping_sub(last_sync);
1031        if sync_elapsed >= self.config.sync_interval_ms as u32 {
1032            self.last_sync_ms.store(now_ms_32, Ordering::Relaxed);
1033            // Only broadcast if we have connected peers
1034            if self.peer_manager.connected_count() > 0 {
1035                let doc = self.document_sync.build_document();
1036                return Some(self.encrypt_document(&doc));
1037            }
1038        }
1039
1040        None
1041    }
1042
1043    // ==================== State Queries ====================
1044
1045    /// Get all known peers
1046    pub fn get_peers(&self) -> Vec<HivePeer> {
1047        self.peer_manager.get_peers()
1048    }
1049
1050    /// Get connected peers only
1051    pub fn get_connected_peers(&self) -> Vec<HivePeer> {
1052        self.peer_manager.get_connected_peers()
1053    }
1054
1055    /// Get a specific peer by NodeId
1056    pub fn get_peer(&self, node_id: NodeId) -> Option<HivePeer> {
1057        self.peer_manager.get_peer(node_id)
1058    }
1059
1060    /// Get peer count
1061    pub fn peer_count(&self) -> usize {
1062        self.peer_manager.peer_count()
1063    }
1064
1065    /// Get connected peer count
1066    pub fn connected_count(&self) -> usize {
1067        self.peer_manager.connected_count()
1068    }
1069
1070    /// Check if a device mesh ID matches our mesh
1071    pub fn matches_mesh(&self, device_mesh_id: Option<&str>) -> bool {
1072        self.peer_manager.matches_mesh(device_mesh_id)
1073    }
1074
1075    /// Get total counter value
1076    pub fn total_count(&self) -> u64 {
1077        self.document_sync.total_count()
1078    }
1079
1080    /// Get document version
1081    pub fn document_version(&self) -> u32 {
1082        self.document_sync.version()
1083    }
1084
1085    /// Get document version (alias)
1086    pub fn version(&self) -> u32 {
1087        self.document_sync.version()
1088    }
1089
1090    /// Update health status (battery percentage)
1091    pub fn update_health(&self, battery_percent: u8) {
1092        self.document_sync.update_health(battery_percent);
1093    }
1094
1095    /// Update activity level (0=still, 1=walking, 2=running, 3=fall)
1096    pub fn update_activity(&self, activity: u8) {
1097        self.document_sync.update_activity(activity);
1098    }
1099
1100    /// Update full health status (battery and activity)
1101    pub fn update_health_full(&self, battery_percent: u8, activity: u8) {
1102        self.document_sync
1103            .update_health_full(battery_percent, activity);
1104    }
1105
1106    /// Build current document for transmission
1107    ///
1108    /// If encryption is enabled, the document is encrypted.
1109    pub fn build_document(&self) -> Vec<u8> {
1110        let doc = self.document_sync.build_document();
1111        self.encrypt_document(&doc)
1112    }
1113
1114    /// Get peers that should be synced with
1115    pub fn peers_needing_sync(&self, now_ms: u64) -> Vec<HivePeer> {
1116        self.peer_manager.peers_needing_sync(now_ms)
1117    }
1118
1119    // ==================== Internal Helpers ====================
1120
1121    fn notify(&self, event: HiveEvent) {
1122        self.observers.notify(event);
1123    }
1124
1125    fn notify_mesh_state_changed(&self) {
1126        self.notify(HiveEvent::MeshStateChanged {
1127            peer_count: self.peer_manager.peer_count(),
1128            connected_count: self.peer_manager.connected_count(),
1129        });
1130    }
1131}
1132
1133/// Result from receiving BLE data
1134#[derive(Debug, Clone)]
1135pub struct DataReceivedResult {
1136    /// Node that sent this data
1137    pub source_node: NodeId,
1138
1139    /// Whether this contained an emergency event
1140    pub is_emergency: bool,
1141
1142    /// Whether this contained an ACK event
1143    pub is_ack: bool,
1144
1145    /// Whether the counter changed (new data)
1146    pub counter_changed: bool,
1147
1148    /// Whether emergency state changed (new emergency or ACK updates)
1149    pub emergency_changed: bool,
1150
1151    /// Updated total count
1152    pub total_count: u64,
1153
1154    /// Event timestamp (if event present) - use to detect duplicate events
1155    pub event_timestamp: u64,
1156}
1157
1158#[cfg(all(test, feature = "std"))]
1159mod tests {
1160    use super::*;
1161    use crate::observer::CollectingObserver;
1162
1163    fn create_mesh(node_id: u32, callsign: &str) -> HiveMesh {
1164        let config = HiveMeshConfig::new(NodeId::new(node_id), callsign, "TEST");
1165        HiveMesh::new(config)
1166    }
1167
1168    #[test]
1169    fn test_mesh_creation() {
1170        let mesh = create_mesh(0x12345678, "ALPHA-1");
1171
1172        assert_eq!(mesh.node_id().as_u32(), 0x12345678);
1173        assert_eq!(mesh.callsign(), "ALPHA-1");
1174        assert_eq!(mesh.mesh_id(), "TEST");
1175        assert_eq!(mesh.device_name(), "HIVE_TEST-12345678");
1176    }
1177
1178    #[test]
1179    fn test_peer_discovery() {
1180        let mesh = create_mesh(0x11111111, "ALPHA-1");
1181        let observer = Arc::new(CollectingObserver::new());
1182        mesh.add_observer(observer.clone());
1183
1184        // Discover a peer
1185        let peer = mesh.on_ble_discovered(
1186            "device-uuid",
1187            Some("HIVE_TEST-22222222"),
1188            -65,
1189            Some("TEST"),
1190            1000,
1191        );
1192
1193        assert!(peer.is_some());
1194        let peer = peer.unwrap();
1195        assert_eq!(peer.node_id.as_u32(), 0x22222222);
1196
1197        // Check events were generated
1198        let events = observer.events();
1199        assert!(events
1200            .iter()
1201            .any(|e| matches!(e, HiveEvent::PeerDiscovered { .. })));
1202        assert!(events
1203            .iter()
1204            .any(|e| matches!(e, HiveEvent::MeshStateChanged { .. })));
1205    }
1206
1207    #[test]
1208    fn test_connection_lifecycle() {
1209        let mesh = create_mesh(0x11111111, "ALPHA-1");
1210        let observer = Arc::new(CollectingObserver::new());
1211        mesh.add_observer(observer.clone());
1212
1213        // Discover and connect
1214        mesh.on_ble_discovered(
1215            "device-uuid",
1216            Some("HIVE_TEST-22222222"),
1217            -65,
1218            Some("TEST"),
1219            1000,
1220        );
1221
1222        let node_id = mesh.on_ble_connected("device-uuid", 2000);
1223        assert_eq!(node_id, Some(NodeId::new(0x22222222)));
1224        assert_eq!(mesh.connected_count(), 1);
1225
1226        // Disconnect
1227        let node_id = mesh.on_ble_disconnected("device-uuid", DisconnectReason::RemoteRequest);
1228        assert_eq!(node_id, Some(NodeId::new(0x22222222)));
1229        assert_eq!(mesh.connected_count(), 0);
1230
1231        // Check events
1232        let events = observer.events();
1233        assert!(events
1234            .iter()
1235            .any(|e| matches!(e, HiveEvent::PeerConnected { .. })));
1236        assert!(events
1237            .iter()
1238            .any(|e| matches!(e, HiveEvent::PeerDisconnected { .. })));
1239    }
1240
1241    #[test]
1242    fn test_emergency_flow() {
1243        let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1244        let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1245
1246        let observer2 = Arc::new(CollectingObserver::new());
1247        mesh2.add_observer(observer2.clone());
1248
1249        // mesh1 sends emergency
1250        let doc = mesh1.send_emergency(1000);
1251        assert!(mesh1.is_emergency_active());
1252
1253        // mesh2 receives it
1254        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1255
1256        assert!(result.is_some());
1257        let result = result.unwrap();
1258        assert!(result.is_emergency);
1259        assert_eq!(result.source_node.as_u32(), 0x11111111);
1260
1261        // Check events on mesh2
1262        let events = observer2.events();
1263        assert!(events
1264            .iter()
1265            .any(|e| matches!(e, HiveEvent::EmergencyReceived { .. })));
1266    }
1267
1268    #[test]
1269    fn test_ack_flow() {
1270        let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1271        let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1272
1273        let observer2 = Arc::new(CollectingObserver::new());
1274        mesh2.add_observer(observer2.clone());
1275
1276        // mesh1 sends ACK
1277        let doc = mesh1.send_ack(1000);
1278        assert!(mesh1.is_ack_active());
1279
1280        // mesh2 receives it
1281        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1282
1283        assert!(result.is_some());
1284        let result = result.unwrap();
1285        assert!(result.is_ack);
1286
1287        // Check events on mesh2
1288        let events = observer2.events();
1289        assert!(events
1290            .iter()
1291            .any(|e| matches!(e, HiveEvent::AckReceived { .. })));
1292    }
1293
1294    #[test]
1295    fn test_tick_cleanup() {
1296        let config = HiveMeshConfig::new(NodeId::new(0x11111111), "ALPHA-1", "TEST")
1297            .with_peer_timeout(10_000);
1298        let mesh = HiveMesh::new(config);
1299
1300        let observer = Arc::new(CollectingObserver::new());
1301        mesh.add_observer(observer.clone());
1302
1303        // Discover a peer
1304        mesh.on_ble_discovered(
1305            "device-uuid",
1306            Some("HIVE_TEST-22222222"),
1307            -65,
1308            Some("TEST"),
1309            1000,
1310        );
1311        assert_eq!(mesh.peer_count(), 1);
1312
1313        // Tick at t=5000 - not stale yet
1314        mesh.tick(5000);
1315        assert_eq!(mesh.peer_count(), 1);
1316
1317        // Tick at t=20000 - peer is stale (10s timeout exceeded)
1318        mesh.tick(20000);
1319        assert_eq!(mesh.peer_count(), 0);
1320
1321        // Check PeerLost event
1322        let events = observer.events();
1323        assert!(events
1324            .iter()
1325            .any(|e| matches!(e, HiveEvent::PeerLost { .. })));
1326    }
1327
1328    #[test]
1329    fn test_tick_sync_broadcast() {
1330        let config = HiveMeshConfig::new(NodeId::new(0x11111111), "ALPHA-1", "TEST")
1331            .with_sync_interval(5000);
1332        let mesh = HiveMesh::new(config);
1333
1334        // Discover and connect a peer first
1335        mesh.on_ble_discovered(
1336            "device-uuid",
1337            Some("HIVE_TEST-22222222"),
1338            -65,
1339            Some("TEST"),
1340            1000,
1341        );
1342        mesh.on_ble_connected("device-uuid", 1000);
1343
1344        // First tick at t=0 sets last_sync
1345        let _result = mesh.tick(0);
1346        // May or may not broadcast depending on initial state
1347
1348        // Tick before interval - no broadcast
1349        let result = mesh.tick(3000);
1350        assert!(result.is_none());
1351
1352        // After interval - should broadcast
1353        let result = mesh.tick(6000);
1354        assert!(result.is_some());
1355
1356        // Immediate second tick - no broadcast (interval not elapsed)
1357        let result = mesh.tick(6100);
1358        assert!(result.is_none());
1359
1360        // After another interval - should broadcast again
1361        let result = mesh.tick(12000);
1362        assert!(result.is_some());
1363    }
1364
1365    #[test]
1366    fn test_incoming_connection() {
1367        let mesh = create_mesh(0x11111111, "ALPHA-1");
1368        let observer = Arc::new(CollectingObserver::new());
1369        mesh.add_observer(observer.clone());
1370
1371        // Incoming connection from unknown peer
1372        let is_new = mesh.on_incoming_connection("central-uuid", NodeId::new(0x22222222), 1000);
1373
1374        assert!(is_new);
1375        assert_eq!(mesh.peer_count(), 1);
1376        assert_eq!(mesh.connected_count(), 1);
1377
1378        // Check events
1379        let events = observer.events();
1380        assert!(events
1381            .iter()
1382            .any(|e| matches!(e, HiveEvent::PeerDiscovered { .. })));
1383        assert!(events
1384            .iter()
1385            .any(|e| matches!(e, HiveEvent::PeerConnected { .. })));
1386    }
1387
1388    #[test]
1389    fn test_mesh_filtering() {
1390        let mesh = create_mesh(0x11111111, "ALPHA-1");
1391
1392        // Wrong mesh - ignored
1393        let peer = mesh.on_ble_discovered(
1394            "device-uuid-1",
1395            Some("HIVE_OTHER-22222222"),
1396            -65,
1397            Some("OTHER"),
1398            1000,
1399        );
1400        assert!(peer.is_none());
1401        assert_eq!(mesh.peer_count(), 0);
1402
1403        // Correct mesh - accepted
1404        let peer = mesh.on_ble_discovered(
1405            "device-uuid-2",
1406            Some("HIVE_TEST-33333333"),
1407            -65,
1408            Some("TEST"),
1409            1000,
1410        );
1411        assert!(peer.is_some());
1412        assert_eq!(mesh.peer_count(), 1);
1413    }
1414
1415    // ==================== Encryption Tests ====================
1416
1417    fn create_encrypted_mesh(node_id: u32, callsign: &str, secret: [u8; 32]) -> HiveMesh {
1418        let config =
1419            HiveMeshConfig::new(NodeId::new(node_id), callsign, "TEST").with_encryption(secret);
1420        HiveMesh::new(config)
1421    }
1422
1423    #[test]
1424    fn test_encryption_enabled() {
1425        let secret = [0x42u8; 32];
1426        let mesh = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1427
1428        assert!(mesh.is_encryption_enabled());
1429    }
1430
1431    #[test]
1432    fn test_encryption_disabled_by_default() {
1433        let mesh = create_mesh(0x11111111, "ALPHA-1");
1434
1435        assert!(!mesh.is_encryption_enabled());
1436    }
1437
1438    #[test]
1439    fn test_encrypted_document_exchange() {
1440        let secret = [0x42u8; 32];
1441        let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1442        let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret);
1443
1444        // mesh1 sends document
1445        let doc = mesh1.build_document();
1446
1447        // Document should be encrypted (starts with ENCRYPTED_MARKER)
1448        assert!(doc.len() >= 2);
1449        assert_eq!(doc[0], crate::document::ENCRYPTED_MARKER);
1450
1451        // mesh2 receives and decrypts
1452        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1453
1454        assert!(result.is_some());
1455        let result = result.unwrap();
1456        assert_eq!(result.source_node.as_u32(), 0x11111111);
1457    }
1458
1459    #[test]
1460    fn test_encrypted_emergency_exchange() {
1461        let secret = [0x42u8; 32];
1462        let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1463        let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret);
1464
1465        let observer = Arc::new(CollectingObserver::new());
1466        mesh2.add_observer(observer.clone());
1467
1468        // mesh1 sends emergency
1469        let doc = mesh1.send_emergency(1000);
1470
1471        // mesh2 receives and decrypts
1472        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1473
1474        assert!(result.is_some());
1475        let result = result.unwrap();
1476        assert!(result.is_emergency);
1477
1478        // Check EmergencyReceived event was fired
1479        let events = observer.events();
1480        assert!(events
1481            .iter()
1482            .any(|e| matches!(e, HiveEvent::EmergencyReceived { .. })));
1483    }
1484
1485    #[test]
1486    fn test_wrong_key_fails_decrypt() {
1487        let secret1 = [0x42u8; 32];
1488        let secret2 = [0x43u8; 32]; // Different key
1489        let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret1);
1490        let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret2);
1491
1492        // mesh1 sends document
1493        let doc = mesh1.build_document();
1494
1495        // mesh2 cannot decrypt (wrong key)
1496        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1497
1498        assert!(result.is_none());
1499    }
1500
1501    #[test]
1502    fn test_unencrypted_mesh_can_read_unencrypted() {
1503        let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1504        let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1505
1506        // mesh1 sends document (unencrypted)
1507        let doc = mesh1.build_document();
1508
1509        // mesh2 receives (also unencrypted)
1510        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1511
1512        assert!(result.is_some());
1513    }
1514
1515    #[test]
1516    fn test_encrypted_mesh_can_receive_unencrypted() {
1517        // Backward compatibility: encrypted mesh can receive unencrypted docs
1518        let secret = [0x42u8; 32];
1519        let mesh1 = create_mesh(0x11111111, "ALPHA-1"); // unencrypted
1520        let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret); // encrypted
1521
1522        // mesh1 sends unencrypted document
1523        let doc = mesh1.build_document();
1524
1525        // mesh2 can receive unencrypted (backward compat)
1526        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1527
1528        assert!(result.is_some());
1529    }
1530
1531    #[test]
1532    fn test_unencrypted_mesh_cannot_receive_encrypted() {
1533        let secret = [0x42u8; 32];
1534        let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret); // encrypted
1535        let mesh2 = create_mesh(0x22222222, "BRAVO-1"); // unencrypted
1536
1537        // mesh1 sends encrypted document
1538        let doc = mesh1.build_document();
1539
1540        // mesh2 cannot decrypt (no key)
1541        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1542
1543        assert!(result.is_none());
1544    }
1545
1546    #[test]
1547    fn test_enable_disable_encryption() {
1548        let mut mesh = create_mesh(0x11111111, "ALPHA-1");
1549
1550        assert!(!mesh.is_encryption_enabled());
1551
1552        // Enable encryption
1553        let secret = [0x42u8; 32];
1554        mesh.enable_encryption(&secret);
1555        assert!(mesh.is_encryption_enabled());
1556
1557        // Build document should now be encrypted
1558        let doc = mesh.build_document();
1559        assert_eq!(doc[0], crate::document::ENCRYPTED_MARKER);
1560
1561        // Disable encryption
1562        mesh.disable_encryption();
1563        assert!(!mesh.is_encryption_enabled());
1564
1565        // Build document should now be unencrypted
1566        let doc = mesh.build_document();
1567        assert_ne!(doc[0], crate::document::ENCRYPTED_MARKER);
1568    }
1569
1570    #[test]
1571    fn test_encryption_overhead() {
1572        let secret = [0x42u8; 32];
1573        let mesh_encrypted = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1574        let mesh_unencrypted = create_mesh(0x22222222, "BRAVO-1");
1575
1576        let doc_encrypted = mesh_encrypted.build_document();
1577        let doc_unencrypted = mesh_unencrypted.build_document();
1578
1579        // Encrypted doc should be larger by:
1580        // - 2 bytes marker header (0xAE + reserved)
1581        // - 12 bytes nonce
1582        // - 16 bytes auth tag
1583        // Total: 30 bytes overhead
1584        let overhead = doc_encrypted.len() - doc_unencrypted.len();
1585        assert_eq!(overhead, 30); // 2 (marker) + 12 (nonce) + 16 (tag)
1586    }
1587
1588    // ==================== Per-Peer E2EE Tests ====================
1589
1590    #[test]
1591    fn test_peer_e2ee_enable_disable() {
1592        let mesh = create_mesh(0x11111111, "ALPHA-1");
1593
1594        assert!(!mesh.is_peer_e2ee_enabled());
1595        assert!(mesh.peer_e2ee_public_key().is_none());
1596
1597        mesh.enable_peer_e2ee();
1598        assert!(mesh.is_peer_e2ee_enabled());
1599        assert!(mesh.peer_e2ee_public_key().is_some());
1600
1601        mesh.disable_peer_e2ee();
1602        assert!(!mesh.is_peer_e2ee_enabled());
1603    }
1604
1605    #[test]
1606    fn test_peer_e2ee_initiate_session() {
1607        let mesh = create_mesh(0x11111111, "ALPHA-1");
1608        mesh.enable_peer_e2ee();
1609
1610        let key_exchange = mesh.initiate_peer_e2ee(NodeId::new(0x22222222), 1000);
1611        assert!(key_exchange.is_some());
1612
1613        let key_exchange = key_exchange.unwrap();
1614        // Should start with KEY_EXCHANGE_MARKER
1615        assert_eq!(key_exchange[0], crate::document::KEY_EXCHANGE_MARKER);
1616
1617        // Should have a pending session
1618        assert_eq!(mesh.peer_e2ee_session_count(), 1);
1619        assert_eq!(mesh.peer_e2ee_established_count(), 0);
1620    }
1621
1622    #[test]
1623    fn test_peer_e2ee_full_handshake() {
1624        let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1625        let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1626
1627        mesh1.enable_peer_e2ee();
1628        mesh2.enable_peer_e2ee();
1629
1630        let observer1 = Arc::new(CollectingObserver::new());
1631        let observer2 = Arc::new(CollectingObserver::new());
1632        mesh1.add_observer(observer1.clone());
1633        mesh2.add_observer(observer2.clone());
1634
1635        // mesh1 initiates to mesh2
1636        let key_exchange1 = mesh1
1637            .initiate_peer_e2ee(NodeId::new(0x22222222), 1000)
1638            .unwrap();
1639
1640        // mesh2 receives and responds
1641        let response = mesh2.handle_key_exchange(&key_exchange1, 1000);
1642        assert!(response.is_some());
1643
1644        // Check mesh2 has established session
1645        assert!(mesh2.has_peer_e2ee_session(NodeId::new(0x11111111)));
1646
1647        // mesh1 receives mesh2's response
1648        let key_exchange2 = response.unwrap();
1649        let _ = mesh1.handle_key_exchange(&key_exchange2, 1000);
1650
1651        // Check mesh1 has established session
1652        assert!(mesh1.has_peer_e2ee_session(NodeId::new(0x22222222)));
1653
1654        // Both should have E2EE established events
1655        let events1 = observer1.events();
1656        assert!(events1
1657            .iter()
1658            .any(|e| matches!(e, HiveEvent::PeerE2eeEstablished { .. })));
1659
1660        let events2 = observer2.events();
1661        assert!(events2
1662            .iter()
1663            .any(|e| matches!(e, HiveEvent::PeerE2eeEstablished { .. })));
1664    }
1665
1666    #[test]
1667    fn test_peer_e2ee_encrypt_decrypt() {
1668        let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1669        let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1670
1671        mesh1.enable_peer_e2ee();
1672        mesh2.enable_peer_e2ee();
1673
1674        // Establish session via key exchange
1675        let key_exchange1 = mesh1
1676            .initiate_peer_e2ee(NodeId::new(0x22222222), 1000)
1677            .unwrap();
1678        let key_exchange2 = mesh2.handle_key_exchange(&key_exchange1, 1000).unwrap();
1679        mesh1.handle_key_exchange(&key_exchange2, 1000);
1680
1681        // mesh1 sends encrypted message to mesh2
1682        let plaintext = b"Secret message from mesh1";
1683        let encrypted = mesh1.send_peer_e2ee(NodeId::new(0x22222222), plaintext, 2000);
1684        assert!(encrypted.is_some());
1685
1686        let encrypted = encrypted.unwrap();
1687        // Should start with PEER_E2EE_MARKER
1688        assert_eq!(encrypted[0], crate::document::PEER_E2EE_MARKER);
1689
1690        // mesh2 receives and decrypts
1691        let observer2 = Arc::new(CollectingObserver::new());
1692        mesh2.add_observer(observer2.clone());
1693
1694        let decrypted = mesh2.handle_peer_e2ee_message(&encrypted, 2000);
1695        assert!(decrypted.is_some());
1696        assert_eq!(decrypted.unwrap(), plaintext);
1697
1698        // Should have received message event
1699        let events = observer2.events();
1700        assert!(events.iter().any(|e| matches!(
1701            e,
1702            HiveEvent::PeerE2eeMessageReceived { from_node, data }
1703            if from_node.as_u32() == 0x11111111 && data == plaintext
1704        )));
1705    }
1706
1707    #[test]
1708    fn test_peer_e2ee_bidirectional() {
1709        let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1710        let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1711
1712        mesh1.enable_peer_e2ee();
1713        mesh2.enable_peer_e2ee();
1714
1715        // Establish session
1716        let key_exchange1 = mesh1
1717            .initiate_peer_e2ee(NodeId::new(0x22222222), 1000)
1718            .unwrap();
1719        let key_exchange2 = mesh2.handle_key_exchange(&key_exchange1, 1000).unwrap();
1720        mesh1.handle_key_exchange(&key_exchange2, 1000);
1721
1722        // mesh1 -> mesh2
1723        let msg1 = mesh1
1724            .send_peer_e2ee(NodeId::new(0x22222222), b"Hello from mesh1", 2000)
1725            .unwrap();
1726        let dec1 = mesh2.handle_peer_e2ee_message(&msg1, 2000).unwrap();
1727        assert_eq!(dec1, b"Hello from mesh1");
1728
1729        // mesh2 -> mesh1
1730        let msg2 = mesh2
1731            .send_peer_e2ee(NodeId::new(0x11111111), b"Hello from mesh2", 2000)
1732            .unwrap();
1733        let dec2 = mesh1.handle_peer_e2ee_message(&msg2, 2000).unwrap();
1734        assert_eq!(dec2, b"Hello from mesh2");
1735    }
1736
1737    #[test]
1738    fn test_peer_e2ee_close_session() {
1739        let mesh = create_mesh(0x11111111, "ALPHA-1");
1740        mesh.enable_peer_e2ee();
1741
1742        let observer = Arc::new(CollectingObserver::new());
1743        mesh.add_observer(observer.clone());
1744
1745        // Initiate a session
1746        mesh.initiate_peer_e2ee(NodeId::new(0x22222222), 1000);
1747        assert_eq!(mesh.peer_e2ee_session_count(), 1);
1748
1749        // Close session
1750        mesh.close_peer_e2ee(NodeId::new(0x22222222));
1751
1752        // Check close event
1753        let events = observer.events();
1754        assert!(events
1755            .iter()
1756            .any(|e| matches!(e, HiveEvent::PeerE2eeClosed { .. })));
1757    }
1758
1759    #[test]
1760    fn test_peer_e2ee_without_enabling() {
1761        let mesh = create_mesh(0x11111111, "ALPHA-1");
1762
1763        // E2EE not enabled - should return None
1764        let result = mesh.initiate_peer_e2ee(NodeId::new(0x22222222), 1000);
1765        assert!(result.is_none());
1766
1767        let result = mesh.send_peer_e2ee(NodeId::new(0x22222222), b"test", 1000);
1768        assert!(result.is_none());
1769
1770        assert!(!mesh.has_peer_e2ee_session(NodeId::new(0x22222222)));
1771    }
1772
1773    #[test]
1774    fn test_peer_e2ee_overhead() {
1775        let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1776        let mesh2 = create_mesh(0x22222222, "BRAVO-1");
1777
1778        mesh1.enable_peer_e2ee();
1779        mesh2.enable_peer_e2ee();
1780
1781        // Establish session
1782        let key_exchange1 = mesh1
1783            .initiate_peer_e2ee(NodeId::new(0x22222222), 1000)
1784            .unwrap();
1785        let key_exchange2 = mesh2.handle_key_exchange(&key_exchange1, 1000).unwrap();
1786        mesh1.handle_key_exchange(&key_exchange2, 1000);
1787
1788        // Encrypt a message
1789        let plaintext = b"Test message";
1790        let encrypted = mesh1
1791            .send_peer_e2ee(NodeId::new(0x22222222), plaintext, 2000)
1792            .unwrap();
1793
1794        // Overhead should be:
1795        // - 2 bytes marker header
1796        // - 4 bytes recipient node ID
1797        // - 4 bytes sender node ID
1798        // - 8 bytes counter
1799        // - 12 bytes nonce
1800        // - 16 bytes auth tag
1801        // Total: 46 bytes overhead
1802        let overhead = encrypted.len() - plaintext.len();
1803        assert_eq!(overhead, 46);
1804    }
1805
1806    // ==================== Strict Encryption Mode Tests ====================
1807
1808    fn create_strict_encrypted_mesh(node_id: u32, callsign: &str, secret: [u8; 32]) -> HiveMesh {
1809        let config = HiveMeshConfig::new(NodeId::new(node_id), callsign, "TEST")
1810            .with_encryption(secret)
1811            .with_strict_encryption();
1812        HiveMesh::new(config)
1813    }
1814
1815    #[test]
1816    fn test_strict_encryption_enabled() {
1817        let secret = [0x42u8; 32];
1818        let mesh = create_strict_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1819
1820        assert!(mesh.is_encryption_enabled());
1821        assert!(mesh.is_strict_encryption_enabled());
1822    }
1823
1824    #[test]
1825    fn test_strict_encryption_disabled_by_default() {
1826        let secret = [0x42u8; 32];
1827        let mesh = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1828
1829        assert!(mesh.is_encryption_enabled());
1830        assert!(!mesh.is_strict_encryption_enabled());
1831    }
1832
1833    #[test]
1834    fn test_strict_encryption_requires_encryption_enabled() {
1835        // strict_encryption without encryption should have no effect
1836        let config = HiveMeshConfig::new(NodeId::new(0x11111111), "ALPHA-1", "TEST")
1837            .with_strict_encryption(); // No encryption!
1838        let mesh = HiveMesh::new(config);
1839
1840        assert!(!mesh.is_encryption_enabled());
1841        assert!(!mesh.is_strict_encryption_enabled());
1842    }
1843
1844    #[test]
1845    fn test_strict_mode_accepts_encrypted_documents() {
1846        let secret = [0x42u8; 32];
1847        let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret);
1848        let mesh2 = create_strict_encrypted_mesh(0x22222222, "BRAVO-1", secret);
1849
1850        // mesh1 sends encrypted document
1851        let doc = mesh1.build_document();
1852        assert_eq!(doc[0], crate::document::ENCRYPTED_MARKER);
1853
1854        // mesh2 (strict mode) should accept encrypted documents
1855        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1856        assert!(result.is_some());
1857    }
1858
1859    #[test]
1860    fn test_strict_mode_rejects_unencrypted_documents() {
1861        let secret = [0x42u8; 32];
1862        let mesh1 = create_mesh(0x11111111, "ALPHA-1"); // Unencrypted sender
1863        let mesh2 = create_strict_encrypted_mesh(0x22222222, "BRAVO-1", secret); // Strict receiver
1864
1865        let observer = Arc::new(CollectingObserver::new());
1866        mesh2.add_observer(observer.clone());
1867
1868        // mesh1 sends unencrypted document
1869        let doc = mesh1.build_document();
1870        assert_ne!(doc[0], crate::document::ENCRYPTED_MARKER);
1871
1872        // mesh2 (strict mode) should reject unencrypted documents
1873        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1874        assert!(result.is_none());
1875
1876        // Should have SecurityViolation event
1877        let events = observer.events();
1878        assert!(events.iter().any(|e| matches!(
1879            e,
1880            HiveEvent::SecurityViolation {
1881                kind: crate::observer::SecurityViolationKind::UnencryptedInStrictMode,
1882                ..
1883            }
1884        )));
1885    }
1886
1887    #[test]
1888    fn test_non_strict_mode_accepts_unencrypted_documents() {
1889        let secret = [0x42u8; 32];
1890        let mesh1 = create_mesh(0x11111111, "ALPHA-1"); // Unencrypted sender
1891        let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret); // Non-strict receiver
1892
1893        // mesh1 sends unencrypted document
1894        let doc = mesh1.build_document();
1895
1896        // mesh2 (non-strict) should accept unencrypted documents (backward compat)
1897        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1898        assert!(result.is_some());
1899    }
1900
1901    #[test]
1902    fn test_strict_mode_security_violation_event_includes_source() {
1903        let secret = [0x42u8; 32];
1904        let mesh1 = create_mesh(0x11111111, "ALPHA-1");
1905        let mesh2 = create_strict_encrypted_mesh(0x22222222, "BRAVO-1", secret);
1906
1907        let observer = Arc::new(CollectingObserver::new());
1908        mesh2.add_observer(observer.clone());
1909
1910        let doc = mesh1.build_document();
1911
1912        // Use on_ble_data_received with identifier to test source is captured
1913        mesh2.on_ble_discovered(
1914            "test-device-uuid",
1915            Some("HIVE_TEST-11111111"),
1916            -65,
1917            Some("TEST"),
1918            500,
1919        );
1920        mesh2.on_ble_connected("test-device-uuid", 600);
1921
1922        let _result = mesh2.on_ble_data_received("test-device-uuid", &doc, 1000);
1923
1924        // Check SecurityViolation event has source
1925        let events = observer.events();
1926        let violation = events.iter().find(|e| {
1927            matches!(
1928                e,
1929                HiveEvent::SecurityViolation {
1930                    kind: crate::observer::SecurityViolationKind::UnencryptedInStrictMode,
1931                    ..
1932                }
1933            )
1934        });
1935        assert!(violation.is_some());
1936
1937        if let Some(HiveEvent::SecurityViolation { source, .. }) = violation {
1938            assert!(source.is_some());
1939            assert_eq!(source.as_ref().unwrap(), "test-device-uuid");
1940        }
1941    }
1942
1943    #[test]
1944    fn test_decryption_failure_emits_security_violation() {
1945        let secret1 = [0x42u8; 32];
1946        let secret2 = [0x43u8; 32]; // Different key
1947        let mesh1 = create_encrypted_mesh(0x11111111, "ALPHA-1", secret1);
1948        let mesh2 = create_encrypted_mesh(0x22222222, "BRAVO-1", secret2);
1949
1950        let observer = Arc::new(CollectingObserver::new());
1951        mesh2.add_observer(observer.clone());
1952
1953        // mesh1 sends encrypted document
1954        let doc = mesh1.build_document();
1955
1956        // mesh2 cannot decrypt (wrong key)
1957        let result = mesh2.on_ble_data_received_from_node(NodeId::new(0x11111111), &doc, 1000);
1958        assert!(result.is_none());
1959
1960        // Should have SecurityViolation event for decryption failure
1961        let events = observer.events();
1962        assert!(events.iter().any(|e| matches!(
1963            e,
1964            HiveEvent::SecurityViolation {
1965                kind: crate::observer::SecurityViolationKind::DecryptionFailed,
1966                ..
1967            }
1968        )));
1969    }
1970
1971    #[test]
1972    fn test_strict_mode_builder_chain() {
1973        let secret = [0x42u8; 32];
1974        let config = HiveMeshConfig::new(NodeId::new(0x11111111), "ALPHA-1", "TEST")
1975            .with_encryption(secret)
1976            .with_strict_encryption()
1977            .with_sync_interval(10_000)
1978            .with_peer_timeout(60_000);
1979
1980        let mesh = HiveMesh::new(config);
1981
1982        assert!(mesh.is_encryption_enabled());
1983        assert!(mesh.is_strict_encryption_enabled());
1984    }
1985}