import 'dart:convert'; import 'dart:io'; class ChatEnvelope { ChatEnvelope({required this.messageId, required this.body, required this.createdAt}); final String messageId; final String body; final DateTime createdAt; Map toJson() => { 'messageId': messageId, 'body': body, 'createdAt': createdAt.millisecondsSinceEpoch, }; factory ChatEnvelope.fromJson(Map json) { return ChatEnvelope( messageId: json['messageId'] as String, body: json['body'] as String, createdAt: DateTime.fromMillisecondsSinceEpoch(json['createdAt'] as int, isUtc: true), ); } } class MothershipChatStore { MothershipChatStore(this.rootDir); final Directory rootDir; Future init() async { if (!await rootDir.exists()) { await rootDir.create(recursive: true); } } Future appendInbound(String clientId, List messages) async { if (messages.isEmpty) return; final file = await _logFile(clientId); final sink = file.openWrite(mode: FileMode.append); for (final message in messages) { sink.writeln(jsonEncode(message.toJson())); } await sink.flush(); await sink.close(); } Future> pendingOutbound(String clientId) async { final file = await _outboxFile(clientId); if (!await file.exists()) return []; try { final raw = await file.readAsString(); if (raw.trim().isEmpty) return []; final decoded = jsonDecode(raw) as List; return decoded.map((e) => ChatEnvelope.fromJson(Map.from(e as Map))).toList(); } catch (_) { return []; } } Future enqueueOutbound(String clientId, List messages) async { if (messages.isEmpty) return; final current = await pendingOutbound(clientId); final combined = [...current, ...messages]; final file = await _outboxFile(clientId); await file.writeAsString(jsonEncode(combined.map((e) => e.toJson()).toList())); } Future acknowledge(String clientId, List messageIds) async { if (messageIds.isEmpty) return; final file = await _outboxFile(clientId); if (!await file.exists()) return; final current = await pendingOutbound(clientId); final filtered = current.where((m) => !messageIds.contains(m.messageId)).toList(); if (filtered.isEmpty) { await file.delete(); } else { await file.writeAsString(jsonEncode(filtered.map((e) => e.toJson()).toList())); } } Future _logFile(String clientId) async { final dir = Directory('${rootDir.path}/$clientId'); if (!await dir.exists()) { await dir.create(recursive: true); } return File('${dir.path}/log.jsonl'); } Future _outboxFile(String clientId) async { final dir = Directory('${rootDir.path}/$clientId'); if (!await dir.exists()) { await dir.create(recursive: true); } return File('${dir.path}/outbox.json'); } }