package dev.lions.unionflow.server.service;

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

import dev.lions.unionflow.server.api.dto.cotisation.response.CotisationResponse;
import dev.lions.unionflow.server.entity.Cotisation;
import dev.lions.unionflow.server.repository.CotisationRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.OrganisationRepository;
import dev.lions.unionflow.server.service.support.SecuriteHelper;
import io.quarkus.panache.common.Page;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;

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

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

/** Tests mock pour {@link CotisationService#getStatistiquesCotisations()} — valeurs null retournées par le repository. */
@QuarkusTest
@DisplayName("CotisationService.getStatistiquesCotisations — branches null (mocks)")
class CotisationServiceMockCoverageTest {

    @Inject
    CotisationService cotisationService;

    @InjectMock
    CotisationRepository cotisationRepository;

    @InjectMock
    MembreRepository membreRepository;

    @InjectMock
    OrganisationRepository organisationRepository;

    @InjectMock
    SecuriteHelper securiteHelper;

    // =========================================================================
    // Branche montantTotalPaye == null → BigDecimal.ZERO
    // =========================================================================

    @Test
    @DisplayName("getStatistiquesCotisations — sommeMontantPayeParStatut retourne null → montantTotalPaye = ZERO")
    void getStatistiquesCotisations_montantTotalPayeNull_retourneZero() {
        when(cotisationRepository.count()).thenReturn(5L);
        when(cotisationRepository.compterParStatut("PAYEE")).thenReturn(3L);
        when(cotisationRepository.findCotisationsEnRetard(any(LocalDate.class), any(Page.class)))
                .thenReturn(Collections.emptyList());

        when(cotisationRepository.sommeMontantPayeParStatut("PAYEE")).thenReturn(null);
        when(cotisationRepository.sommeMontantDu()).thenReturn(BigDecimal.valueOf(10000));

        Map<String, Object> stats = cotisationService.getStatistiquesCotisations();

        assertThat(stats).isNotNull();
        assertThat((BigDecimal) stats.get("montantTotalPaye"))
                .isEqualByComparingTo(BigDecimal.ZERO);
        assertThat((BigDecimal) stats.get("totalMontant"))
                .isEqualByComparingTo(BigDecimal.valueOf(10000));
    }

    @Test
    @DisplayName("getStatistiquesCotisations — sommeMontantDu retourne null → totalMontant = ZERO")
    void getStatistiquesCotisations_totalMontantNull_retourneZero() {
        when(cotisationRepository.count()).thenReturn(0L);
        when(cotisationRepository.compterParStatut("PAYEE")).thenReturn(0L);
        when(cotisationRepository.findCotisationsEnRetard(any(LocalDate.class), any(Page.class)))
                .thenReturn(Collections.emptyList());

        when(cotisationRepository.sommeMontantPayeParStatut("PAYEE")).thenReturn(BigDecimal.valueOf(5000));
        when(cotisationRepository.sommeMontantDu()).thenReturn(null);

        Map<String, Object> stats = cotisationService.getStatistiquesCotisations();

        assertThat(stats).isNotNull();
        assertThat((BigDecimal) stats.get("montantTotalPaye"))
                .isEqualByComparingTo(BigDecimal.valueOf(5000));
        assertThat((BigDecimal) stats.get("totalMontant"))
                .isEqualByComparingTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("getStatistiquesCotisations — les deux montants null et totalCotisations > 0 → tauxPaiement calculé")
    void getStatistiquesCotisations_lesDeuxMontantsNullEtTotalPositif_tauxCalcule() {
        when(cotisationRepository.count()).thenReturn(10L);
        when(cotisationRepository.compterParStatut("PAYEE")).thenReturn(7L);
        when(cotisationRepository.findCotisationsEnRetard(any(LocalDate.class), any(Page.class)))
                .thenReturn(Collections.emptyList());
        when(cotisationRepository.sommeMontantPayeParStatut("PAYEE")).thenReturn(null);
        when(cotisationRepository.sommeMontantDu()).thenReturn(null);

        Map<String, Object> stats = cotisationService.getStatistiquesCotisations();

        assertThat(stats).isNotNull();
        assertThat((Double) stats.get("tauxPaiement")).isEqualTo(70.0);
        assertThat((BigDecimal) stats.get("montantTotalPaye")).isEqualByComparingTo(BigDecimal.ZERO);
        assertThat((BigDecimal) stats.get("totalMontant")).isEqualByComparingTo(BigDecimal.ZERO);
    }

    @Test
    @DisplayName("getCotisationById — membre null → membreId et nomMembre absents")
    void getCotisationById_membreNull_membreIdAbsent() {
        UUID id = UUID.randomUUID();

        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(1000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);
        // membre = null → branche L382 false

        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));

