package dev.lions.unionflow.server.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

import dev.lions.unionflow.server.api.dto.dashboard.MembreDashboardSyntheseResponse;
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
import dev.lions.unionflow.server.entity.DemandeAide;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.repository.CotisationRepository;
import dev.lions.unionflow.server.repository.DemandeAideRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.mutuelle.epargne.CompteEpargneRepository;
import dev.lions.unionflow.server.service.support.SecuriteHelper;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

@QuarkusTest
class MembreDashboardServiceTest {

    @Inject
    MembreDashboardService service;

    @InjectMock
    SecuriteHelper securiteHelper;

    @InjectMock
    MembreRepository membreRepository;

    @InjectMock
    CotisationRepository cotisationRepository;

    @InjectMock
    CompteEpargneRepository compteEpargneRepository;

    @InjectMock
    DemandeAideRepository demandeAideRepository;

    // -------------------------------------------------------------------------
    // Helper
    // -------------------------------------------------------------------------

    private Membre buildMembre(boolean actif) {
        Membre m = new Membre();
        m.setId(UUID.randomUUID());
        m.setPrenom("Alice");
        m.setNom("Dupont");
        m.setEmail("alice@test.com");
        m.setActif(actif);
        m.setDateCreation(LocalDateTime.of(2024, 1, 15, 10, 0));
        return m;
    }

    private void stubbingCotisationsBase(UUID membreId,
                                          BigDecimal paiementsMois,
                                          long enRetardCount,
                                          BigDecimal totalDu,
                                          BigDecimal totalPaye,
                                          BigDecimal totalToutTemps,
                                          long countAll,
                                          long countPayees) {
        when(cotisationRepository.calculerTotalCotisationsPayeesCeMois(membreId))
                .thenReturn(paiementsMois);
        when(cotisationRepository.countRetardByMembreId(membreId)).thenReturn(enRetardCount);
        when(cotisationRepository.calculerTotalCotisationsAnneeEnCours(membreId))
                .thenReturn(totalDu);
        when(cotisationRepository.calculerTotalCotisationsPayeesAnneeEnCours(membreId))
                .thenReturn(totalPaye);
        when(cotisationRepository.calculerTotalCotisationsPayeesToutTemps(membreId))
                .thenReturn(totalToutTemps);
        when(cotisationRepository.countByMembreId(membreId)).thenReturn(countAll);
        when(cotisationRepository.countPayeesByMembreId(membreId)).thenReturn(countPayees);
    }

    // -------------------------------------------------------------------------
    // emailNull / blank → NotFoundException
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getDashboardData - email null → NotFoundException")
    void getDashboardData_emailNull_throws() {
        when(securiteHelper.resolveEmail()).thenReturn(null);

        assertThatThrownBy(() -> service.getDashboardData())
                .isInstanceOf(NotFoundException.class)
                .hasMessageContaining("Identité non disponible");
    }

    @Test
    @DisplayName("getDashboardData - email blank → NotFoundException")
    void getDashboardData_emailBlank_throws() {
        when(securiteHelper.resolveEmail()).thenReturn("   ");

        assertThatThrownBy(() -> service.getDashboardData())
                .isInstanceOf(NotFoundException.class)
                .hasMessageContaining("Identité non disponible");
    }

    // -------------------------------------------------------------------------
    // Membre introuvable
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getDashboardData - membre absent en base → NotFoundException")
    void getDashboardData_membreInexistant_throws() {
        when(securiteHelper.resolveEmail()).thenReturn("inconnu@test.com");
        when(membreRepository.findByEmail(anyString())).thenReturn(Optional.empty());

        assertThatThrownBy(() -> service.getDashboardData())
                .isInstanceOf(NotFoundException.class)
                .hasMessageContaining("inconnu@test.com");
    }

    @Test
    @DisplayName("getDashboardData - membre inactif → NotFoundException")
    void getDashboardData_membreInactif_throws() {
        Membre inactif = buildMembre(false);
        inactif.setActif(false);
        when(securiteHelper.resolveEmail()).thenReturn("inactif@test.com");
        when(membreRepository.findByEmail("inactif@test.com")).thenReturn(Optional.of(inactif));
        when(membreRepository.findByEmail("inactif@test.com")).thenReturn(Optional.of(inactif));

        assertThatThrownBy(() -> service.getDashboardData())
                .isInstanceOf(NotFoundException.class);
    }

