import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:injectable/injectable.dart'; import '../../../../core/models/cotisation_model.dart'; import '../../domain/repositories/cotisation_repository.dart'; import 'cotisations_event.dart'; import 'cotisations_state.dart'; /// BLoC pour la gestion des cotisations /// Gère l'état et les événements liés aux cotisations @injectable class CotisationsBloc extends Bloc { final CotisationRepository _cotisationRepository; CotisationsBloc(this._cotisationRepository) : super(const CotisationsInitial()) { // Enregistrement des handlers d'événements on(_onLoadCotisations); on(_onLoadCotisationById); on(_onLoadCotisationByReference); on(_onCreateCotisation); on(_onUpdateCotisation); on(_onDeleteCotisation); on(_onLoadCotisationsByMembre); on(_onLoadCotisationsByStatut); on(_onLoadCotisationsEnRetard); on(_onSearchCotisations); on(_onLoadCotisationsStats); on(_onRefreshCotisations); on(_onResetCotisationsState); on(_onFilterCotisations); on(_onSortCotisations); } /// Handler pour charger la liste des cotisations Future _onLoadCotisations( LoadCotisations event, Emitter emit, ) async { try { if (event.refresh || state is CotisationsInitial) { emit(CotisationsLoading(isRefreshing: event.refresh)); } final cotisations = await _cotisationRepository.getCotisations( page: event.page, size: event.size, ); List allCotisations = []; // Si c'est un refresh ou la première page, remplacer la liste if (event.refresh || event.page == 0) { allCotisations = cotisations; } else { // Sinon, ajouter à la liste existante (pagination) if (state is CotisationsLoaded) { final currentState = state as CotisationsLoaded; allCotisations = [...currentState.cotisations, ...cotisations]; } else { allCotisations = cotisations; } } emit(CotisationsLoaded( cotisations: allCotisations, filteredCotisations: allCotisations, hasReachedMax: cotisations.length < event.size, currentPage: event.page, )); } catch (error) { emit(CotisationsError( 'Erreur lors du chargement des cotisations: ${error.toString()}', originalError: error, )); } } /// Handler pour charger une cotisation par ID Future _onLoadCotisationById( LoadCotisationById event, Emitter emit, ) async { try { emit(const CotisationsLoading()); final cotisation = await _cotisationRepository.getCotisationById(event.id); emit(CotisationDetailLoaded(cotisation)); } catch (error) { emit(CotisationsError( 'Erreur lors du chargement de la cotisation: ${error.toString()}', originalError: error, )); } } /// Handler pour charger une cotisation par référence Future _onLoadCotisationByReference( LoadCotisationByReference event, Emitter emit, ) async { try { emit(const CotisationsLoading()); final cotisation = await _cotisationRepository.getCotisationByReference(event.numeroReference); emit(CotisationDetailLoaded(cotisation)); } catch (error) { emit(CotisationsError( 'Erreur lors du chargement de la cotisation: ${error.toString()}', originalError: error, )); } } /// Handler pour créer une nouvelle cotisation Future _onCreateCotisation( CreateCotisation event, Emitter emit, ) async { try { emit(const CotisationOperationLoading('create')); final nouvelleCotisation = await _cotisationRepository.createCotisation(event.cotisation); emit(CotisationCreated(nouvelleCotisation)); // Recharger la liste des cotisations add(const LoadCotisations(refresh: true)); } catch (error) { emit(CotisationsError( 'Erreur lors de la création de la cotisation: ${error.toString()}', originalError: error, )); } } /// Handler pour mettre à jour une cotisation Future _onUpdateCotisation( UpdateCotisation event, Emitter emit, ) async { try { emit(CotisationOperationLoading('update', cotisationId: event.id)); final cotisationMiseAJour = await _cotisationRepository.updateCotisation( event.id, event.cotisation, ); emit(CotisationUpdated(cotisationMiseAJour)); // Mettre à jour la liste si elle est chargée if (state is CotisationsLoaded) { final currentState = state as CotisationsLoaded; final updatedList = currentState.cotisations.map((c) { return c.id == event.id ? cotisationMiseAJour : c; }).toList(); emit(currentState.copyWith( cotisations: updatedList, filteredCotisations: updatedList, )); } } catch (error) { emit(CotisationsError( 'Erreur lors de la mise à jour de la cotisation: ${error.toString()}', originalError: error, )); } } /// Handler pour supprimer une cotisation Future _onDeleteCotisation( DeleteCotisation event, Emitter emit, ) async { try { emit(CotisationOperationLoading('delete', cotisationId: event.id)); await _cotisationRepository.deleteCotisation(event.id); emit(CotisationDeleted(event.id)); // Retirer de la liste si elle est chargée if (state is CotisationsLoaded) { final currentState = state as CotisationsLoaded; final updatedList = currentState.cotisations .where((c) => c.id != event.id) .toList(); emit(currentState.copyWith( cotisations: updatedList, filteredCotisations: updatedList, )); } } catch (error) { emit(CotisationsError( 'Erreur lors de la suppression de la cotisation: ${error.toString()}', originalError: error, )); } } /// Handler pour charger les cotisations d'un membre Future _onLoadCotisationsByMembre( LoadCotisationsByMembre event, Emitter emit, ) async { try { if (event.refresh || event.page == 0) { emit(CotisationsLoading(isRefreshing: event.refresh)); } final cotisations = await _cotisationRepository.getCotisationsByMembre( event.membreId, page: event.page, size: event.size, ); List allCotisations = []; if (event.refresh || event.page == 0) { allCotisations = cotisations; } else { if (state is CotisationsByMembreLoaded) { final currentState = state as CotisationsByMembreLoaded; allCotisations = [...currentState.cotisations, ...cotisations]; } else { allCotisations = cotisations; } } emit(CotisationsByMembreLoaded( membreId: event.membreId, cotisations: allCotisations, hasReachedMax: cotisations.length < event.size, currentPage: event.page, )); } catch (error) { emit(CotisationsError( 'Erreur lors du chargement des cotisations du membre: ${error.toString()}', originalError: error, )); } } /// Handler pour charger les cotisations par statut Future _onLoadCotisationsByStatut( LoadCotisationsByStatut event, Emitter emit, ) async { try { if (event.refresh || event.page == 0) { emit(CotisationsLoading(isRefreshing: event.refresh)); } final cotisations = await _cotisationRepository.getCotisationsByStatut( event.statut, page: event.page, size: event.size, ); List allCotisations = []; if (event.refresh || event.page == 0) { allCotisations = cotisations; } else { if (state is CotisationsLoaded) { final currentState = state as CotisationsLoaded; allCotisations = [...currentState.cotisations, ...cotisations]; } else { allCotisations = cotisations; } } emit(CotisationsLoaded( cotisations: allCotisations, filteredCotisations: allCotisations, hasReachedMax: cotisations.length < event.size, currentPage: event.page, currentFilter: event.statut, )); } catch (error) { emit(CotisationsError( 'Erreur lors du chargement des cotisations par statut: ${error.toString()}', originalError: error, )); } } /// Handler pour charger les cotisations en retard Future _onLoadCotisationsEnRetard( LoadCotisationsEnRetard event, Emitter emit, ) async { try { if (event.refresh || event.page == 0) { emit(CotisationsLoading(isRefreshing: event.refresh)); } final cotisations = await _cotisationRepository.getCotisationsEnRetard( page: event.page, size: event.size, ); List allCotisations = []; if (event.refresh || event.page == 0) { allCotisations = cotisations; } else { if (state is CotisationsEnRetardLoaded) { final currentState = state as CotisationsEnRetardLoaded; allCotisations = [...currentState.cotisations, ...cotisations]; } else { allCotisations = cotisations; } } emit(CotisationsEnRetardLoaded( cotisations: allCotisations, hasReachedMax: cotisations.length < event.size, currentPage: event.page, )); } catch (error) { emit(CotisationsError( 'Erreur lors du chargement des cotisations en retard: ${error.toString()}', originalError: error, )); } } /// Handler pour la recherche de cotisations Future _onSearchCotisations( SearchCotisations event, Emitter emit, ) async { try { if (event.refresh || event.page == 0) { emit(CotisationsLoading(isRefreshing: event.refresh)); } final cotisations = await _cotisationRepository.rechercherCotisations( membreId: event.membreId, statut: event.statut, typeCotisation: event.typeCotisation, annee: event.annee, mois: event.mois, page: event.page, size: event.size, ); final searchCriteria = { if (event.membreId != null) 'membreId': event.membreId, if (event.statut != null) 'statut': event.statut, if (event.typeCotisation != null) 'typeCotisation': event.typeCotisation, if (event.annee != null) 'annee': event.annee, if (event.mois != null) 'mois': event.mois, }; List allCotisations = []; if (event.refresh || event.page == 0) { allCotisations = cotisations; } else { if (state is CotisationsSearchResults) { final currentState = state as CotisationsSearchResults; allCotisations = [...currentState.cotisations, ...cotisations]; } else { allCotisations = cotisations; } } emit(CotisationsSearchResults( cotisations: allCotisations, searchCriteria: searchCriteria, hasReachedMax: cotisations.length < event.size, currentPage: event.page, )); } catch (error) { emit(CotisationsError( 'Erreur lors de la recherche de cotisations: ${error.toString()}', originalError: error, )); } } /// Handler pour charger les statistiques Future _onLoadCotisationsStats( LoadCotisationsStats event, Emitter emit, ) async { try { emit(const CotisationsLoading()); final statistics = await _cotisationRepository.getCotisationsStats(); emit(CotisationsStatsLoaded(statistics)); } catch (error) { emit(CotisationsError( 'Erreur lors du chargement des statistiques: ${error.toString()}', originalError: error, )); } } /// Handler pour rafraîchir les données Future _onRefreshCotisations( RefreshCotisations event, Emitter emit, ) async { add(const LoadCotisations(refresh: true)); } /// Handler pour réinitialiser l'état Future _onResetCotisationsState( ResetCotisationsState event, Emitter emit, ) async { emit(const CotisationsInitial()); } /// Handler pour filtrer les cotisations localement Future _onFilterCotisations( FilterCotisations event, Emitter emit, ) async { if (state is CotisationsLoaded) { final currentState = state as CotisationsLoaded; List filteredList = currentState.cotisations; // Filtrage par recherche textuelle if (event.searchQuery != null && event.searchQuery!.isNotEmpty) { final query = event.searchQuery!.toLowerCase(); filteredList = filteredList.where((cotisation) { return cotisation.numeroReference.toLowerCase().contains(query) || (cotisation.nomMembre?.toLowerCase().contains(query) ?? false) || cotisation.typeCotisation.toLowerCase().contains(query) || (cotisation.description?.toLowerCase().contains(query) ?? false); }).toList(); } // Filtrage par statut if (event.statutFilter != null && event.statutFilter!.isNotEmpty) { filteredList = filteredList.where((cotisation) { return cotisation.statut == event.statutFilter; }).toList(); } // Filtrage par type if (event.typeFilter != null && event.typeFilter!.isNotEmpty) { filteredList = filteredList.where((cotisation) { return cotisation.typeCotisation == event.typeFilter; }).toList(); } emit(currentState.copyWith( filteredCotisations: filteredList, searchQuery: event.searchQuery, currentFilter: event.statutFilter ?? event.typeFilter, )); } } /// Handler pour trier les cotisations Future _onSortCotisations( SortCotisations event, Emitter emit, ) async { if (state is CotisationsLoaded) { final currentState = state as CotisationsLoaded; List sortedList = [...currentState.filteredCotisations]; switch (event.sortBy) { case 'dateEcheance': sortedList.sort((a, b) => event.ascending ? a.dateEcheance.compareTo(b.dateEcheance) : b.dateEcheance.compareTo(a.dateEcheance)); break; case 'montantDu': sortedList.sort((a, b) => event.ascending ? a.montantDu.compareTo(b.montantDu) : b.montantDu.compareTo(a.montantDu)); break; case 'statut': sortedList.sort((a, b) => event.ascending ? a.statut.compareTo(b.statut) : b.statut.compareTo(a.statut)); break; case 'nomMembre': sortedList.sort((a, b) => event.ascending ? (a.nomMembre ?? '').compareTo(b.nomMembre ?? '') : (b.nomMembre ?? '').compareTo(a.nomMembre ?? '')); break; case 'typeCotisation': sortedList.sort((a, b) => event.ascending ? a.typeCotisation.compareTo(b.typeCotisation) : b.typeCotisation.compareTo(a.typeCotisation)); break; default: // Tri par défaut par date d'échéance sortedList.sort((a, b) => b.dateEcheance.compareTo(a.dateEcheance)); } emit(currentState.copyWith(filteredCotisations: sortedList)); } } }