        CotisationResponse response = cotisationService.getCotisationById(id);

        assertThat(response).isNotNull();
        assertThat(response.getMembreId()).isNull();
        assertThat(response.getNomMembre()).isNull();
    }

    @Test
    @DisplayName("getCotisationById — organisation null → organisationId absent")
    void getCotisationById_organisationNull_organisationIdAbsent() {
        UUID id = UUID.randomUUID();

        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(2000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("ADHESION");
        cot.setAnnee(2026);

        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));

        CotisationResponse response = cotisationService.getCotisationById(id);

        assertThat(response).isNotNull();
        assertThat(response.getOrganisationId()).isNull();
        assertThat(response.getNomOrganisation()).isNull();
    }

    @Test
    @DisplayName("enregistrerPaiement — montantPaye = ZERO → statut inchangé")
    void enregistrerPaiement_montantPayeZero_statutInchange() {
        UUID id = UUID.randomUUID();

        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(5000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);

        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));

        CotisationResponse response = cotisationService.enregistrerPaiement(
                id, BigDecimal.ZERO, null, null, null);

        assertThat(response).isNotNull();
        assertThat(response.getStatut()).isEqualTo("EN_ATTENTE");
    }

    @Test
    @DisplayName("enregistrerPaiement — montantDu null → statut PARTIELLEMENT_PAYEE si montantPaye > 0")
    void enregistrerPaiement_montantDuNull_skipPayeeCheck() {
        UUID id = UUID.randomUUID();

        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(null);
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);

        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));

        CotisationResponse response = cotisationService.enregistrerPaiement(
                id, BigDecimal.valueOf(5000), null, null, null);

        assertThat(response).isNotNull();
        assertThat(response.getStatut()).isEqualTo("PARTIELLEMENT_PAYEE");
    }

    @Test
    @DisplayName("getCotisationById — statut non-PAYEE et non-retard → retardTexte 'À jour'")
    void getCotisationById_statutNonPayee_retardTexteAJour() {
        UUID id = UUID.randomUUID();

        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(3000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(3)); // future → pas en retard

        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));

        CotisationResponse response = cotisationService.getCotisationById(id);

        assertThat(response).isNotNull();
        // statut != "PAYEE" → "À jour"
        assertThat(response.getRetardTexte()).isEqualTo("À jour");
        assertThat(response.getRetardCouleur()).isEqualTo("text-green-600");
    }

    @Test
    @DisplayName("getCotisationById statut PARTIELLEMENT_PAYEE → statut libellé/icône/sévérité couverts (switch cases)")
    void getCotisationById_statutPartiellementPayee_couvreSwitch() {
        UUID id = UUID.randomUUID();

        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("PARTIELLEMENT_PAYEE");
        cot.setMontantDu(BigDecimal.valueOf(5000));
        cot.setMontantPaye(BigDecimal.valueOf(2000));
        cot.setTypeCotisation("ANNUELLE"); // case ANNUELLE → "Annuelle"
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(6));

        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));

        CotisationResponse response = cotisationService.getCotisationById(id);

        assertThat(response).isNotNull();
        // getStatutLibelle("PARTIELLEMENT_PAYEE") → "Partiellement payée"
        assertThat(response.getStatutLibelle()).isEqualTo("Partiellement payée");
        // getStatutIcon("PARTIELLEMENT_PAYEE") → "pi-percentage"
        assertThat(response.getStatutIcon()).isEqualTo("pi-percentage");
        // getStatutSeverity("PARTIELLEMENT_PAYEE") → "info"
        assertThat(response.getStatutSeverity()).isEqualTo("info");
        // getTypeCotisationLibelle("ANNUELLE") → "Annuelle"
        assertThat(response.getTypeCotisationLibelle()).isEqualTo("Annuelle");
    }

    @Test
    @DisplayName("getCotisationById statut ANNULEE → switch case ANNULEE couverts")
    void getCotisationById_statutAnnulee_couvreSwitch() {
        UUID id = UUID.randomUUID();

        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("ANNULEE");
        cot.setMontantDu(BigDecimal.valueOf(4000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("ADHESION"); // case ADHESION → "Adhésion"
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(1));

        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));

        CotisationResponse response = cotisationService.getCotisationById(id);

        assertThat(response).isNotNull();
        // getStatutLibelle("ANNULEE") → "Annulée"
        assertThat(response.getStatutLibelle()).isEqualTo("Annulée");
        // getStatutIcon("ANNULEE") → "pi-times"
        assertThat(response.getStatutIcon()).isEqualTo("pi-times");
        // getStatutSeverity("ANNULEE") → "secondary"
        assertThat(response.getStatutSeverity()).isEqualTo("secondary");
        // getTypeCotisationLibelle("ADHESION") → "Adhésion"
        assertThat(response.getTypeCotisationLibelle()).isEqualTo("Adhésion");
    }

    @Test
    @DisplayName("getCotisationById typeCotisation TRIMESTRIELLE, SEMESTRIELLE, EXCEPTIONNELLE → switch cases couverts")
    void getCotisationById_autresTypesCotisation_couvreSwitch() {
        UUID id1 = UUID.randomUUID();
        Cotisation cot1 = new Cotisation();
        cot1.setId(id1);
        cot1.setStatut("EN_ATTENTE");
        cot1.setMontantDu(BigDecimal.valueOf(2000));
        cot1.setMontantPaye(BigDecimal.ZERO);
        cot1.setTypeCotisation("TRIMESTRIELLE");
        cot1.setAnnee(2026);
        cot1.setDateEcheance(java.time.LocalDate.now().plusMonths(3));
        when(cotisationRepository.findByIdOptional(id1)).thenReturn(Optional.of(cot1));
        CotisationResponse r1 = cotisationService.getCotisationById(id1);
        assertThat(r1.getTypeCotisationLibelle()).isEqualTo("Trimestrielle");

        UUID id2 = UUID.randomUUID();
        Cotisation cot2 = new Cotisation();
        cot2.setId(id2);
        cot2.setStatut("EN_ATTENTE");
        cot2.setMontantDu(BigDecimal.valueOf(2000));
        cot2.setMontantPaye(BigDecimal.ZERO);
        cot2.setTypeCotisation("SEMESTRIELLE");
        cot2.setAnnee(2026);
        cot2.setDateEcheance(java.time.LocalDate.now().plusMonths(6));
        when(cotisationRepository.findByIdOptional(id2)).thenReturn(Optional.of(cot2));
        CotisationResponse r2 = cotisationService.getCotisationById(id2);
        assertThat(r2.getTypeCotisationLibelle()).isEqualTo("Semestrielle");

        UUID id3 = UUID.randomUUID();
        Cotisation cot3 = new Cotisation();
        cot3.setId(id3);
        cot3.setStatut("EN_RETARD");
        cot3.setMontantDu(BigDecimal.valueOf(3000));
        cot3.setMontantPaye(BigDecimal.ZERO);
        cot3.setTypeCotisation("EXCEPTIONNELLE");
        cot3.setAnnee(2026);
        cot3.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        when(cotisationRepository.findByIdOptional(id3)).thenReturn(Optional.of(cot3));
        CotisationResponse r3 = cotisationService.getCotisationById(id3);
        assertThat(r3.getTypeCotisationLibelle()).isEqualTo("Exceptionnelle");
        // getStatutLibelle("EN_RETARD") → "En retard"
        assertThat(r3.getStatutLibelle()).isEqualTo("En retard");
        // getStatutIcon("EN_RETARD") → "pi-exclamation-triangle"
        assertThat(r3.getStatutIcon()).isEqualTo("pi-exclamation-triangle");
        // getStatutSeverity("EN_RETARD") → "error"
        assertThat(r3.getStatutSeverity()).isEqualTo("error");
    }

    @Test
    @DisplayName("getCotisationById typeCotisation 'INCONNUE' → default switch case couverts")
    void getCotisationById_typeCotisationInconnu_defaultSwitch() {
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("STATUT_INCONNU");
        cot.setMontantDu(BigDecimal.valueOf(1000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("TYPE_INCONNU"); // default case
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r.getTypeCotisationLibelle()).isEqualTo("TYPE_INCONNU"); // default → code
        assertThat(r.getStatutLibelle()).isEqualTo("STATUT_INCONNU"); // default → code
    }

    @Test
    @DisplayName("getCotisationById avec membre SUSPENDU/RADIE/INACTIF → getTypeMembreLibelle switch couverts")
    void getCotisationById_membreStatutSuspenduRadieInactif_couvreSwitch() {
        // SUSPENDU
        UUID id1 = UUID.randomUUID();
        Cotisation cot1 = new Cotisation();
        cot1.setId(id1);
        cot1.setStatut("EN_ATTENTE");
        cot1.setMontantDu(BigDecimal.valueOf(1000));
        cot1.setMontantPaye(BigDecimal.ZERO);
        cot1.setTypeCotisation("MENSUELLE");
        cot1.setAnnee(2026);
        cot1.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Membre m1 = new dev.lions.unionflow.server.entity.Membre();
        m1.setId(UUID.randomUUID());
        m1.setStatutCompte("SUSPENDU");
        cot1.setMembre(m1);
        when(cotisationRepository.findByIdOptional(id1)).thenReturn(Optional.of(cot1));
        CotisationResponse r1 = cotisationService.getCotisationById(id1);
        assertThat(r1.getTypeMembre()).isEqualTo("Suspendu");

        // RADIE
        UUID id2 = UUID.randomUUID();
        Cotisation cot2 = new Cotisation();
        cot2.setId(id2);
        cot2.setStatut("EN_ATTENTE");
        cot2.setMontantDu(BigDecimal.valueOf(1000));
        cot2.setMontantPaye(BigDecimal.ZERO);
        cot2.setTypeCotisation("MENSUELLE");
        cot2.setAnnee(2026);
        cot2.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Membre m2 = new dev.lions.unionflow.server.entity.Membre();
        m2.setId(UUID.randomUUID());
        m2.setStatutCompte("RADIE");
        cot2.setMembre(m2);
        when(cotisationRepository.findByIdOptional(id2)).thenReturn(Optional.of(cot2));
        CotisationResponse r2 = cotisationService.getCotisationById(id2);
        assertThat(r2.getTypeMembre()).isEqualTo("Radié");

        // INACTIF
        UUID id3 = UUID.randomUUID();
        Cotisation cot3 = new Cotisation();
        cot3.setId(id3);
        cot3.setStatut("EN_ATTENTE");
        cot3.setMontantDu(BigDecimal.valueOf(1000));
        cot3.setMontantPaye(BigDecimal.ZERO);
        cot3.setTypeCotisation("MENSUELLE");
        cot3.setAnnee(2026);
        cot3.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Membre m3 = new dev.lions.unionflow.server.entity.Membre();
        m3.setId(UUID.randomUUID());
        m3.setStatutCompte("INACTIF");
        cot3.setMembre(m3);
        when(cotisationRepository.findByIdOptional(id3)).thenReturn(Optional.of(cot3));
        CotisationResponse r3 = cotisationService.getCotisationById(id3);
        assertThat(r3.getTypeMembre()).isEqualTo("Inactif");
    }

    @Test
    @DisplayName("getCotisationById avec organisation COOPERATIVE/ONG → getIconeOrganisation switch couverts")
    void getCotisationById_orgCooperativeOng_couvreSwitch() {
        // ONG
        UUID id1 = UUID.randomUUID();
        Cotisation cot1 = new Cotisation();
        cot1.setId(id1);
        cot1.setStatut("EN_ATTENTE");
        cot1.setMontantDu(BigDecimal.valueOf(1000));
        cot1.setMontantPaye(BigDecimal.ZERO);
        cot1.setTypeCotisation("MENSUELLE");
        cot1.setAnnee(2026);
        cot1.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Organisation org1 = new dev.lions.unionflow.server.entity.Organisation();
        org1.setId(UUID.randomUUID());
        org1.setNom("ONG Test");
        org1.setTypeOrganisation("ONG");
        cot1.setOrganisation(org1);
        when(cotisationRepository.findByIdOptional(id1)).thenReturn(Optional.of(cot1));
        CotisationResponse r1 = cotisationService.getCotisationById(id1);
        assertThat(r1.getIconeOrganisation()).isEqualTo("pi-users"); // ONG → pi-users

        // COOPERATIVE
        UUID id2 = UUID.randomUUID();
        Cotisation cot2 = new Cotisation();
        cot2.setId(id2);
        cot2.setStatut("EN_ATTENTE");
        cot2.setMontantDu(BigDecimal.valueOf(1000));
        cot2.setMontantPaye(BigDecimal.ZERO);
        cot2.setTypeCotisation("MENSUELLE");
        cot2.setAnnee(2026);
        cot2.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Organisation org2 = new dev.lions.unionflow.server.entity.Organisation();
        org2.setId(UUID.randomUUID());
        org2.setNom("Coop Test");
        org2.setTypeOrganisation("COOPERATIVE");
        cot2.setOrganisation(org2);
        when(cotisationRepository.findByIdOptional(id2)).thenReturn(Optional.of(cot2));
        CotisationResponse r2 = cotisationService.getCotisationById(id2);
        assertThat(r2.getIconeOrganisation()).isEqualTo("pi-briefcase"); // COOPERATIVE → pi-briefcase
    }

    @Test
    @DisplayName("getCotisationById avec typeCotisation SEMESTRIELLE → getTypeCotisationSeverity/Icon switch couverts")
    void getCotisationById_typeSemestrielle_severityIconSwitch() {
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(2000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("SEMESTRIELLE"); // SEMESTRIELLE is not in severity/icon switch → default
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(6));
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r.getTypeSeverity()).isEqualTo("secondary"); // default
        assertThat(r.getTypeIcon()).isEqualTo("pi-tag"); // default
    }

    @Test
    @DisplayName("buildInitiales — prenom null mais nom non-null → initiales du nom seul")
    void getCotisationById_prenomNull_nomNonNull_initialeNomSeul() {
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(1000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Membre m = new dev.lions.unionflow.server.entity.Membre();
        m.setId(UUID.randomUUID());
        m.setPrenom(null); // prenom null → L523: p = ""
        m.setNom("Kouakou"); // nom non-null → L524: n = "K"
        cot.setMembre(m);
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r).isNotNull();
        // p="" + n="K" → "K"
        assertThat(r.getInitialesMembre()).isEqualTo("K");
    }

    @Test
    @DisplayName("buildInitiales — prenom non-null mais nom null → initiales du prenom seul")
    void getCotisationById_prenomNonNull_nomNull_initialePrenomSeul() {
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(1000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Membre m = new dev.lions.unionflow.server.entity.Membre();
        m.setId(UUID.randomUUID());
        m.setPrenom("Aya"); // prenom non-null → L523: p = "A"
        m.setNom(null); // nom null → L524: n = ""
        cot.setMembre(m);
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r).isNotNull();
        // p="A" + n="" → "A"
        assertThat(r.getInitialesMembre()).isEqualTo("A");
    }

    @Test
    @DisplayName("buildInitiales — prenom vide et nom vide → retourne '—'")
    void getCotisationById_prenomVide_nomVide_retourneTiret() {
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(1000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Membre m = new dev.lions.unionflow.server.entity.Membre();
        m.setId(UUID.randomUUID());
        m.setPrenom(""); // prenom vide → L523: isEmpty=true → p = ""
        m.setNom(""); // nom vide → L524: isEmpty=true → n = ""
        cot.setMembre(m);
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r).isNotNull();
        // p="" + n="" → isEmpty → "—"
        assertThat(r.getInitialesMembre()).isEqualTo("—");
    }

    @Test
    @DisplayName("getCotisationById typeCotisation ADHESION → getTypeCotisationSeverity/Icon cases couverts")
    void getCotisationById_typeAdhesion_severityIconSwitch() {
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(2000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("ADHESION"); // ANNUELLE,ADHESION → success; MENSUELLE,TRIMESTRIELLE → info
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r.getTypeSeverity()).isEqualTo("success"); // ADHESION → success
        assertThat(r.getTypeIcon()).isEqualTo("pi-user-plus"); // ADHESION → pi-user-plus
    }

    @Test
    @DisplayName("getCotisationById typeCotisation ANNUELLE → getTypeCotisationIcon case pi-star, TRIMESTRIELLE→info couverts")
    void getCotisationById_typeAnnuelle_iconEtTrimestrielle_info() {
        // ANNUELLE → getTypeCotisationIcon = pi-star
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(2000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("ANNUELLE");
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(12));
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r.getTypeIcon()).isEqualTo("pi-star"); // ANNUELLE → pi-star
        assertThat(r.getTypeSeverity()).isEqualTo("success"); // ANNUELLE → success

        // TRIMESTRIELLE → info
        UUID id2 = UUID.randomUUID();
        Cotisation cot2 = new Cotisation();
        cot2.setId(id2);
        cot2.setStatut("EN_ATTENTE");
        cot2.setMontantDu(BigDecimal.valueOf(2000));
        cot2.setMontantPaye(BigDecimal.ZERO);
        cot2.setTypeCotisation("TRIMESTRIELLE");
        cot2.setAnnee(2026);
        cot2.setDateEcheance(java.time.LocalDate.now().plusMonths(3));
        when(cotisationRepository.findByIdOptional(id2)).thenReturn(Optional.of(cot2));
        CotisationResponse r2 = cotisationService.getCotisationById(id2);
        assertThat(r2.getTypeSeverity()).isEqualTo("info"); // TRIMESTRIELLE → info
    }

    @Test
    @DisplayName("getCotisationById organisation DEFAULT type → getIconeOrganisation pi-building default couverts")
    void getCotisationById_orgDefaultType_couvreDefault() {
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(1000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Organisation org = new dev.lions.unionflow.server.entity.Organisation();
        org.setId(UUID.randomUUID());
        org.setNom("Org Inconnue");
        org.setTypeOrganisation("AUTRE_TYPE"); // default case → pi-building
        cot.setOrganisation(org);
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r.getIconeOrganisation()).isEqualTo("pi-building");
    }

    @Test
    @DisplayName("getCotisationById membre EN_ATTENTE_VALIDATION → getTypeMembreLibelle case couverts")
    void getCotisationById_membreEnAttenteValidation_couvreSwitch() {
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(1000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Membre m = new dev.lions.unionflow.server.entity.Membre();
        m.setId(UUID.randomUUID());
        m.setStatutCompte("EN_ATTENTE_VALIDATION");
        cot.setMembre(m);
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r.getTypeMembre()).isEqualTo("En attente");
    }

    @Test
    @DisplayName("getCotisationById membre avec statusCompte inconnu → default case de getTypeMembreLibelle")
    void getCotisationById_membreStatutInconnu_defaultCase() {
        UUID id = UUID.randomUUID();
        Cotisation cot = new Cotisation();
        cot.setId(id);
        cot.setStatut("EN_ATTENTE");
        cot.setMontantDu(BigDecimal.valueOf(1000));
        cot.setMontantPaye(BigDecimal.ZERO);
        cot.setTypeCotisation("MENSUELLE");
        cot.setAnnee(2026);
        cot.setDateEcheance(java.time.LocalDate.now().plusMonths(1));
        dev.lions.unionflow.server.entity.Membre m = new dev.lions.unionflow.server.entity.Membre();
        m.setId(UUID.randomUUID());
        m.setStatutCompte("STATUT_INCONNU"); // default → retourne le code
        cot.setMembre(m);
        when(cotisationRepository.findByIdOptional(id)).thenReturn(Optional.of(cot));
        CotisationResponse r = cotisationService.getCotisationById(id);
        assertThat(r.getTypeMembre()).isEqualTo("STATUT_INCONNU");
    }
}