    // -------------------------------------------------------------------------
    // Happy path - cotisations normales avec totalAnneeDu > 0
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getDashboardData - happy path - cotisations normales (taux calculé sur l'année)")
    void getDashboardData_happyPath_cotisationsNormales() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                new BigDecimal("5000"),   // paiementsMois
                0L,                        // enRetardCount → "À jour"
                new BigDecimal("12000"),   // totalAnneeDu
                new BigDecimal("6000"),    // totalAnneePaye
                new BigDecimal("20000"),   // totalToutTemps
                10L,                       // countAll
                5L                         // countPayees
        );

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(new BigDecimal("50000"));
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(Collections.emptyList());

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result).isNotNull();
        assertThat(result.getPrenom()).isEqualTo("Alice");
        assertThat(result.getNom()).isEqualTo("Dupont");
        assertThat(result.getDateInscription()).isEqualTo(LocalDate.of(2024, 1, 15));
        assertThat(result.getStatutCotisations()).isEqualTo("À jour");
        assertThat(result.getTauxCotisationsPerso()).isEqualTo(50); // 6000/12000*100
        assertThat(result.getMesCotisationsPaiement()).isEqualByComparingTo("5000");
        assertThat(result.getMonSoldeEpargne()).isEqualByComparingTo("50000");
        assertThat(result.getMesDemandesAide()).isEqualTo(0);
        assertThat(result.getAidesEnCours()).isEqualTo(0);
        assertThat(result.getTauxAidesApprouvees()).isNull();
    }

    @Test
    @DisplayName("getDashboardData - statut En retard quand enRetardCount > 0")
    void getDashboardData_enRetard_statutEnRetard() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                BigDecimal.ZERO,           // paiementsMois
                2L,                        // enRetardCount > 0 → "En retard"
                new BigDecimal("12000"),   // totalAnneeDu
                new BigDecimal("3000"),    // totalAnneePaye
                new BigDecimal("5000"),    // totalToutTemps
                10L,
                3L
        );

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(Collections.emptyList());

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getStatutCotisations()).isEqualTo("En retard");
        assertThat(result.getTauxCotisationsPerso()).isEqualTo(25); // 3000/12000*100
    }

    // -------------------------------------------------------------------------
    // Fallback: totalAnneeDu == 0 → branches du else
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getDashboardData - fallback (aucune cotisation en cours) → tauxCotisations null")
    void getDashboardData_fallback_aucuneCotisation() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                BigDecimal.ZERO,
                0L,
                null,   // totalAnneeDu null → fallback
                null,
                BigDecimal.ZERO,
                0L,     // totalToutesAnneesCount = 0 → taux null
                0L
        );

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(Collections.emptyList());

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getTauxCotisationsPerso()).isNull();
    }

    @Test
    @DisplayName("getDashboardData - fallback avec cotisations en retard → taux partiel")
    void getDashboardData_fallback_avecRetard_tauxPartiel() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        when(cotisationRepository.calculerTotalCotisationsPayeesCeMois(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(cotisationRepository.countRetardByMembreId(membreId)).thenReturn(1L);
        when(cotisationRepository.calculerTotalCotisationsAnneeEnCours(membreId))
                .thenReturn(null); // totalAnneeDu null → fallback
        when(cotisationRepository.calculerTotalCotisationsPayeesAnneeEnCours(membreId))
                .thenReturn(null);
        when(cotisationRepository.calculerTotalCotisationsPayeesToutTemps(membreId))
                .thenReturn(new BigDecimal("2000"));
        when(cotisationRepository.countByMembreId(membreId)).thenReturn(4L);
        when(cotisationRepository.countPayeesByMembreId(membreId)).thenReturn(2L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(Collections.emptyList());

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        // 2 payées / 4 totales = 50%
        assertThat(result.getTauxCotisationsPerso()).isEqualTo(50);
    }

    @Test
    @DisplayName("getDashboardData - fallback sans retard → taux 100%")
    void getDashboardData_fallback_sansRetard_taux100() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        when(cotisationRepository.calculerTotalCotisationsPayeesCeMois(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(cotisationRepository.countRetardByMembreId(membreId)).thenReturn(0L);
        when(cotisationRepository.calculerTotalCotisationsAnneeEnCours(membreId))
                .thenReturn(null); // fallback
        when(cotisationRepository.calculerTotalCotisationsPayeesAnneeEnCours(membreId))
                .thenReturn(null);
        when(cotisationRepository.calculerTotalCotisationsPayeesToutTemps(membreId))
                .thenReturn(new BigDecimal("3000"));
        when(cotisationRepository.countByMembreId(membreId)).thenReturn(3L);
        when(cotisationRepository.countPayeesByMembreId(membreId)).thenReturn(3L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(new BigDecimal("10000"));
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(Collections.emptyList());

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getTauxCotisationsPerso()).isEqualTo(100);
    }

    // -------------------------------------------------------------------------
    // Aides
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getDashboardData - demandes d'aide avec approbations")
    void getDashboardData_avecDemandes_tauxAidesCalcule() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                new BigDecimal("1000"), 0L, new BigDecimal("5000"),
                new BigDecimal("5000"), new BigDecimal("10000"), 5L, 5L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);

        DemandeAide approuvee = new DemandeAide();
        approuvee.setStatut(StatutAide.APPROUVEE);

        DemandeAide enCours = new DemandeAide();
        enCours.setStatut(StatutAide.EN_COURS_EVALUATION);

        DemandeAide rejetee = new DemandeAide();
        rejetee.setStatut(StatutAide.REJETEE);

        when(demandeAideRepository.findByDemandeurId(membreId))
                .thenReturn(List.of(approuvee, enCours, rejetee));

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getMesDemandesAide()).isEqualTo(3);
        assertThat(result.getAidesEnCours()).isEqualTo(1); // seulement EN_COURS_EXAMEN
        assertThat(result.getTauxAidesApprouvees()).isEqualTo(33); // 1/3 * 100
    }

    @Test
    @DisplayName("getDashboardData - demande avec statut null ne bloque pas")
    void getDashboardData_demande_statutNull() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                BigDecimal.ZERO, 0L, new BigDecimal("1000"),
                new BigDecimal("500"), new BigDecimal("500"), 1L, 1L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);

        DemandeAide avecStatutNull = new DemandeAide();
        avecStatutNull.setStatut(null);

        when(demandeAideRepository.findByDemandeurId(membreId))
                .thenReturn(List.of(avecStatutNull));

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getMesDemandesAide()).isEqualTo(1);
        // statut null → exclut du filtre aidesEnCours (condition: d.getStatut() != null && ...)
        assertThat(result.getAidesEnCours()).isEqualTo(0);
        assertThat(result.getTauxAidesApprouvees()).isEqualTo(0);
    }

    // -------------------------------------------------------------------------
    // Données nulles des repositories
    // -------------------------------------------------------------------------

    @Test
    @DisplayName("getDashboardData - paiementsMois null → remplacé par ZERO")
    void getDashboardData_paiementsMoisNull_treatedAsZero() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                null,   // paiementsMois null
                0L,
                new BigDecimal("1000"),
                new BigDecimal("500"),
                null,   // totalToutTemps null
                1L,
                1L
        );

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(Collections.emptyList());

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getMesCotisationsPaiement()).isEqualByComparingTo(BigDecimal.ZERO);
        assertThat(result.getTotalCotisationsPayeesToutTemps()).isEqualByComparingTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("getDashboardData - membre sans dateCreation → dateInscription null")
    void getDashboardData_sansDates_dateInscriptionNull() {
        Membre membre = buildMembre(true);
        membre.setDateCreation(null);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                BigDecimal.ZERO, 0L, BigDecimal.ZERO, BigDecimal.ZERO,
                BigDecimal.ZERO, 0L, 0L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(Collections.emptyList());

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getDateInscription()).isNull();
    }

    @Test
    @DisplayName("getDashboardData - membre actif null (traité comme actif)")
    void getDashboardData_actifNull_treatedAsActif() {
        Membre membre = buildMembre(true);
        membre.setActif(null); // actif null → filtre passe (actif == null || actif)
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                BigDecimal.ZERO, 0L, BigDecimal.ZERO, BigDecimal.ZERO,
                BigDecimal.ZERO, 0L, 0L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(Collections.emptyList());

        // Membre avec actif=null doit passer le filtre
        MembreDashboardSyntheseResponse result = service.getDashboardData();
        assertThat(result).isNotNull();
    }

    @Test
    @DisplayName("getDashboardData - totalAnneePaye null avec totalAnneeDu > 0 → traité comme ZERO")
    void getDashboardData_totalAnneePayeNull_treatedAsZero() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        when(cotisationRepository.calculerTotalCotisationsPayeesCeMois(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(cotisationRepository.countRetardByMembreId(membreId)).thenReturn(0L);
        when(cotisationRepository.calculerTotalCotisationsAnneeEnCours(membreId))
                .thenReturn(new BigDecimal("10000")); // > 0
        when(cotisationRepository.calculerTotalCotisationsPayeesAnneeEnCours(membreId))
                .thenReturn(null); // null → ZERO
        when(cotisationRepository.calculerTotalCotisationsPayeesToutTemps(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(cotisationRepository.countByMembreId(membreId)).thenReturn(5L);
        when(cotisationRepository.countPayeesByMembreId(membreId)).thenReturn(0L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(Collections.emptyList());

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getTauxCotisationsPerso()).isEqualTo(0);
        assertThat(result.getTotalCotisationsPayeesAnnee()).isEqualByComparingTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("getDashboardData - demande ANNULEE n'est pas comptée dans aidesEnCours (branche != ANNULEE false)")
    void getDashboardData_demandeAnnulee_nonComptee() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                BigDecimal.ZERO, 0L, new BigDecimal("1000"),
                new BigDecimal("1000"), new BigDecimal("1000"), 1L, 1L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId))
                .thenReturn(BigDecimal.ZERO);

        DemandeAide annulee = new DemandeAide();
        annulee.setStatut(StatutAide.ANNULEE); // != ANNULEE → false → exclu

        when(demandeAideRepository.findByDemandeurId(membreId))
                .thenReturn(List.of(annulee));

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getMesDemandesAide()).isEqualTo(1);
        assertThat(result.getAidesEnCours()).isEqualTo(0); // ANNULEE exclue
    }

    @Test
    @DisplayName("getDashboardData - demande EN_COURS_EVALUATION non APPROUVEE/REJETEE/ANNULEE → comptée dans aidesEnCours (L129 lambda branche)")
    void getDashboardData_demandeEnCoursEvaluation_compteeAidesEnCours() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                BigDecimal.ZERO, 0L, new BigDecimal("1000"),
                new BigDecimal("1000"), new BigDecimal("1000"), 1L, 1L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId)).thenReturn(BigDecimal.ZERO);

        // Demande EN_COURS_EVALUATION : statut != null, != APPROUVEE, != REJETEE, != ANNULEE → comptée
        DemandeAide enCoursEval = new DemandeAide();
        enCoursEval.setStatut(StatutAide.EN_COURS_EVALUATION);

        // Demande REJETEE : exclue de aidesEnCours (statut != REJETEE = false)
        DemandeAide rejetee = new DemandeAide();
        rejetee.setStatut(StatutAide.REJETEE);

        when(demandeAideRepository.findByDemandeurId(membreId))
                .thenReturn(List.of(enCoursEval, rejetee));

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        // EN_COURS_EVALUATION comptée + REJETEE non comptée → aidesEnCours = 1
        assertThat(result.getAidesEnCours()).isEqualTo(1);
        assertThat(result.getMesDemandesAide()).isEqualTo(2);
        // 0 APPROUVEE / 2 total → tauxAidesApprouvees = 0
        assertThat(result.getTauxAidesApprouvees()).isEqualTo(0);
    }

    @Test
    @DisplayName("getDashboardData - demandeAideRepository retourne null → demandes traitées comme 0")
    void getDashboardData_demandesNull_returnsZero() {
        Membre membre = buildMembre(true);
        UUID membreId = membre.getId();

        when(securiteHelper.resolveEmail()).thenReturn("alice@test.com");
        when(membreRepository.findByEmail("alice@test.com")).thenReturn(Optional.of(membre));

        stubbingCotisationsBase(membreId,
                BigDecimal.ZERO, 0L, BigDecimal.ZERO, BigDecimal.ZERO,
                BigDecimal.ZERO, 0L, 0L);

        when(compteEpargneRepository.sumSoldeActuelByMembreId(membreId)).thenReturn(BigDecimal.ZERO);
        // null au lieu de liste vide → couvre la branche demandes == null
        when(demandeAideRepository.findByDemandeurId(membreId)).thenReturn(null);

        MembreDashboardSyntheseResponse result = service.getDashboardData();

        assertThat(result.getMesDemandesAide()).isEqualTo(0);
        assertThat(result.getAidesEnCours()).isEqualTo(0);
        assertThat(result.getTauxAidesApprouvees()).isNull();
    }
}
