/// BLoC pour la gestion des contributions
library contributions_bloc;

import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../core/utils/logger.dart';
import '../data/models/contribution_model.dart';
import 'contributions_event.dart';
import 'contributions_state.dart';

/// BLoC pour gérer l'état des contributions
class ContributionsBloc extends Bloc<ContributionsEvent, ContributionsState> {
  ContributionsBloc() : super(const ContributionsInitial()) {
    on<LoadContributions>(_onLoadContributions);
    on<LoadContributionById>(_onLoadContributionById);
    on<CreateContribution>(_onCreateContribution);
    on<UpdateContribution>(_onUpdateContribution);
    on<DeleteContribution>(_onDeleteContribution);
    on<SearchContributions>(_onSearchContributions);
    on<LoadContributionsByMembre>(_onLoadContributionsByMembre);
    on<LoadContributionsPayees>(_onLoadContributionsPayees);
    on<LoadContributionsNonPayees>(_onLoadContributionsNonPayees);
    on<LoadContributionsEnRetard>(_onLoadContributionsEnRetard);
    on<RecordPayment>(_onRecordPayment);
    on<LoadContributionsStats>(_onLoadContributionsStats);
    on<GenerateAnnualContributions>(_onGenerateAnnualContributions);
    on<SendPaymentReminder>(_onSendPaymentReminder);
  }

