/// Tests unitaires pour PayContribution use case
library pay_contribution_test;

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:unionflow_mobile_apps/features/contributions/domain/repositories/contribution_repository.dart';
import 'package:unionflow_mobile_apps/features/contributions/domain/usecases/pay_contribution.dart';
import 'package:unionflow_mobile_apps/features/contributions/data/models/contribution_model.dart';

@GenerateMocks([IContributionRepository])
import 'pay_contribution_test.mocks.dart';

void main() {
  late PayContribution useCase;
  late MockIContributionRepository mockRepository;

  setUp(() {
    mockRepository = MockIContributionRepository();
    useCase = PayContribution(mockRepository);
  });

  group('PayContribution Use Case', () {
    const tContributionId = 'cont123';
    const tMontant = 5000.0;
    final tDatePaiement = DateTime(2024, 11, 15);
    const tMethode = 'WAVE_MONEY';
    const tNumero = 'WAVE123456';

    final tPaidContribution = ContributionModel(
      id: tContributionId,
      membreId: 'membre1',
      montant: 5000.0,
      montantPaye: 5000.0,
      dateEcheance: DateTime(2024, 12, 31),
      datePaiement: tDatePaiement,
      annee: 2024,
      type: ContributionType.annuelle,
      statut: ContributionStatus.payee,
      methodePaiement: PaymentMethod.waveMoney,
      numeroPaiement: tNumero,
    );

    test('should record payment successfully', () async {
      // Arrange
      when(mockRepository.enregistrerPaiement(
        tContributionId,
        montant: tMontant,
        datePaiement: tDatePaiement,
        methodePaiement: tMethode,
        numeroPaiement: tNumero,
        referencePaiement: anyNamed('referencePaiement'),
      )).thenAnswer((_) async => tPaidContribution);

      // Act
      final result = await useCase(
        cotisationId: tContributionId,
        montant: tMontant,
        datePaiement: tDatePaiement,
        methodePaiement: tMethode,
        numeroPaiement: tNumero,
      );

      // Assert
      expect(result, equals(tPaidContribution));
      expect(result.statut, equals(ContributionStatus.payee));
      expect(result.montantPaye, equals(5000.0));
      expect(result.datePaiement, equals(tDatePaiement));
      verify(mockRepository.enregistrerPaiement(
        tContributionId,
        montant: tMontant,
        datePaiement: tDatePaiement,
        methodePaiement: tMethode,
        numeroPaiement: tNumero,
        referencePaiement: null,
      ));
      verifyNoMoreInteractions(mockRepository);
    });

    test('should record partial payment', () async {
      // Arrange
      const partialMontant = 2500.0;
      final partialPaid = ContributionModel(
        id: tContributionId,
        membreId: 'membre1',
        montant: 5000.0,
        montantPaye: 2500.0,
        dateEcheance: DateTime(2024, 12, 31),
        datePaiement: tDatePaiement,
        annee: 2024,
        type: ContributionType.annuelle,
        statut: ContributionStatus.partielle,
        methodePaiement: PaymentMethod.especes,
      );
      when(mockRepository.enregistrerPaiement(
        tContributionId,
        montant: partialMontant,
        datePaiement: tDatePaiement,
        methodePaiement: 'ESPECES',
        numeroPaiement: null,
        referencePaiement: null,
      )).thenAnswer((_) async => partialPaid);

      // Act
      final result = await useCase(
        cotisationId: tContributionId,
        montant: partialMontant,
        datePaiement: tDatePaiement,
        methodePaiement: 'ESPECES',
      );

      // Assert
      expect(result.statut, equals(ContributionStatus.partielle));
      expect(result.montantPaye, equals(2500.0));
    });

    test('should record payment with reference', () async {
      // Arrange
      const reference = 'REF-2024-001';
      when(mockRepository.enregistrerPaiement(
        tContributionId,
        montant: tMontant,
        datePaiement: tDatePaiement,
        methodePaiement: tMethode,
        numeroPaiement: null,
        referencePaiement: reference,
      )).thenAnswer((_) async => tPaidContribution.copyWith(referencePaiement: reference));

      // Act
      final result = await useCase(
        cotisationId: tContributionId,
        montant: tMontant,
        datePaiement: tDatePaiement,
        methodePaiement: tMethode,
        referencePaiement: reference,
      );

      // Assert
      expect(result.referencePaiement, equals(reference));
    });

    test('should throw exception when payment fails', () async {
      // Arrange
      when(mockRepository.enregistrerPaiement(
        tContributionId,
        montant: tMontant,
        datePaiement: tDatePaiement,
        methodePaiement: tMethode,
        numeroPaiement: null,
        referencePaiement: null,
      )).thenThrow(Exception('Erreur lors de l\'enregistrement du paiement'));

      // Act & Assert
      expect(
        () => useCase(
          cotisationId: tContributionId,
          montant: tMontant,
          datePaiement: tDatePaiement,
          methodePaiement: tMethode,
        ),
        throwsException,
      );
    });
  });
}
