/// Datasource distant pour la messagerie v4 — /api/messagerie/* library messaging_remote_datasource; import 'dart:convert'; import 'package:injectable/injectable.dart'; import '../../../../core/config/environment.dart'; import '../../../../core/error/exceptions.dart'; import '../../../authentication/data/datasources/keycloak_auth_service.dart'; import '../models/conversation_model.dart'; import '../models/message_model.dart'; import '../models/contact_policy_model.dart'; import 'package:http/http.dart' as http; @lazySingleton class MessagingRemoteDatasource { final http.Client client; final KeycloakAuthService authService; MessagingRemoteDatasource({ required this.client, required this.authService, }); /// Headers HTTP avec authentification — rafraîchit le token si expiré Future> _getHeaders() async { final token = await authService.getValidAccessToken(); return { 'Content-Type': 'application/json', 'Accept': 'application/json', if (token != null) 'Authorization': 'Bearer $token', }; } String get _base => '${AppConfig.apiBaseUrl}/api/messagerie'; // ── Conversations ───────────────────────────────────────────────────────── Future> getMesConversations() async { final response = await client.get( Uri.parse('$_base/conversations'), headers: await _getHeaders(), ); _checkAuth(response); if (response.statusCode == 200) { final list = json.decode(response.body) as List; return list .map((j) => ConversationSummaryModel.fromJson(j as Map)) .toList(); } throw ServerException('Erreur récupération conversations (${response.statusCode})'); } Future getConversation(String id) async { final response = await client.get( Uri.parse('$_base/conversations/$id'), headers: await _getHeaders(), ); _checkAuth(response); if (response.statusCode == 200) { return ConversationModel.fromJson(json.decode(response.body) as Map); } if (response.statusCode == 404) throw NotFoundException('Conversation non trouvée'); throw ServerException('Erreur récupération conversation (${response.statusCode})'); } Future demarrerConversationDirecte({ required String destinataireId, required String organisationId, String? premierMessage, }) async { final body = json.encode({ 'destinataireId': destinataireId, 'organisationId': organisationId, if (premierMessage != null) 'premierMessage': premierMessage, }); final response = await client.post( Uri.parse('$_base/conversations/directe'), headers: await _getHeaders(), body: body, ); _checkAuth(response); if (response.statusCode == 201 || response.statusCode == 200) { return ConversationModel.fromJson(json.decode(response.body) as Map); } throw ServerException('Erreur démarrage conversation directe (${response.statusCode})'); } Future demarrerConversationRole({ required String roleCible, required String organisationId, String? premierMessage, }) async { final body = json.encode({ 'roleCible': roleCible, 'organisationId': organisationId, if (premierMessage != null) 'premierMessage': premierMessage, }); final response = await client.post( Uri.parse('$_base/conversations/role'), headers: await _getHeaders(), body: body, ); _checkAuth(response); if (response.statusCode == 201 || response.statusCode == 200) { return ConversationModel.fromJson(json.decode(response.body) as Map); } throw ServerException('Erreur démarrage conversation rôle (${response.statusCode})'); } Future archiverConversation(String id) async { final response = await client.delete( Uri.parse('$_base/conversations/$id'), headers: await _getHeaders(), ); _checkAuth(response); if (response.statusCode != 200 && response.statusCode != 204) { throw ServerException('Erreur archivage conversation (${response.statusCode})'); } } // ── Messages ────────────────────────────────────────────────────────────── Future envoyerMessage( String conversationId, { required String typeMessage, String? contenu, String? urlFichier, int? dureeAudio, String? messageParentId, }) async { final body = json.encode({ 'typeMessage': typeMessage, if (contenu != null) 'contenu': contenu, if (urlFichier != null) 'urlFichier': urlFichier, if (dureeAudio != null) 'dureeAudio': dureeAudio, if (messageParentId != null) 'messageParentId': messageParentId, }); final response = await client.post( Uri.parse('$_base/conversations/$conversationId/messages'), headers: await _getHeaders(), body: body, ); _checkAuth(response); if (response.statusCode == 201 || response.statusCode == 200) { return MessageModel.fromJson(json.decode(response.body) as Map); } throw ServerException('Erreur envoi message (${response.statusCode})'); } Future> getMessages(String conversationId, {int page = 0}) async { final uri = Uri.parse('$_base/conversations/$conversationId/messages') .replace(queryParameters: {'page': page.toString()}); final response = await client.get(uri, headers: await _getHeaders()); _checkAuth(response); if (response.statusCode == 200) { final list = json.decode(response.body) as List; return list .map((j) => MessageModel.fromJson(j as Map)) .toList(); } throw ServerException('Erreur récupération messages (${response.statusCode})'); } Future marquerLu(String conversationId) async { final response = await client.put( Uri.parse('$_base/conversations/$conversationId/lire'), headers: await _getHeaders(), ); _checkAuth(response); if (response.statusCode != 200 && response.statusCode != 204) { throw ServerException('Erreur marquage lu (${response.statusCode})'); } } Future supprimerMessage(String conversationId, String messageId) async { final response = await client.delete( Uri.parse('$_base/conversations/$conversationId/messages/$messageId'), headers: await _getHeaders(), ); _checkAuth(response); if (response.statusCode != 200 && response.statusCode != 204) { throw ServerException('Erreur suppression message (${response.statusCode})'); } } // ── Blocages ────────────────────────────────────────────────────────────── Future bloquerMembre({ required String membreABloquerId, String? organisationId, String? raison, }) async { final body = json.encode({ 'membreABloquerId': membreABloquerId, if (organisationId != null) 'organisationId': organisationId, if (raison != null) 'raison': raison, }); final response = await client.post( Uri.parse('$_base/blocages'), headers: await _getHeaders(), body: body, ); _checkAuth(response); if (response.statusCode != 200 && response.statusCode != 204) { throw ServerException('Erreur blocage membre (${response.statusCode})'); } } Future debloquerMembre(String membreId, {String? organisationId}) async { final uri = Uri.parse('$_base/blocages/$membreId').replace( queryParameters: { if (organisationId != null) 'organisationId': organisationId, }, ); final response = await client.delete(uri, headers: await _getHeaders()); _checkAuth(response); if (response.statusCode != 200 && response.statusCode != 204) { throw ServerException('Erreur déblocage membre (${response.statusCode})'); } } Future>> getMesBlocages() async { final response = await client.get( Uri.parse('$_base/blocages'), headers: await _getHeaders(), ); _checkAuth(response); if (response.statusCode == 200) { final list = json.decode(response.body) as List; return list.map((j) => j as Map).toList(); } throw ServerException('Erreur récupération blocages (${response.statusCode})'); } // ── Politique de communication ──────────────────────────────────────────── Future getPolitique(String organisationId) async { final response = await client.get( Uri.parse('$_base/politique/$organisationId'), headers: await _getHeaders(), ); _checkAuth(response); if (response.statusCode == 200) { return ContactPolicyModel.fromJson(json.decode(response.body) as Map); } throw ServerException('Erreur récupération politique (${response.statusCode})'); } Future mettreAJourPolitique( String organisationId, { required String typePolitique, required bool autoriserMembreVersMembre, required bool autoriserMembreVersRole, required bool autoriserNotesVocales, }) async { final body = json.encode({ 'typePolitique': typePolitique, 'autoriserMembreVersMembre': autoriserMembreVersMembre, 'autoriserMembreVersRole': autoriserMembreVersRole, 'autoriserNotesVocales': autoriserNotesVocales, }); final response = await client.put( Uri.parse('$_base/politique/$organisationId'), headers: await _getHeaders(), body: body, ); _checkAuth(response); if (response.statusCode == 200) { return ContactPolicyModel.fromJson(json.decode(response.body) as Map); } throw ServerException('Erreur mise à jour politique (${response.statusCode})'); } // ── Helpers ──────────────────────────────────────────────────────────────── void _checkAuth(http.Response response) { if (response.statusCode == 401) throw UnauthorizedException(); if (response.statusCode == 403) throw ForbiddenException('Permission insuffisante'); } }