import 'package:dartz/dartz.dart'; import '../../core/errors/exceptions.dart'; import '../../core/errors/failures.dart'; import '../../core/utils/app_logger.dart'; import '../../domain/entities/chat_message.dart'; import '../../domain/entities/conversation.dart'; import '../../domain/repositories/chat_repository.dart'; import '../datasources/chat_remote_data_source.dart'; /// Implémentation du repository de chat. /// /// Cette classe fait le lien entre la couche domaine et la couche données, /// en convertissant les exceptions en failures et en gérant la transformation /// entre les modèles de données et les entités de domaine. /// /// **Usage:** /// ```dart /// final repository = ChatRepositoryImpl( /// remoteDataSource: chatRemoteDataSource, /// ); /// final result = await repository.getConversations('userId'); /// result.fold( /// (failure) => print('Erreur: $failure'), /// (conversations) => print('${conversations.length} conversations'), /// ); /// ``` class ChatRepositoryImpl implements ChatRepository { /// Crée une nouvelle instance de [ChatRepositoryImpl]. /// /// [remoteDataSource] La source de données distante pour le chat ChatRepositoryImpl({required this.remoteDataSource}); /// Source de données distante pour les opérations de chat final ChatRemoteDataSource remoteDataSource; /// Log un message si le mode debug est activé. void _log(String message) { AppLogger.d(message, tag: 'ChatRepositoryImpl'); } @override Future>> getConversations( String userId, ) async { _log('Récupération des conversations pour $userId'); try { if (userId.isEmpty) { return const Left(ValidationFailure(message: 'L\'ID utilisateur ne peut pas être vide', field: 'userId', )); } final conversationModels = await remoteDataSource.getConversations(userId); final conversations = conversationModels.map((model) => model.toEntity()).toList(); _log('${conversations.length} conversations récupérées'); return Right(conversations); } on ServerException catch (e) { _log('Erreur serveur: ${e.message}'); return Left(ServerFailure( message: e.message, statusCode: e.statusCode, )); } on UnauthorizedException catch (e) { _log('Non autorisé: ${e.message}'); return Left(AuthenticationFailure( message: e.message, code: 'UNAUTHORIZED', )); } catch (e) { _log('Erreur inattendue: $e'); return Left(ServerFailure( message: 'Erreur lors de la récupération des conversations: $e', )); } } @override Future> getOrCreateConversation( String userId, String participantId, ) async { _log('Récupération/création conversation entre $userId et $participantId'); try { if (userId.isEmpty || participantId.isEmpty) { return const Left(ValidationFailure(message: 'Les IDs utilisateur et participant sont requis', )); } if (userId == participantId) { return const Left(ValidationFailure(message: 'Impossible de créer une conversation avec soi-même', )); } final conversationModel = await remoteDataSource.getOrCreateConversation( userId, participantId, ); _log('Conversation récupérée/créée avec succès'); return Right(conversationModel.toEntity()); } on ServerException catch (e) { _log('Erreur serveur: ${e.message}'); return Left(ServerFailure( message: e.message, statusCode: e.statusCode, )); } on UnauthorizedException catch (e) { _log('Non autorisé: ${e.message}'); return Left(AuthenticationFailure( message: e.message, code: 'UNAUTHORIZED', )); } catch (e) { _log('Erreur inattendue: $e'); return Left(ServerFailure( message: 'Erreur lors de la récupération/création de la conversation: $e', )); } } @override Future>> getMessages( String conversationId, { int page = 0, int size = 50, }) async { _log('Récupération des messages de $conversationId (page: $page)'); try { if (conversationId.isEmpty) { return const Left(ValidationFailure(message: 'L\'ID de conversation ne peut pas être vide', field: 'conversationId', )); } final messageModels = await remoteDataSource.getMessages( conversationId, page: page, size: size, ); final messages = messageModels.map((model) => model.toEntity()).toList(); _log('${messages.length} messages récupérés'); return Right(messages); } on ServerException catch (e) { _log('Erreur serveur: ${e.message}'); return Left(ServerFailure( message: e.message, statusCode: e.statusCode, )); } on UnauthorizedException catch (e) { _log('Non autorisé: ${e.message}'); return Left(AuthenticationFailure( message: e.message, code: 'UNAUTHORIZED', )); } catch (e) { _log('Erreur inattendue: $e'); return Left(ServerFailure( message: 'Erreur lors de la récupération des messages: $e', )); } } @override Future> sendMessage({ required String senderId, required String recipientId, required String content, String? messageType, String? mediaUrl, }) async { _log('Envoi d\'un message de $senderId à $recipientId'); try { if (senderId.isEmpty || recipientId.isEmpty) { return const Left(ValidationFailure(message: 'Les IDs expéditeur et destinataire sont requis', )); } if (content.isEmpty) { return const Left(ValidationFailure(message: 'Le contenu du message ne peut pas être vide', field: 'content', )); } if (senderId == recipientId) { return const Left(ValidationFailure(message: 'Impossible d\'envoyer un message à soi-même', )); } final messageModel = await remoteDataSource.sendMessage( senderId: senderId, recipientId: recipientId, content: content, messageType: messageType, mediaUrl: mediaUrl, ); _log('Message envoyé avec succès'); return Right(messageModel.toEntity()); } on ServerException catch (e) { _log('Erreur serveur: ${e.message}'); return Left(ServerFailure( message: e.message, statusCode: e.statusCode, )); } on UnauthorizedException catch (e) { _log('Non autorisé: ${e.message}'); return Left(AuthenticationFailure( message: e.message, code: 'UNAUTHORIZED', )); } catch (e) { _log('Erreur inattendue: $e'); return Left(ServerFailure( message: 'Erreur lors de l\'envoi du message: $e', )); } } @override Future> markMessageAsRead(String messageId) async { _log('Marquage du message $messageId comme lu'); try { if (messageId.isEmpty) { return const Left(ValidationFailure(message: 'L\'ID du message ne peut pas être vide', field: 'messageId', )); } await remoteDataSource.markMessageAsRead(messageId); _log('Message marqué comme lu'); return const Right(null); } on ServerException catch (e) { _log('Erreur serveur: ${e.message}'); return Left(ServerFailure( message: e.message, statusCode: e.statusCode, )); } on UnauthorizedException catch (e) { _log('Non autorisé: ${e.message}'); return Left(AuthenticationFailure( message: e.message, code: 'UNAUTHORIZED', )); } catch (e) { _log('Erreur inattendue: $e'); return Left(ServerFailure( message: 'Erreur lors du marquage du message: $e', )); } } @override Future> markConversationAsRead( String conversationId, String userId, ) async { _log('Marquage de tous les messages de $conversationId comme lus'); try { if (conversationId.isEmpty || userId.isEmpty) { return const Left(ValidationFailure(message: 'Les IDs conversation et utilisateur sont requis', )); } await remoteDataSource.markConversationAsRead(conversationId, userId); _log('Conversation marquée comme lue'); return const Right(null); } on ServerException catch (e) { _log('Erreur serveur: ${e.message}'); return Left(ServerFailure( message: e.message, statusCode: e.statusCode, )); } on UnauthorizedException catch (e) { _log('Non autorisé: ${e.message}'); return Left(AuthenticationFailure( message: e.message, code: 'UNAUTHORIZED', )); } catch (e) { _log('Erreur inattendue: $e'); return Left(ServerFailure( message: 'Erreur lors du marquage de la conversation: $e', )); } } @override Future> deleteMessage(String messageId) async { _log('Suppression du message $messageId'); try { if (messageId.isEmpty) { return const Left(ValidationFailure(message: 'L\'ID du message ne peut pas être vide', field: 'messageId', )); } await remoteDataSource.deleteMessage(messageId); _log('Message supprimé'); return const Right(null); } on ServerException catch (e) { _log('Erreur serveur: ${e.message}'); return Left(ServerFailure( message: e.message, statusCode: e.statusCode, )); } on UnauthorizedException catch (e) { _log('Non autorisé: ${e.message}'); return Left(AuthenticationFailure( message: e.message, code: 'UNAUTHORIZED', )); } catch (e) { _log('Erreur inattendue: $e'); return Left(ServerFailure( message: 'Erreur lors de la suppression du message: $e', )); } } @override Future> deleteConversation(String conversationId) async { _log('Suppression de la conversation $conversationId'); try { if (conversationId.isEmpty) { return const Left(ValidationFailure(message: 'L\'ID de la conversation ne peut pas être vide', field: 'conversationId', )); } await remoteDataSource.deleteConversation(conversationId); _log('Conversation supprimée'); return const Right(null); } on ServerException catch (e) { _log('Erreur serveur: ${e.message}'); return Left(ServerFailure( message: e.message, statusCode: e.statusCode, )); } on UnauthorizedException catch (e) { _log('Non autorisé: ${e.message}'); return Left(AuthenticationFailure( message: e.message, code: 'UNAUTHORIZED', )); } catch (e) { _log('Erreur inattendue: $e'); return Left(ServerFailure( message: 'Erreur lors de la suppression de la conversation: $e', )); } } @override Future> getUnreadMessagesCount(String userId) async { _log('Récupération du nombre de messages non lus pour $userId'); try { if (userId.isEmpty) { return const Left(ValidationFailure(message: 'L\'ID utilisateur ne peut pas être vide', field: 'userId', )); } final count = await remoteDataSource.getUnreadMessagesCount(userId); _log('$count messages non lus'); return Right(count); } on ServerException catch (e) { _log('Erreur serveur: ${e.message}'); return Left(ServerFailure( message: e.message, statusCode: e.statusCode, )); } on UnauthorizedException catch (e) { _log('Non autorisé: ${e.message}'); return Left(AuthenticationFailure( message: e.message, code: 'UNAUTHORIZED', )); } catch (e) { _log('Erreur inattendue: $e'); return Left(ServerFailure( message: 'Erreur lors de la récupération du nombre de messages non lus: $e', )); } } }