import 'package:dio/dio.dart'; import 'package:injectable/injectable.dart'; import '../models/membre_model.dart'; import '../models/cotisation_model.dart'; import '../models/wave_checkout_session_model.dart'; import '../network/dio_client.dart'; /// Service API principal pour communiquer avec le serveur UnionFlow @singleton class ApiService { final DioClient _dioClient; ApiService(this._dioClient); Dio get _dio => _dioClient.dio; // ======================================== // MEMBRES // ======================================== /// Récupère la liste de tous les membres actifs Future> getMembres() async { try { final response = await _dio.get('/api/membres'); if (response.data is List) { return (response.data as List) .map((json) => MembreModel.fromJson(json as Map)) .toList(); } throw Exception('Format de réponse invalide pour la liste des membres'); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération des membres'); } } /// Récupère un membre par son ID Future getMembreById(String id) async { try { final response = await _dio.get('/api/membres/$id'); return MembreModel.fromJson(response.data as Map); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération du membre'); } } /// Crée un nouveau membre Future createMembre(MembreModel membre) async { try { final response = await _dio.post( '/api/membres', data: membre.toJson(), ); return MembreModel.fromJson(response.data as Map); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la création du membre'); } } /// Met à jour un membre existant Future updateMembre(String id, MembreModel membre) async { try { final response = await _dio.put( '/api/membres/$id', data: membre.toJson(), ); return MembreModel.fromJson(response.data as Map); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la mise à jour du membre'); } } /// Désactive un membre Future deleteMembre(String id) async { try { await _dio.delete('/api/membres/$id'); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la suppression du membre'); } } /// Recherche des membres par nom ou prénom Future> searchMembres(String query) async { try { final response = await _dio.get( '/api/membres/recherche', queryParameters: {'q': query}, ); if (response.data is List) { return (response.data as List) .map((json) => MembreModel.fromJson(json as Map)) .toList(); } throw Exception('Format de réponse invalide pour la recherche'); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la recherche de membres'); } } /// Récupère les statistiques des membres Future> getMembresStats() async { try { final response = await _dio.get('/api/membres/stats'); return response.data as Map; } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération des statistiques'); } } // ======================================== // PAIEMENTS WAVE // ======================================== /// Crée une session de paiement Wave Future createWaveSession({ required double montant, required String devise, required String successUrl, required String errorUrl, String? organisationId, String? membreId, String? typePaiement, String? description, }) async { try { final response = await _dio.post( '/api/paiements/wave/sessions', data: { 'montant': montant, 'devise': devise, 'successUrl': successUrl, 'errorUrl': errorUrl, if (organisationId != null) 'organisationId': organisationId, if (membreId != null) 'membreId': membreId, if (typePaiement != null) 'typePaiement': typePaiement, if (description != null) 'description': description, }, ); return WaveCheckoutSessionModel.fromJson(response.data as Map); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la création de la session Wave'); } } /// Récupère une session de paiement Wave par son ID Future getWaveSession(String sessionId) async { try { final response = await _dio.get('/api/paiements/wave/sessions/$sessionId'); return WaveCheckoutSessionModel.fromJson(response.data as Map); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération de la session Wave'); } } /// Vérifie le statut d'une session de paiement Wave Future checkWaveSessionStatus(String sessionId) async { try { final response = await _dio.get('/api/paiements/wave/sessions/$sessionId/status'); return response.data['statut'] as String; } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la vérification du statut Wave'); } } // ======================================== // COTISATIONS // ======================================== /// Récupère la liste de toutes les cotisations avec pagination Future> getCotisations({int page = 0, int size = 20}) async { try { final response = await _dio.get('/api/cotisations', queryParameters: { 'page': page, 'size': size, }); if (response.data is List) { return (response.data as List) .map((json) => CotisationModel.fromJson(json as Map)) .toList(); } throw Exception('Format de réponse invalide pour la liste des cotisations'); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération des cotisations'); } } /// Récupère une cotisation par son ID Future getCotisationById(String id) async { try { final response = await _dio.get('/api/cotisations/$id'); return CotisationModel.fromJson(response.data as Map); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération de la cotisation'); } } /// Récupère une cotisation par son numéro de référence Future getCotisationByReference(String numeroReference) async { try { final response = await _dio.get('/api/cotisations/reference/$numeroReference'); return CotisationModel.fromJson(response.data as Map); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération de la cotisation'); } } /// Crée une nouvelle cotisation Future createCotisation(CotisationModel cotisation) async { try { final response = await _dio.post( '/api/cotisations', data: cotisation.toJson(), ); return CotisationModel.fromJson(response.data as Map); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la création de la cotisation'); } } /// Met à jour une cotisation existante Future updateCotisation(String id, CotisationModel cotisation) async { try { final response = await _dio.put( '/api/cotisations/$id', data: cotisation.toJson(), ); return CotisationModel.fromJson(response.data as Map); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la mise à jour de la cotisation'); } } /// Supprime une cotisation Future deleteCotisation(String id) async { try { await _dio.delete('/api/cotisations/$id'); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la suppression de la cotisation'); } } /// Récupère les cotisations d'un membre Future> getCotisationsByMembre(String membreId, {int page = 0, int size = 20}) async { try { final response = await _dio.get('/api/cotisations/membre/$membreId', queryParameters: { 'page': page, 'size': size, }); if (response.data is List) { return (response.data as List) .map((json) => CotisationModel.fromJson(json as Map)) .toList(); } throw Exception('Format de réponse invalide pour les cotisations du membre'); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération des cotisations du membre'); } } /// Récupère les cotisations par statut Future> getCotisationsByStatut(String statut, {int page = 0, int size = 20}) async { try { final response = await _dio.get('/api/cotisations/statut/$statut', queryParameters: { 'page': page, 'size': size, }); if (response.data is List) { return (response.data as List) .map((json) => CotisationModel.fromJson(json as Map)) .toList(); } throw Exception('Format de réponse invalide pour les cotisations par statut'); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération des cotisations par statut'); } } /// Récupère les cotisations en retard Future> getCotisationsEnRetard({int page = 0, int size = 20}) async { try { final response = await _dio.get('/api/cotisations/en-retard', queryParameters: { 'page': page, 'size': size, }); if (response.data is List) { return (response.data as List) .map((json) => CotisationModel.fromJson(json as Map)) .toList(); } throw Exception('Format de réponse invalide pour les cotisations en retard'); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération des cotisations en retard'); } } /// Recherche avancée de cotisations Future> rechercherCotisations({ String? membreId, String? statut, String? typeCotisation, int? annee, int? mois, int page = 0, int size = 20, }) async { try { final queryParams = { 'page': page, 'size': size, }; if (membreId != null) queryParams['membreId'] = membreId; if (statut != null) queryParams['statut'] = statut; if (typeCotisation != null) queryParams['typeCotisation'] = typeCotisation; if (annee != null) queryParams['annee'] = annee; if (mois != null) queryParams['mois'] = mois; final response = await _dio.get('/api/cotisations/recherche', queryParameters: queryParams); if (response.data is List) { return (response.data as List) .map((json) => CotisationModel.fromJson(json as Map)) .toList(); } throw Exception('Format de réponse invalide pour la recherche de cotisations'); } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la recherche de cotisations'); } } /// Récupère les statistiques des cotisations Future> getCotisationsStats() async { try { final response = await _dio.get('/api/cotisations/stats'); return response.data as Map; } on DioException catch (e) { throw _handleDioException(e, 'Erreur lors de la récupération des statistiques des cotisations'); } } // ======================================== // GESTION DES ERREURS // ======================================== /// Gère les exceptions Dio et les convertit en messages d'erreur appropriés Exception _handleDioException(DioException e, String defaultMessage) { switch (e.type) { case DioExceptionType.connectionTimeout: case DioExceptionType.sendTimeout: case DioExceptionType.receiveTimeout: return Exception('Délai d\'attente dépassé. Vérifiez votre connexion internet.'); case DioExceptionType.badResponse: final statusCode = e.response?.statusCode; final responseData = e.response?.data; if (statusCode == 400) { if (responseData is Map && responseData.containsKey('message')) { return Exception(responseData['message']); } return Exception('Données invalides'); } else if (statusCode == 401) { return Exception('Non autorisé. Veuillez vous reconnecter.'); } else if (statusCode == 403) { return Exception('Accès interdit'); } else if (statusCode == 404) { return Exception('Ressource non trouvée'); } else if (statusCode == 500) { return Exception('Erreur serveur. Veuillez réessayer plus tard.'); } return Exception('$defaultMessage (Code: $statusCode)'); case DioExceptionType.cancel: return Exception('Requête annulée'); case DioExceptionType.connectionError: return Exception('Erreur de connexion. Vérifiez votre connexion internet.'); case DioExceptionType.badCertificate: return Exception('Certificat SSL invalide'); case DioExceptionType.unknown: default: return Exception(defaultMessage); } } }