import 'package:hive/hive.dart'; import 'package:uuid/uuid.dart'; import '../../core/constants/app_keys.dart'; import '../models/conversation_model.dart'; import '../models/message_model.dart'; import '../models/participant_model.dart'; import '../services/chat_api_service.dart'; import '../services/notifications/mqtt_notification_service.dart'; /// Repository pour la gestion des fonctionnalités de chat /// /// Ce repository centralise toutes les opérations liées au chat, /// y compris la gestion des conversations, des messages et des participants class ChatRepository { final ChatApiService _apiService; final MqttNotificationService _mqttService; ChatRepository(this._apiService, this._mqttService); /// Liste des conversations de l'utilisateur Future> getConversations({bool forceRefresh = false}) async { try { // Récupérer depuis Hive var box = await Hive.openBox(AppKeys.chatConversationsBoxName); var localConversations = box.values.toList(); // Si on force le rafraîchissement ou qu'on n'a pas de données locales if (forceRefresh || localConversations.isEmpty) { try { // Récupérer depuis l'API var apiConversations = await _apiService.getConversations(); // Mettre à jour Hive await box.clear(); for (var conversation in apiConversations) { await box.put(conversation.id, conversation); } return apiConversations; } catch (e) { // Si l'API échoue, utiliser les données locales if (localConversations.isNotEmpty) { return localConversations; } rethrow; } } return localConversations; } catch (e) { throw Exception('Erreur lors de la récupération des conversations: $e'); } } /// Récupère une conversation spécifique Future getConversation(String id) async { try { // Vérifier d'abord dans Hive var box = await Hive.openBox(AppKeys.chatConversationsBoxName); var localConversation = box.get(id); if (localConversation != null) { return localConversation; } // Sinon récupérer depuis l'API var apiConversation = await _apiService.getConversation(id); await box.put(id, apiConversation); return apiConversation; } catch (e) { throw Exception('Erreur lors de la récupération de la conversation: $e'); } } /// Crée une nouvelle conversation Future createConversation(Map data) async { try { // Créer via l'API var conversation = await _apiService.createConversation(data); // Sauvegarder dans Hive var box = await Hive.openBox(AppKeys.chatConversationsBoxName); await box.put(conversation.id, conversation); // S'abonner aux notifications de la conversation await _mqttService.subscribeToConversation(conversation.id); return conversation; } catch (e) { throw Exception('Erreur lors de la création de la conversation: $e'); } } /// Supprime une conversation Future deleteConversation(String id) async { try { // Supprimer via l'API await _apiService.deleteConversation(id); // Supprimer de Hive var box = await Hive.openBox(AppKeys.chatConversationsBoxName); await box.delete(id); // Se désabonner des notifications await _mqttService.unsubscribeFromConversation(id); } catch (e) { throw Exception('Erreur lors de la suppression de la conversation: $e'); } } /// Épingle/désépingle une conversation Future pinConversation(String id, bool isPinned) async { try { await _apiService.pinConversation(id, isPinned); // Mettre à jour dans Hive var box = await Hive.openBox(AppKeys.chatConversationsBoxName); var conversation = box.get(id); if (conversation != null) { await box.put(id, conversation.copyWith(isPinned: isPinned)); } } catch (e) { throw Exception('Erreur lors de l\'épinglage de la conversation: $e'); } } /// Met à jour les permissions de réponse Future updateReplyPermission(String id, String replyPermission) async { try { await _apiService.updateReplyPermission(id, replyPermission); // Mettre à jour dans Hive var box = await Hive.openBox(AppKeys.chatConversationsBoxName); var conversation = box.get(id); if (conversation != null) { await box.put(id, conversation.copyWith(replyPermission: replyPermission)); } } catch (e) { throw Exception('Erreur lors de la mise à jour des permissions: $e'); } } /// Récupère les messages d'une conversation Future> getMessages(String conversationId, {int page = 1, int limit = 50}) async { try { // Récupérer depuis Hive var box = await Hive.openBox(AppKeys.chatMessagesBoxName); var localMessages = box.values .where((m) => m.conversationId == conversationId) .toList() ..sort((a, b) => b.createdAt.compareTo(a.createdAt)); // Si on a assez de messages localement if (localMessages.length >= page * limit) { return localMessages.skip((page - 1) * limit).take(limit).toList(); } try { // Récupérer depuis l'API var apiMessages = await _apiService.getMessages(conversationId, page: page, limit: limit); // Mettre à jour Hive for (var message in apiMessages) { await box.put(message.id, message); } return apiMessages; } catch (e) { // Si l'API échoue, utiliser les données locales if (localMessages.isNotEmpty) { return localMessages.skip((page - 1) * limit).take(limit).toList(); } rethrow; } } catch (e) { throw Exception('Erreur lors de la récupération des messages: $e'); } } /// Envoie un message via MQTT Future sendMessage(String conversationId, Map messageData) async { try { // Générer un ID unique pour le message var messageId = const Uuid().v4(); var userId = messageData['senderId'] as String?; // Créer le message var message = MessageModel( id: messageId, conversationId: conversationId, senderId: userId, senderType: 'user', content: messageData['content'] as String, contentType: messageData['contentType'] as String? ?? 'text', createdAt: DateTime.now(), status: 'sent', isAnnouncement: messageData['isAnnouncement'] as bool? ?? false, ); // Sauvegarder temporairement dans Hive var box = await Hive.openBox(AppKeys.chatMessagesBoxName); await box.put(messageId, message); // Publier via MQTT await _mqttService.publishMessage('chat/message/send', { 'messageId': messageId, 'conversationId': conversationId, 'senderId': userId, 'content': message.content, 'contentType': message.contentType, 'timestamp': message.createdAt.toIso8601String(), 'isAnnouncement': message.isAnnouncement, }); } catch (e) { throw Exception('Erreur lors de l\'envoi du message: $e'); } } /// Marque un message comme lu Future markMessageAsRead(String messageId) async { try { // Mettre à jour via l'API await _apiService.markMessageAsRead(messageId); // Mettre à jour dans Hive var box = await Hive.openBox(AppKeys.chatMessagesBoxName); var message = box.get(messageId); if (message != null) { await box.put(messageId, message.copyWith( status: 'read', readAt: DateTime.now(), )); } } catch (e) { throw Exception('Erreur lors du marquage comme lu: $e'); } } /// Ajoute un participant à une conversation Future addParticipant(String conversationId, Map participantData) async { try { await _apiService.addParticipant(conversationId, participantData); // Mettre à jour la conversation dans Hive var conversationBox = await Hive.openBox(AppKeys.chatConversationsBoxName); var conversation = conversationBox.get(conversationId); if (conversation != null) { var updatedParticipants = List.from(conversation.participants); updatedParticipants.add(ParticipantModel.fromJson(participantData)); await conversationBox.put(conversationId, conversation.copyWith(participants: updatedParticipants)); } } catch (e) { throw Exception('Erreur lors de l\'ajout du participant: $e'); } } /// Retire un participant d'une conversation Future removeParticipant(String conversationId, String participantId) async { try { await _apiService.removeParticipant(conversationId, participantId); // Mettre à jour la conversation dans Hive var conversationBox = await Hive.openBox(AppKeys.chatConversationsBoxName); var conversation = conversationBox.get(conversationId); if (conversation != null) { var updatedParticipants = List.from(conversation.participants); updatedParticipants.removeWhere((p) => p.id == participantId); await conversationBox.put(conversationId, conversation.copyWith(participants: updatedParticipants)); } } catch (e) { throw Exception('Erreur lors du retrait du participant: $e'); } } /// Crée un utilisateur anonyme (pour Resalice) Future createAnonymousUser({String? name, String? email}) async { try { return await _apiService.createAnonymousUser(name: name, email: email); } catch (e) { throw Exception('Erreur lors de la création de l\'utilisateur anonyme: $e'); } } /// Convertit un utilisateur anonyme en utilisateur authentifié Future convertAnonymousToUser(String anonymousId, String userId) async { try { // Mettre à jour tous les messages de l'utilisateur anonyme var messageBox = await Hive.openBox(AppKeys.chatMessagesBoxName); var messages = messageBox.values.where((m) => m.senderId == anonymousId).toList(); for (var message in messages) { await messageBox.put(message.id, message.copyWith( senderId: userId, senderType: 'user', )); } } catch (e) { throw Exception('Erreur lors de la conversion de l\'utilisateur: $e'); } } /// Récupère les annonces Future> getAnnouncements({bool forceRefresh = false}) async { try { // Filtrer les conversations pour n'avoir que les annonces var conversations = await getConversations(forceRefresh: forceRefresh); return conversations.where((c) => c.type == 'announcement').toList(); } catch (e) { throw Exception('Erreur lors de la récupération des annonces: $e'); } } /// Crée une nouvelle annonce Future createAnnouncement(Map data) async { try { // Créer la conversation comme une annonce data['type'] = 'announcement'; return await createConversation(data); } catch (e) { throw Exception('Erreur lors de la création de l\'annonce: $e'); } } /// Récupère les statistiques d'une annonce Future> getAnnouncementStats(String conversationId) async { try { return await _apiService.getAnnouncementStats(conversationId); } catch (e) { throw Exception('Erreur lors de la récupération des statistiques: $e'); } } /// Récupère les cibles d'audience disponibles Future>> getAvailableAudienceTargets() async { try { return await _apiService.getAvailableAudienceTargets(); } catch (e) { throw Exception('Erreur lors de la récupération des cibles: $e'); } } /// Ajoute une cible d'audience Future addAudienceTarget(String conversationId, Map targetData) async { try { // L'ajout des cibles d'audience est géré lors de la création de l'annonce // Mais on pourrait avoir besoin de modifier les cibles plus tard throw UnimplementedError('Ajout de cible non encore implémenté'); } catch (e) { throw Exception('Erreur lors de l\'ajout de cible: $e'); } } /// Retire une cible d'audience Future removeAudienceTarget(String conversationId, String targetId) async { try { // Le retrait des cibles d'audience est géré lors de la création de l'annonce throw UnimplementedError('Retrait de cible non encore implémenté'); } catch (e) { throw Exception('Erreur lors du retrait de cible: $e'); } } }