  /// Charger la liste des contributions
  Future<void> _onLoadContributions(
    LoadContributions event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      AppLogger.blocEvent('ContributionsBloc', 'LoadContributions', data: {
        'page': event.page,
        'size': event.size,
      });

      emit(const ContributionsLoading(message: 'Chargement des contributions...'));

      // Simuler un délai réseau
      await Future.delayed(const Duration(milliseconds: 500));

      // Données mock
      final contributions = _getMockContributions();
      final total = contributions.length;
      final totalPages = (total / event.size).ceil();

      // Pagination
      final start = event.page * event.size;
      final end = (start + event.size).clamp(0, total);
      final paginatedContributions = contributions.sublist(
        start.clamp(0, total),
        end,
      );

      emit(ContributionsLoaded(
        contributions: paginatedContributions,
        total: total,
        page: event.page,
        size: event.size,
        totalPages: totalPages,
      ));

      AppLogger.blocState('ContributionsBloc', 'ContributionsLoaded', data: {
        'count': paginatedContributions.length,
        'total': total,
      });
    } catch (e, stackTrace) {
      AppLogger.error(
        'Erreur lors du chargement des contributions',
        error: e,
        stackTrace: stackTrace,
      );
      emit(ContributionsError(
        message: 'Erreur lors du chargement des contributions',
        error: e,
      ));
    }
  }

  /// Charger une contribution par ID
  Future<void> _onLoadContributionById(
    LoadContributionById event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      AppLogger.blocEvent('ContributionsBloc', 'LoadContributionById', data: {
        'id': event.id,
      });

      emit(const ContributionsLoading(message: 'Chargement de la contribution...'));

      await Future.delayed(const Duration(milliseconds: 300));

      final contributions = _getMockContributions();
      final contribution = contributions.firstWhere(
        (c) => c.id == event.id,
        orElse: () => throw Exception('Contribution non trouvée'),
      );

      emit(ContributionDetailLoaded(contribution: contribution));

      AppLogger.blocState('ContributionsBloc', 'ContributionDetailLoaded');
    } catch (e, stackTrace) {
      AppLogger.error(
        'Erreur lors du chargement de la contribution',
        error: e,
        stackTrace: stackTrace,
      );
      emit(ContributionsError(
        message: 'Contribution non trouvée',
        error: e,
      ));
    }
  }

  /// Créer une nouvelle contribution
  Future<void> _onCreateContribution(
    CreateContribution event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      AppLogger.blocEvent('ContributionsBloc', 'CreateContribution');

      emit(const ContributionsLoading(message: 'Création de la contribution...'));

      await Future.delayed(const Duration(milliseconds: 500));

      final newContribution = event.contribution.copyWith(
        id: 'cont_${DateTime.now().millisecondsSinceEpoch}',
        dateCreation: DateTime.now(),
      );

      emit(ContributionCreated(contribution: newContribution));

      AppLogger.blocState('ContributionsBloc', 'ContributionCreated');
    } catch (e, stackTrace) {
      AppLogger.error(
        'Erreur lors de la création de la contribution',
        error: e,
        stackTrace: stackTrace,
      );
      emit(ContributionsError(
        message: 'Erreur lors de la création de la contribution',
        error: e,
      ));
    }
  }

  /// Mettre à jour une contribution
  Future<void> _onUpdateContribution(
    UpdateContribution event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      AppLogger.blocEvent('ContributionsBloc', 'UpdateContribution', data: {
        'id': event.id,
      });

      emit(const ContributionsLoading(message: 'Mise à jour de la contribution...'));

      await Future.delayed(const Duration(milliseconds: 500));

      final updatedContribution = event.contribution.copyWith(
        id: event.id,
        dateModification: DateTime.now(),
      );

      emit(ContributionUpdated(contribution: updatedContribution));

      AppLogger.blocState('ContributionsBloc', 'ContributionUpdated');
    } catch (e, stackTrace) {
      AppLogger.error(
        'Erreur lors de la mise à jour de la contribution',
        error: e,
        stackTrace: stackTrace,
      );
      emit(ContributionsError(
        message: 'Erreur lors de la mise à jour de la contribution',
        error: e,
      ));
    }
  }

  /// Supprimer une contribution
  Future<void> _onDeleteContribution(
    DeleteContribution event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      AppLogger.blocEvent('ContributionsBloc', 'DeleteContribution', data: {
        'id': event.id,
      });

      emit(const ContributionsLoading(message: 'Suppression de la contribution...'));

      await Future.delayed(const Duration(milliseconds: 500));

      emit(ContributionDeleted(id: event.id));

      AppLogger.blocState('ContributionsBloc', 'ContributionDeleted');
    } catch (e, stackTrace) {
      AppLogger.error(
        'Erreur lors de la suppression de la contribution',
        error: e,
        stackTrace: stackTrace,
      );
      emit(ContributionsError(
        message: 'Erreur lors de la suppression de la contribution',
        error: e,
      ));
    }
  }

  /// Rechercher des contributions
  Future<void> _onSearchContributions(
    SearchContributions event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      AppLogger.blocEvent('ContributionsBloc', 'SearchContributions');

      emit(const ContributionsLoading(message: 'Recherche en cours...'));

      await Future.delayed(const Duration(milliseconds: 500));

      var contributions = _getMockContributions();

      // Filtrer par membre
      if (event.membreId != null) {
        contributions = contributions
            .where((c) => c.membreId == event.membreId)
            .toList();
      }

      // Filtrer par statut
      if (event.statut != null) {
        contributions = contributions
            .where((c) => c.statut == event.statut)
            .toList();
      }

      // Filtrer par type
      if (event.type != null) {
        contributions = contributions
            .where((c) => c.type == event.type)
            .toList();
      }

      // Filtrer par année
      if (event.annee != null) {
        contributions = contributions
            .where((c) => c.annee == event.annee)
            .toList();
      }

      final total = contributions.length;
      final totalPages = (total / event.size).ceil();

      // Pagination
      final start = event.page * event.size;
      final end = (start + event.size).clamp(0, total);
      final paginatedContributions = contributions.sublist(
        start.clamp(0, total),
        end,
      );

      emit(ContributionsLoaded(
        contributions: paginatedContributions,
        total: total,
        page: event.page,
        size: event.size,
        totalPages: totalPages,
      ));

      AppLogger.blocState('ContributionsBloc', 'ContributionsLoaded (search)');
    } catch (e, stackTrace) {
      AppLogger.error(
        'Erreur lors de la recherche de contributions',
        error: e,
        stackTrace: stackTrace,
      );
      emit(ContributionsError(
        message: 'Erreur lors de la recherche',
        error: e,
      ));
    }
  }

  /// Charger les contributions d'un membre
  Future<void> _onLoadContributionsByMembre(
    LoadContributionsByMembre event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      AppLogger.blocEvent('ContributionsBloc', 'LoadContributionsByMembre', data: {
        'membreId': event.membreId,
      });

      emit(const ContributionsLoading(message: 'Chargement des contributions du membre...'));

      await Future.delayed(const Duration(milliseconds: 500));

      final contributions = _getMockContributions()
          .where((c) => c.membreId == event.membreId)
          .toList();

      final total = contributions.length;
      final totalPages = (total / event.size).ceil();

      emit(ContributionsLoaded(
        contributions: contributions,
        total: total,
        page: event.page,
        size: event.size,
        totalPages: totalPages,
      ));

      AppLogger.blocState('ContributionsBloc', 'ContributionsLoaded (by membre)');
    } catch (e, stackTrace) {
      AppLogger.error(
        'Erreur lors du chargement des contributions du membre',
        error: e,
        stackTrace: stackTrace,
      );
      emit(ContributionsError(
        message: 'Erreur lors du chargement',
        error: e,
      ));
    }
  }

  /// Charger les contributions payées
  Future<void> _onLoadContributionsPayees(
    LoadContributionsPayees event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      emit(const ContributionsLoading(message: 'Chargement des contributions payées...'));

      await Future.delayed(const Duration(milliseconds: 500));

      final contributions = _getMockContributions()
          .where((c) => c.statut == ContributionStatus.payee)
          .toList();

      final total = contributions.length;
      final totalPages = (total / event.size).ceil();

      emit(ContributionsLoaded(
        contributions: contributions,
        total: total,
        page: event.page,
        size: event.size,
        totalPages: totalPages,
      ));
    } catch (e, stackTrace) {
      AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
      emit(ContributionsError(message: 'Erreur', error: e));
    }
  }

  /// Charger les contributions non payées
  Future<void> _onLoadContributionsNonPayees(
    LoadContributionsNonPayees event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      emit(const ContributionsLoading(message: 'Chargement des contributions non payées...'));

      await Future.delayed(const Duration(milliseconds: 500));

      final contributions = _getMockContributions()
          .where((c) => c.statut == ContributionStatus.nonPayee)
          .toList();

      final total = contributions.length;
      final totalPages = (total / event.size).ceil();

      emit(ContributionsLoaded(
        contributions: contributions,
        total: total,
        page: event.page,
        size: event.size,
        totalPages: totalPages,
      ));
    } catch (e, stackTrace) {
      AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
      emit(ContributionsError(message: 'Erreur', error: e));
    }
  }

  /// Charger les contributions en retard
  Future<void> _onLoadContributionsEnRetard(
    LoadContributionsEnRetard event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      emit(const ContributionsLoading(message: 'Chargement des contributions en retard...'));

      await Future.delayed(const Duration(milliseconds: 500));

      final contributions = _getMockContributions()
          .where((c) => c.statut == ContributionStatus.enRetard)
          .toList();

      final total = contributions.length;
      final totalPages = (total / event.size).ceil();

      emit(ContributionsLoaded(
        contributions: contributions,
        total: total,
        page: event.page,
        size: event.size,
        totalPages: totalPages,
      ));
    } catch (e, stackTrace) {
      AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
      emit(ContributionsError(message: 'Erreur', error: e));
    }
  }

  /// Enregistrer un paiement
  Future<void> _onRecordPayment(
    RecordPayment event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      AppLogger.blocEvent('ContributionsBloc', 'RecordPayment');

      emit(const ContributionsLoading(message: 'Enregistrement du paiement...'));

      await Future.delayed(const Duration(milliseconds: 500));

      final contributions = _getMockContributions();
      final contribution = contributions.firstWhere((c) => c.id == event.contributionId);

      final updatedContribution = contribution.copyWith(
        montantPaye: event.montant,
        datePaiement: event.datePaiement,
        methodePaiement: event.methodePaiement,
        numeroPaiement: event.numeroPaiement,
        referencePaiement: event.referencePaiement,
        statut: event.montant >= contribution.montant
            ? ContributionStatus.payee
            : ContributionStatus.partielle,
        dateModification: DateTime.now(),
      );

      emit(PaymentRecorded(contribution: updatedContribution));

      AppLogger.blocState('ContributionsBloc', 'PaymentRecorded');
    } catch (e, stackTrace) {
      AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
      emit(ContributionsError(message: 'Erreur lors de l\'enregistrement du paiement', error: e));
    }
  }

  /// Charger les statistiques
  Future<void> _onLoadContributionsStats(
    LoadContributionsStats event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      emit(const ContributionsLoading(message: 'Chargement des statistiques...'));

      await Future.delayed(const Duration(milliseconds: 500));

      final contributions = _getMockContributions();

      final stats = {
        'total': contributions.length,
        'payees': contributions.where((c) => c.statut == ContributionStatus.payee).length,
        'nonPayees': contributions.where((c) => c.statut == ContributionStatus.nonPayee).length,
        'enRetard': contributions.where((c) => c.statut == ContributionStatus.enRetard).length,
        'partielles': contributions.where((c) => c.statut == ContributionStatus.partielle).length,
        'montantTotal': contributions.fold<double>(0, (sum, c) => sum + c.montant),
        'montantPaye': contributions.fold<double>(0, (sum, c) => sum + (c.montantPaye ?? 0)),
        'montantRestant': contributions.fold<double>(0, (sum, c) => sum + c.montantRestant),
        'tauxRecouvrement': 0.0,
      };

      if (stats['montantTotal']! > 0) {
        stats['tauxRecouvrement'] = (stats['montantPaye']! / stats['montantTotal']!) * 100;
      }

      emit(ContributionsStatsLoaded(stats: stats));
    } catch (e, stackTrace) {
      AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
      emit(ContributionsError(message: 'Erreur', error: e));
    }
  }

  /// Générer les contributions annuelles
  Future<void> _onGenerateAnnualContributions(
    GenerateAnnualContributions event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      emit(const ContributionsLoading(message: 'Génération des contributions...'));

      await Future.delayed(const Duration(seconds: 1));

      // Simuler la génération de 50 contributions
      emit(const ContributionsGenerated(nombreGenere: 50));
    } catch (e, stackTrace) {
      AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
      emit(ContributionsError(message: 'Erreur', error: e));
    }
  }

  /// Envoyer un rappel de paiement
  Future<void> _onSendPaymentReminder(
    SendPaymentReminder event,
    Emitter<ContributionsState> emit,
  ) async {
    try {
      emit(const ContributionsLoading(message: 'Envoi du rappel...'));

      await Future.delayed(const Duration(milliseconds: 500));

      emit(ReminderSent(contributionId: event.contributionId));
    } catch (e, stackTrace) {
      AppLogger.error('Erreur', error: e, stackTrace: stackTrace);
      emit(ContributionsError(message: 'Erreur', error: e));
    }
  }

  /// Données mock pour les tests
  List<ContributionModel> _getMockContributions() {
    final now = DateTime.now();
    return [
      ContributionModel(
        id: 'cont_001',
        membreId: 'mbr_001',
        membreNom: 'Dupont',
        membrePrenom: 'Jean',
        montant: 50000,
        dateEcheance: DateTime(now.year, 12, 31),
        annee: now.year,
        statut: ContributionStatus.payee,
        montantPaye: 50000,
        datePaiement: DateTime(now.year, 1, 15),
        methodePaiement: PaymentMethod.virement,
      ),
      ContributionModel(
        id: 'cont_002',
        membreId: 'mbr_002',
        membreNom: 'Martin',
        membrePrenom: 'Marie',
        montant: 50000,
        dateEcheance: DateTime(now.year, 12, 31),
        annee: now.year,
        statut: ContributionStatus.nonPayee,
      ),
      ContributionModel(
        id: 'cont_003',
        membreId: 'mbr_003',
        membreNom: 'Bernard',
        membrePrenom: 'Pierre',
        montant: 50000,
        dateEcheance: DateTime(now.year - 1, 12, 31),
        annee: now.year - 1,
        statut: ContributionStatus.enRetard,
      ),
      ContributionModel(
        id: 'cont_004',
        membreId: 'mbr_004',
        membreNom: 'Dubois',
        membrePrenom: 'Sophie',
        montant: 50000,
        dateEcheance: DateTime(now.year, 12, 31),
        annee: now.year,
        statut: ContributionStatus.partielle,
        montantPaye: 25000,
        datePaiement: DateTime(now.year, 2, 10),
        methodePaiement: PaymentMethod.especes,
      ),
      ContributionModel(
        id: 'cont_005',
        membreId: 'mbr_005',
        membreNom: 'Petit',
        membrePrenom: 'Luc',
        montant: 50000,
        dateEcheance: DateTime(now.year, 12, 31),
        annee: now.year,
        statut: ContributionStatus.payee,
        montantPaye: 50000,
        datePaiement: DateTime(now.year, 3, 5),
        methodePaiement: PaymentMethod.mobileMoney,
      ),
    ];
  }
}

