import 'package:dartz/dartz.dart'; import '../../../../core/error/failures.dart'; import '../../../../core/usecases/usecase.dart'; import '../entities/demande_aide.dart'; import '../entities/proposition_aide.dart'; import '../repositories/solidarite_repository.dart'; /// Cas d'usage pour trouver les propositions compatibles avec une demande class TrouverPropositionsCompatiblesUseCase implements UseCase, TrouverPropositionsCompatiblesParams> { final SolidariteRepository repository; TrouverPropositionsCompatiblesUseCase(this.repository); @override Future>> call(TrouverPropositionsCompatiblesParams params) async { return await repository.trouverPropositionsCompatibles(params.demandeId); } } class TrouverPropositionsCompatiblesParams { final String demandeId; TrouverPropositionsCompatiblesParams({required this.demandeId}); } /// Cas d'usage pour trouver les demandes compatibles avec une proposition class TrouverDemandesCompatiblesUseCase implements UseCase, TrouverDemandesCompatiblesParams> { final SolidariteRepository repository; TrouverDemandesCompatiblesUseCase(this.repository); @override Future>> call(TrouverDemandesCompatiblesParams params) async { return await repository.trouverDemandesCompatibles(params.propositionId); } } class TrouverDemandesCompatiblesParams { final String propositionId; TrouverDemandesCompatiblesParams({required this.propositionId}); } /// Cas d'usage pour rechercher des proposants financiers class RechercherProposantsFinanciersUseCase implements UseCase, RechercherProposantsFinanciersParams> { final SolidariteRepository repository; RechercherProposantsFinanciersUseCase(this.repository); @override Future>> call(RechercherProposantsFinanciersParams params) async { return await repository.rechercherProposantsFinanciers(params.demandeId); } } class RechercherProposantsFinanciersParams { final String demandeId; RechercherProposantsFinanciersParams({required this.demandeId}); } /// Cas d'usage pour calculer le score de compatibilité entre une demande et une proposition class CalculerScoreCompatibiliteUseCase implements UseCase { CalculerScoreCompatibiliteUseCase(); @override Future> call(CalculerScoreCompatibiliteParams params) async { try { final demande = params.demande; final proposition = params.proposition; double score = 0.0; // 1. Correspondance du type d'aide (40 points max) if (demande.typeAide == proposition.typeAide) { score += 40.0; } else if (_sontTypesCompatibles(demande.typeAide, proposition.typeAide)) { score += 25.0; } else if (proposition.typeAide == TypeAide.autre) { score += 15.0; } // 2. Compatibilité financière (25 points max) if (_necessiteMontant(demande.typeAide) && proposition.montantMaximum != null) { final montantDemande = demande.montantDemande; if (montantDemande != null) { if (montantDemande <= proposition.montantMaximum!) { score += 25.0; } else { // Pénalité proportionnelle au dépassement double ratio = proposition.montantMaximum! / montantDemande; score += 25.0 * ratio; } } } else if (!_necessiteMontant(demande.typeAide)) { score += 25.0; // Pas de contrainte financière } // 3. Expérience du proposant (15 points max) if (proposition.nombreBeneficiairesAides > 0) { score += (proposition.nombreBeneficiairesAides * 2.0).clamp(0.0, 15.0); } // 4. Réputation (10 points max) if (proposition.noteMoyenne != null && proposition.nombreEvaluations >= 3) { score += (proposition.noteMoyenne! - 3.0) * 3.33; } // 5. Disponibilité et capacité (10 points max) if (proposition.peutAccepterBeneficiaires) { double ratioCapacite = proposition.placesRestantes / proposition.nombreMaxBeneficiaires; score += 10.0 * ratioCapacite; } // Bonus et malus additionnels score += _calculerBonusGeographique(demande, proposition); score += _calculerBonusTemporel(demande, proposition); score -= _calculerMalusDelai(demande, proposition); return Right(score.clamp(0.0, 100.0)); } catch (e) { return Left(UnexpectedFailure('Erreur lors du calcul de compatibilité: ${e.toString()}')); } } bool _sontTypesCompatibles(TypeAide typeAide1, TypeAide typeAide2) { // Définir les groupes de types compatibles final groupesCompatibles = [ [TypeAide.aideFinanciereUrgente, TypeAide.aideFinanciereMedicale, TypeAide.aideFinanciereEducation], [TypeAide.aideMaterielleVetements, TypeAide.aideMaterielleNourriture], [TypeAide.aideProfessionnelleFormation, TypeAide.aideSocialeAccompagnement], ]; for (final groupe in groupesCompatibles) { if (groupe.contains(typeAide1) && groupe.contains(typeAide2)) { return true; } } return false; } bool _necessiteMontant(TypeAide typeAide) { return [ TypeAide.aideFinanciereUrgente, TypeAide.aideFinanciereMedicale, TypeAide.aideFinanciereEducation, ].contains(typeAide); } double _calculerBonusGeographique(DemandeAide demande, PropositionAide proposition) { // Simulation - dans une vraie implémentation, on utiliserait les données de localisation if (demande.localisation != null && proposition.zonesGeographiques.isNotEmpty) { // Logique de proximité géographique return 5.0; } return 0.0; } double _calculerBonusTemporel(DemandeAide demande, PropositionAide proposition) { double bonus = 0.0; // Bonus pour demande urgente if (demande.estUrgente) { bonus += 5.0; } // Bonus pour proposition récente final joursDepuisCreation = DateTime.now().difference(proposition.dateCreation).inDays; if (joursDepuisCreation <= 30) { bonus += 3.0; } return bonus; } double _calculerMalusDelai(DemandeAide demande, PropositionAide proposition) { double malus = 0.0; // Malus si la demande est en retard if (demande.delaiDepasse) { malus += 5.0; } // Malus si la proposition a un délai de réponse long if (proposition.delaiReponseHeures > 168) { // Plus d'une semaine malus += 3.0; } return malus; } } class CalculerScoreCompatibiliteParams { final DemandeAide demande; final PropositionAide proposition; CalculerScoreCompatibiliteParams({ required this.demande, required this.proposition, }); } /// Cas d'usage pour effectuer un matching intelligent class EffectuerMatchingIntelligentUseCase implements UseCase, EffectuerMatchingIntelligentParams> { final TrouverPropositionsCompatiblesUseCase trouverPropositionsCompatibles; final CalculerScoreCompatibiliteUseCase calculerScoreCompatibilite; EffectuerMatchingIntelligentUseCase({ required this.trouverPropositionsCompatibles, required this.calculerScoreCompatibilite, }); @override Future>> call(EffectuerMatchingIntelligentParams params) async { try { // 1. Trouver les propositions compatibles final propositionsResult = await trouverPropositionsCompatibles( TrouverPropositionsCompatiblesParams(demandeId: params.demande.id) ); return propositionsResult.fold( (failure) => Left(failure), (propositions) async { // 2. Calculer les scores de compatibilité final resultats = []; for (final proposition in propositions) { final scoreResult = await calculerScoreCompatibilite( CalculerScoreCompatibiliteParams( demande: params.demande, proposition: proposition, ) ); scoreResult.fold( (failure) { // Ignorer les erreurs de calcul de score individuel }, (score) { if (score >= params.scoreMinimum) { resultats.add(ResultatMatching( proposition: proposition, score: score, raisonCompatibilite: _genererRaisonCompatibilite(params.demande, proposition, score), )); } }, ); } // 3. Trier par score décroissant resultats.sort((a, b) => b.score.compareTo(a.score)); // 4. Limiter le nombre de résultats final resultatsLimites = resultats.take(params.limiteResultats).toList(); return Right(resultatsLimites); }, ); } catch (e) { return Left(UnexpectedFailure('Erreur lors du matching intelligent: ${e.toString()}')); } } String _genererRaisonCompatibilite(DemandeAide demande, PropositionAide proposition, double score) { final raisons = []; // Type d'aide if (demande.typeAide == proposition.typeAide) { raisons.add('Type d\'aide identique'); } // Compatibilité financière if (demande.montantDemande != null && proposition.montantMaximum != null) { if (demande.montantDemande! <= proposition.montantMaximum!) { raisons.add('Montant compatible'); } } // Expérience if (proposition.nombreBeneficiairesAides > 5) { raisons.add('Proposant expérimenté'); } // Réputation if (proposition.noteMoyenne != null && proposition.noteMoyenne! >= 4.0) { raisons.add('Excellente réputation'); } // Disponibilité if (proposition.peutAccepterBeneficiaires) { raisons.add('Places disponibles'); } return raisons.isEmpty ? 'Compatible' : raisons.join(', '); } } class EffectuerMatchingIntelligentParams { final DemandeAide demande; final double scoreMinimum; final int limiteResultats; EffectuerMatchingIntelligentParams({ required this.demande, this.scoreMinimum = 30.0, this.limiteResultats = 10, }); } /// Classe représentant un résultat de matching class ResultatMatching { final PropositionAide proposition; final double score; final String raisonCompatibilite; const ResultatMatching({ required this.proposition, required this.score, required this.raisonCompatibilite, }); } /// Cas d'usage pour analyser les tendances de matching class AnalyserTendancesMatchingUseCase implements UseCase { AnalyserTendancesMatchingUseCase(); @override Future> call(AnalyserTendancesMatchingParams params) async { try { // Simulation d'analyse des tendances // Dans une vraie implémentation, on analyserait les données historiques final analyse = AnalyseTendances( tauxMatchingMoyen: 78.5, tempsMatchingMoyen: const Duration(hours: 6), typesAidePlusDemandesMap: { TypeAide.aideFinanciereUrgente: 45, TypeAide.aideFinanciereMedicale: 32, TypeAide.aideMaterielleNourriture: 28, }, typesAidePlusProposesMap: { TypeAide.aideFinanciereEducation: 38, TypeAide.aideProfessionnelleFormation: 25, TypeAide.aideSocialeAccompagnement: 22, }, heuresOptimalesMatching: ['09:00', '14:00', '18:00'], recommandations: [ 'Augmenter les propositions d\'aide financière urgente', 'Promouvoir les aides matérielles auprès des proposants', 'Optimiser les notifications entre 9h et 18h', ], ); return Right(analyse); } catch (e) { return Left(UnexpectedFailure('Erreur lors de l\'analyse des tendances: ${e.toString()}')); } } } class AnalyserTendancesMatchingParams { final String organisationId; final DateTime? dateDebut; final DateTime? dateFin; AnalyserTendancesMatchingParams({ required this.organisationId, this.dateDebut, this.dateFin, }); } /// Classe représentant une analyse des tendances de matching class AnalyseTendances { final double tauxMatchingMoyen; final Duration tempsMatchingMoyen; final Map typesAidePlusDemandesMap; final Map typesAidePlusProposesMap; final List heuresOptimalesMatching; final List recommandations; const AnalyseTendances({ required this.tauxMatchingMoyen, required this.tempsMatchingMoyen, required this.typesAidePlusDemandesMap, required this.typesAidePlusProposesMap, required this.heuresOptimalesMatching, required this.recommandations, }); }