1use crate::crdt::*;
17use crate::error::AppResult;
18use std::collections::HashMap;
19use std::sync::Arc;
20use tokio::sync::RwLock;
21use tracing::{debug, info, warn};
22
23#[derive(Debug)]
25pub struct MessageSyncService {
26 peer_id: String,
28
29 entity_clocks: Arc<RwLock<HashMap<String, VectorClock>>>,
31
32 entity_messages: Arc<RwLock<HashMap<String, Vec<CRDTMessage>>>>,
34
35 pending_messages: Arc<RwLock<HashMap<String, Vec<CRDTMessage>>>>,
37
38 lamport_clock: Arc<RwLock<u64>>,
40}
41
42impl MessageSyncService {
43 pub fn new(peer_id: String) -> Self {
45 info!("🔄 MessageSyncService initialized for peer: {}", peer_id);
46
47 Self {
48 peer_id,
49 entity_clocks: Arc::new(RwLock::new(HashMap::new())),
50 entity_messages: Arc::new(RwLock::new(HashMap::new())),
51 pending_messages: Arc::new(RwLock::new(HashMap::new())),
52 lamport_clock: Arc::new(RwLock::new(0)),
53 }
54 }
55
56 pub async fn get_all_messages(&self, entity_id: &str) -> AppResult<SyncResponse> {
59 let messages_map = self.entity_messages.read().await;
60 let clocks_map = self.entity_clocks.read().await;
61
62 let mut messages = messages_map.get(entity_id).cloned().unwrap_or_default();
63
64 let vector_clock = clocks_map.get(entity_id).cloned().unwrap_or_default();
65
66 sort_messages_causally(&mut messages);
68
69 info!(
70 "📤 get_all_messages for {}: {} messages",
71 entity_id,
72 messages.len()
73 );
74
75 Ok(SyncResponse {
76 entity_id: entity_id.to_string(),
77 entity_type: self.infer_entity_type(entity_id),
78 messages,
79 vector_clock,
80 })
81 }
82
83 pub async fn receive_message(&self, message: CRDTMessage) -> AppResult<ReceiveResult> {
85 let entity_id = message.metadata.entity_id.clone();
86 let clocks_map = self.entity_clocks.read().await;
87
88 let local_clock = clocks_map.get(&entity_id).cloned().unwrap_or_default();
89
90 drop(clocks_map);
92
93 let has_deps = local_clock.has_dependencies(&message.metadata.vector_clock);
95
96 if !has_deps {
97 warn!("⚠️ Out-of-order message detected: {}", message.metadata.id);
99
100 let mut pending_map = self.pending_messages.write().await;
101 let pending = pending_map.entry(entity_id.clone()).or_default();
102 pending.push(message.clone());
103
104 let missing = local_clock.get_missing_ranges(&message.metadata.vector_clock);
106
107 return Ok(ReceiveResult {
108 accepted: false,
109 out_of_order: true,
110 missing_ranges: Some(missing),
111 });
112 }
113
114 self.add_message(message).await?;
116
117 self.process_pending_messages(&entity_id).await?;
119
120 Ok(ReceiveResult {
121 accepted: true,
122 out_of_order: false,
123 missing_ranges: None,
124 })
125 }
126
127 pub async fn send_message(
129 &self,
130 entity_id: String,
131 entity_type: EntityType,
132 content: MessageContent,
133 reply_to_id: Option<String>,
134 ) -> AppResult<CRDTMessage> {
135 let mut clocks_map = self.entity_clocks.write().await;
137 let clock = clocks_map.entry(entity_id.clone()).or_default();
138 clock.increment(&self.peer_id);
139 let new_clock = clock.clone();
140 drop(clocks_map);
141
142 let mut lamport = self.lamport_clock.write().await;
144 *lamport += 1;
145 let lamport_value = *lamport;
146 drop(lamport);
147
148 let messages_map = self.entity_messages.read().await;
150 let previous_id = messages_map
151 .get(&entity_id)
152 .and_then(|msgs| msgs.last())
153 .map(|msg| msg.metadata.id.clone());
154 drop(messages_map);
155
156 let metadata = MessageMetadata {
157 id: format!(
158 "{}-{}-{}",
159 self.peer_id,
160 new_clock.0.get(&self.peer_id).copied().unwrap_or(0),
161 chrono::Utc::now().timestamp_millis()
162 ),
163 entity_id: entity_id.clone(),
164 entity_type,
165 author_peer_id: self.peer_id.clone(),
166 vector_clock: new_clock,
167 lamport_clock: lamport_value,
168 timestamp: chrono::Utc::now().timestamp_millis() as u64,
169 previous_message_id: previous_id,
170 reply_to_id,
171 };
172
173 let message = CRDTMessage {
174 content,
175 metadata,
176 local_state: Some(LocalMessageState {
177 status: Some(MessageStatus::Sent),
178 reactions: Vec::new(),
179 thread_count: None,
180 latest_reply_by: None,
181 }),
182 };
183
184 self.add_message(message.clone()).await?;
185
186 Ok(message)
187 }
188
189 pub async fn request_sync(
191 &self,
192 entity_id: &str,
193 from_peer_id: &str,
194 ) -> AppResult<SyncRequest> {
195 let clocks_map = self.entity_clocks.read().await;
196 let local_clock = clocks_map.get(entity_id).cloned().unwrap_or_default();
197 drop(clocks_map);
198
199 let pending_map = self.pending_messages.read().await;
201 let missing_ids = pending_map
202 .get(entity_id)
203 .map(|pending| pending.iter().map(|m| m.metadata.id.clone()).collect());
204 drop(pending_map);
205
206 debug!("🔄 Requesting sync for {} from {}", entity_id, from_peer_id);
207 debug!(" Local clock: {:?}", local_clock);
208 debug!(" Missing messages: {:?}", missing_ids);
209
210 Ok(SyncRequest {
211 entity_id: entity_id.to_string(),
212 entity_type: self.infer_entity_type(entity_id),
213 requester_peer_id: self.peer_id.clone(),
214 vector_clock: local_clock,
215 missing_message_ids: missing_ids,
216 })
217 }
218
219 pub async fn handle_sync_response(&self, response: SyncResponse) -> AppResult<SyncResult> {
221 let entity_id = &response.entity_id;
222 let mut added = 0;
223 let mut rejected = 0;
224
225 info!(
226 "📥 Handling sync response for {}: {} messages",
227 entity_id,
228 response.messages.len()
229 );
230
231 for message in response.messages {
233 let result = self.receive_message(message).await?;
234 if result.accepted {
235 added += 1;
236 } else {
237 rejected += 1;
238 }
239 }
240
241 let mut clocks_map = self.entity_clocks.write().await;
243 let local_clock = clocks_map.entry(entity_id.clone()).or_default();
244 local_clock.merge(&response.vector_clock);
245 let merged_clock = local_clock.clone();
246 drop(clocks_map);
247
248 info!("✅ Sync complete: {} added, {} rejected", added, rejected);
249 debug!(" Updated clock: {:?}", merged_clock);
250
251 Ok(SyncResult {
252 messages_added: added,
253 messages_rejected: rejected,
254 })
255 }
256
257 pub async fn get_sync_state(&self, entity_id: &str) -> AppResult<EntitySyncState> {
259 let messages_map = self.entity_messages.read().await;
260 let pending_map = self.pending_messages.read().await;
261 let clocks_map = self.entity_clocks.read().await;
262
263 let messages = messages_map.get(entity_id).cloned().unwrap_or_default();
264 let pending = pending_map.get(entity_id).cloned().unwrap_or_default();
265 let clock = clocks_map.get(entity_id).cloned().unwrap_or_default();
266
267 Ok(EntitySyncState {
268 entity_id: entity_id.to_string(),
269 entity_type: self.infer_entity_type(entity_id),
270 vector_clock: clock,
271 last_sync_time: chrono::Utc::now().timestamp_millis() as u64,
272 message_count: messages.len(),
273 missing_messages: pending.iter().map(|m| m.metadata.id.clone()).collect(),
274 out_of_order_messages: pending.iter().map(|m| m.metadata.id.clone()).collect(),
275 })
276 }
277
278 pub async fn get_messages(&self, entity_id: &str) -> AppResult<Vec<CRDTMessage>> {
280 let messages_map = self.entity_messages.read().await;
281 let mut messages = messages_map.get(entity_id).cloned().unwrap_or_default();
282
283 sort_messages_causally(&mut messages);
284
285 Ok(messages)
286 }
287
288 pub async fn needs_sync(&self, entity_id: &str, remote_clock: &VectorClock) -> bool {
290 let clocks_map = self.entity_clocks.read().await;
291 let local_clock = clocks_map.get(entity_id).cloned().unwrap_or_default();
292
293 let missing = local_clock.get_missing_ranges(remote_clock);
294 !missing.is_empty()
295 }
296
297 pub async fn delete_message(&self, entity_id: &str, message_id: &str) -> AppResult<bool> {
298 let mut messages_map = self.entity_messages.write().await;
299 if let Some(messages) = messages_map.get_mut(entity_id) {
300 let original_len = messages.len();
301 messages.retain(|m| m.metadata.id != message_id);
302 let deleted = messages.len() < original_len;
303 if deleted {
304 info!("🗑️ Message deleted: {} (entity: {})", message_id, entity_id);
305 }
306 return Ok(deleted);
307 }
308 Ok(false)
309 }
310
311 pub async fn edit_message(
312 &self,
313 entity_id: &str,
314 message_id: &str,
315 new_text: String,
316 ) -> AppResult<u64> {
317 let mut messages_map = self.entity_messages.write().await;
318 if let Some(messages) = messages_map.get_mut(entity_id) {
319 for message in messages.iter_mut() {
320 if message.metadata.id == message_id {
321 message.content.text = new_text;
322 let edited_at = chrono::Utc::now().timestamp_millis() as u64;
323 info!("✏️ Message edited: {} (entity: {})", message_id, entity_id);
324 return Ok(edited_at);
325 }
326 }
327 }
328 Err(crate::error::AppError::NotFound(format!(
329 "Message not found: {}",
330 message_id
331 )))
332 }
333
334 pub async fn add_reaction(
335 &self,
336 entity_id: &str,
337 message_id: &str,
338 emoji: String,
339 peer_id: String,
340 ) -> AppResult<()> {
341 let mut messages_map = self.entity_messages.write().await;
342 if let Some(messages) = messages_map.get_mut(entity_id) {
343 for message in messages.iter_mut() {
344 if message.metadata.id == message_id {
345 let local_state =
346 message
347 .local_state
348 .get_or_insert_with(|| LocalMessageState {
349 status: None,
350 reactions: Vec::new(),
351 thread_count: None,
352 latest_reply_by: None,
353 });
354
355 if let Some(reaction) =
356 local_state.reactions.iter_mut().find(|r| r.emoji == emoji)
357 {
358 if !reaction.peer_ids.contains(&peer_id) {
359 reaction.peer_ids.push(peer_id.clone());
360 reaction.count += 1;
361 }
362 } else {
363 local_state.reactions.push(Reaction {
364 emoji: emoji.clone(),
365 count: 1,
366 user_reacted: Some(true),
367 peer_ids: vec![peer_id.clone()],
368 });
369 }
370
371 info!(
372 "👍 Reaction added: {} to {} (entity: {})",
373 emoji, message_id, entity_id
374 );
375 return Ok(());
376 }
377 }
378 }
379 Err(crate::error::AppError::NotFound(format!(
380 "Message not found: {}",
381 message_id
382 )))
383 }
384
385 pub async fn remove_reaction(
386 &self,
387 entity_id: &str,
388 message_id: &str,
389 emoji: String,
390 peer_id: String,
391 ) -> AppResult<()> {
392 let mut messages_map = self.entity_messages.write().await;
393 if let Some(messages) = messages_map.get_mut(entity_id) {
394 for message in messages.iter_mut() {
395 if message.metadata.id == message_id {
396 if let Some(ref mut local_state) = message.local_state
397 && let Some(reaction) =
398 local_state.reactions.iter_mut().find(|r| r.emoji == emoji)
399 {
400 reaction.peer_ids.retain(|p| p != &peer_id);
401 reaction.count = reaction.count.saturating_sub(1);
402
403 if reaction.count == 0 {
404 local_state.reactions.retain(|r| r.emoji != emoji);
405 }
406
407 info!(
408 "👎 Reaction removed: {} from {} (entity: {})",
409 emoji, message_id, entity_id
410 );
411 return Ok(());
412 }
413 return Err(crate::error::AppError::NotFound(format!(
414 "Reaction not found: {} on {}",
415 emoji, message_id
416 )));
417 }
418 }
419 }
420 Err(crate::error::AppError::NotFound(format!(
421 "Message not found: {}",
422 message_id
423 )))
424 }
425
426 pub async fn get_reactions(
427 &self,
428 entity_id: &str,
429 message_id: &str,
430 ) -> AppResult<Vec<Reaction>> {
431 let messages_map = self.entity_messages.read().await;
432 if let Some(messages) = messages_map.get(entity_id) {
433 for message in messages.iter() {
434 if message.metadata.id == message_id {
435 return Ok(message
436 .local_state
437 .as_ref()
438 .map(|ls| ls.reactions.clone())
439 .unwrap_or_default());
440 }
441 }
442 }
443 Err(crate::error::AppError::NotFound(format!(
444 "Message not found: {}",
445 message_id
446 )))
447 }
448
449 async fn add_message(&self, message: CRDTMessage) -> AppResult<()> {
452 let entity_id = &message.metadata.entity_id;
453
454 let mut messages_map = self.entity_messages.write().await;
455 let messages = messages_map.entry(entity_id.clone()).or_default();
456
457 if messages
459 .iter()
460 .any(|m| m.metadata.id == message.metadata.id)
461 {
462 warn!("⚠️ Duplicate message ignored: {}", message.metadata.id);
463 return Ok(());
464 }
465
466 messages.push(message.clone());
467 drop(messages_map);
468
469 let mut clocks_map = self.entity_clocks.write().await;
471 let local_clock = clocks_map.entry(entity_id.clone()).or_default();
472 local_clock.merge(&message.metadata.vector_clock);
473 drop(clocks_map);
474
475 let mut lamport = self.lamport_clock.write().await;
477 *lamport = (*lamport).max(message.metadata.lamport_clock) + 1;
478 drop(lamport);
479
480 info!(
481 "📨 Message added: {} (entity: {})",
482 message.metadata.id, entity_id
483 );
484
485 Ok(())
486 }
487
488 async fn process_pending_messages(&self, entity_id: &str) -> AppResult<()> {
489 let mut pending_map = self.pending_messages.write().await;
491 let pending_messages = match pending_map.get_mut(entity_id) {
492 Some(p) if !p.is_empty() => std::mem::take(p),
493 _ => return Ok(()),
494 };
495 drop(pending_map);
496
497 let clocks_map = self.entity_clocks.read().await;
498 let local_clock = clocks_map.get(entity_id).cloned().unwrap_or_default();
499 drop(clocks_map);
500
501 let mut still_pending = Vec::new();
502
503 for message in pending_messages {
504 if local_clock.has_dependencies(&message.metadata.vector_clock) {
505 info!(
506 "✅ Pending message now has dependencies: {}",
507 message.metadata.id
508 );
509 self.add_message(message).await?;
510 } else {
511 still_pending.push(message);
512 }
513 }
514
515 let mut pending_map = self.pending_messages.write().await;
517 if !still_pending.is_empty() {
518 info!(
519 "⏳ Still pending: {} messages for {}",
520 still_pending.len(),
521 entity_id
522 );
523 pending_map.insert(entity_id.to_string(), still_pending);
524 } else {
525 pending_map.remove(entity_id);
526 info!("✨ All pending messages processed for {}", entity_id);
527 }
528
529 Ok(())
530 }
531
532 fn infer_entity_type(&self, entity_id: &str) -> EntityType {
533 if entity_id.starts_with("contact-")
535 || entity_id.starts_with("ben-")
536 || entity_id.starts_with("lauren")
537 {
538 EntityType::Person
539 } else if entity_id.contains("-org") {
540 EntityType::Organisation
541 } else if entity_id.starts_with("project-") {
542 EntityType::Project
543 } else if entity_id.contains("general") || entity_id.contains("channel") {
544 EntityType::Channel
545 } else {
546 EntityType::Group
547 }
548 }
549}
550
551#[derive(Debug)]
553pub struct ReceiveResult {
554 pub accepted: bool,
555 pub out_of_order: bool,
556 pub missing_ranges: Option<Vec<MissingRange>>,
557}
558
559#[derive(Debug)]
561pub struct SyncResult {
562 pub messages_added: usize,
563 pub messages_rejected: usize,
564}