/// BLoC pour la gestion des événements library evenements_bloc; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:dio/dio.dart'; import 'package:injectable/injectable.dart'; import 'evenements_event.dart'; import 'evenements_state.dart'; import '../domain/usecases/get_events.dart'; import '../domain/usecases/get_event_by_id.dart'; import '../domain/usecases/create_event.dart' as uc; import '../domain/usecases/update_event.dart' as uc; import '../domain/usecases/delete_event.dart' as uc; import '../domain/usecases/register_for_event.dart'; import '../domain/usecases/cancel_registration.dart'; import '../domain/usecases/get_event_participants.dart'; import '../domain/repositories/evenement_repository.dart'; /// BLoC pour la gestion des événements (Clean Architecture) @injectable class EvenementsBloc extends Bloc { final GetEvents _getEvents; final GetEventById _getEventById; final uc.CreateEvent _createEvent; final uc.UpdateEvent _updateEvent; final uc.DeleteEvent _deleteEvent; final RegisterForEvent _registerForEvent; final CancelRegistration _cancelRegistration; final GetEventParticipants _getEventParticipants; final IEvenementRepository _repository; // Pour méthodes non-couvertes par use cases EvenementsBloc( this._getEvents, this._getEventById, this._createEvent, this._updateEvent, this._deleteEvent, this._registerForEvent, this._cancelRegistration, this._getEventParticipants, this._repository, ) : super(const EvenementsInitial()) { on(_onLoadEvenements); on(_onLoadEvenementById); on(_onCreateEvenement); on(_onUpdateEvenement); on(_onDeleteEvenement); on(_onLoadEvenementsAVenir); on(_onLoadEvenementsEnCours); on(_onLoadEvenementsPasses); on(_onInscrireEvenement); on(_onDesinscrireEvenement); on(_onLoadParticipants); on(_onLoadEvenementsStats); } /// Charge la liste des événements Future _onLoadEvenements( LoadEvenements event, Emitter emit, ) async { try { if (event.refresh && state is EvenementsLoaded) { final currentState = state as EvenementsLoaded; emit(EvenementsRefreshing(currentState.evenements)); } else { emit(const EvenementsLoading()); } final result = await _getEvents( page: event.page, size: event.size, recherche: event.recherche, ); emit(EvenementsLoaded( evenements: result.evenements, total: result.total, page: result.page, size: result.size, totalPages: result.totalPages, )); } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors du chargement des événements. Veuillez réessayer.', error: e, )); } } /// Charge un événement par ID Future _onLoadEvenementById( LoadEvenementById event, Emitter emit, ) async { try { emit(const EvenementsLoading()); final evenement = await _getEventById(event.id); if (evenement != null) { emit(EvenementDetailLoaded(evenement)); } else { emit(const EvenementsError( message: 'Événement non trouvé', code: '404', )); } } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors du chargement de l\'événement. Veuillez réessayer.', error: e, )); } } /// Crée un nouvel événement Future _onCreateEvenement( CreateEvenement event, Emitter emit, ) async { try { emit(const EvenementsLoading()); final evenement = await _createEvent(event.evenement); emit(EvenementCreated(evenement)); } on DioException catch (e) { if (e.type == DioExceptionType.cancel) return; if (e.response?.statusCode == 400) { final errors = _extractValidationErrors(e.response?.data); emit(EvenementsValidationError( message: 'Erreur de validation', validationErrors: errors, code: '400', )); } else { _emitDioError(e, emit); } } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors de la création de l\'événement. Veuillez réessayer.', error: e, )); } } /// Met à jour un événement Future _onUpdateEvenement( UpdateEvenement event, Emitter emit, ) async { try { emit(const EvenementsLoading()); final evenement = await _updateEvent(event.id, event.evenement); emit(EvenementUpdated(evenement)); } on DioException catch (e) { if (e.type == DioExceptionType.cancel) return; if (e.response?.statusCode == 400) { final errors = _extractValidationErrors(e.response?.data); emit(EvenementsValidationError( message: 'Erreur de validation', validationErrors: errors, code: '400', )); } else { _emitDioError(e, emit); } } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors de la mise à jour de l\'événement. Veuillez réessayer.', error: e, )); } } /// Supprime un événement Future _onDeleteEvenement( DeleteEvenement event, Emitter emit, ) async { try { emit(const EvenementsLoading()); await _deleteEvent(event.id); emit(EvenementDeleted(event.id)); } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors de la suppression de l\'événement. Veuillez réessayer.', error: e, )); } } /// Charge les événements à venir Future _onLoadEvenementsAVenir( LoadEvenementsAVenir event, Emitter emit, ) async { try { emit(const EvenementsLoading()); final result = await _repository.getEvenementsAVenir( page: event.page, size: event.size, ); emit(EvenementsLoaded( evenements: result.evenements, total: result.total, page: result.page, size: result.size, totalPages: result.totalPages, )); } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors du chargement des événements à venir. Veuillez réessayer.', error: e, )); } } /// Charge les événements en cours Future _onLoadEvenementsEnCours( LoadEvenementsEnCours event, Emitter emit, ) async { try { emit(const EvenementsLoading()); final result = await _repository.getEvenementsEnCours( page: event.page, size: event.size, ); emit(EvenementsLoaded( evenements: result.evenements, total: result.total, page: result.page, size: result.size, totalPages: result.totalPages, )); } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors du chargement des événements en cours. Veuillez réessayer.', error: e, )); } } /// Charge les événements passés Future _onLoadEvenementsPasses( LoadEvenementsPasses event, Emitter emit, ) async { try { emit(const EvenementsLoading()); final result = await _repository.getEvenementsPasses( page: event.page, size: event.size, ); emit(EvenementsLoaded( evenements: result.evenements, total: result.total, page: result.page, size: result.size, totalPages: result.totalPages, )); } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors du chargement des événements passés. Veuillez réessayer.', error: e, )); } } /// S'inscrire à un événement Future _onInscrireEvenement( InscrireEvenement event, Emitter emit, ) async { try { emit(const EvenementsLoading()); await _registerForEvent(event.evenementId); emit(EvenementInscrit(event.evenementId)); } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors de l\'inscription à l\'événement. Veuillez réessayer.', error: e, )); } } /// Se désinscrire d'un événement Future _onDesinscrireEvenement( DesinscrireEvenement event, Emitter emit, ) async { try { emit(const EvenementsLoading()); await _cancelRegistration(event.evenementId); emit(EvenementDesinscrit(event.evenementId)); } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors de la désinscription de l\'événement. Veuillez réessayer.', error: e, )); } } /// Charge les participants Future _onLoadParticipants( LoadParticipants event, Emitter emit, ) async { try { emit(const EvenementsLoading()); final participants = await _getEventParticipants(event.evenementId); emit(ParticipantsLoaded( evenementId: event.evenementId, participants: participants, )); } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors du chargement des participants. Veuillez réessayer.', error: e, )); } } /// Charge les statistiques Future _onLoadEvenementsStats( LoadEvenementsStats event, Emitter emit, ) async { try { emit(const EvenementsLoading()); final stats = await _repository.getEvenementsStats(); emit(EvenementsStatsLoaded(stats)); } on DioException catch (e) { _emitDioError(e, emit); } catch (e) { if (e is DioException && e.type == DioExceptionType.cancel) return; emit(EvenementsError( message: 'Erreur lors du chargement des statistiques. Veuillez réessayer.', error: e, )); } } /// Extrait les erreurs de validation Map _extractValidationErrors(dynamic data) { final errors = {}; if (data is Map && data.containsKey('errors')) { final errorsData = data['errors']; if (errorsData is Map) { errorsData.forEach((key, value) { errors[key] = value.toString(); }); } } return errors; } /// Génère un message d'erreur réseau approprié String _getNetworkErrorMessage(DioException e) { switch (e.type) { case DioExceptionType.connectionTimeout: return 'Délai de connexion dépassé. Vérifiez votre connexion internet.'; case DioExceptionType.sendTimeout: return 'Délai d\'envoi dépassé. Vérifiez votre connexion internet.'; case DioExceptionType.receiveTimeout: return 'Délai de réception dépassé. Vérifiez votre connexion internet.'; case DioExceptionType.badResponse: final statusCode = e.response?.statusCode; if (statusCode == 401) { return 'Non autorisé. Veuillez vous reconnecter.'; } else if (statusCode == 403) { return 'Accès refusé. Vous n\'avez pas les permissions nécessaires.'; } else if (statusCode == 404) { return 'Ressource non trouvée.'; } else if (statusCode == 409) { return 'Conflit. Cette ressource existe déjà.'; } else if (statusCode != null && statusCode >= 500) { return 'Erreur serveur. Veuillez réessayer plus tard.'; } return 'Erreur lors de la communication avec le serveur.'; case DioExceptionType.cancel: return 'Requête annulée.'; case DioExceptionType.unknown: return 'Erreur de connexion. Vérifiez votre connexion internet.'; default: return 'Erreur réseau inattendue.'; } } /// Émet le bon état selon le type d'erreur : /// 401/403 → EvenementsError (autorisation), autres → EvenementsNetworkError (réseau). void _emitDioError(DioException e, Emitter emit) { if (e.type == DioExceptionType.cancel) return; final statusCode = e.response?.statusCode; if (statusCode == 401 || statusCode == 403) { emit(EvenementsError( message: _getNetworkErrorMessage(e), error: e, )); } else { emit(EvenementsNetworkError( message: _getNetworkErrorMessage(e), code: statusCode?.toString(), error: e, )); } } }