package dev.lions.unionflow.server.service;

import static org.assertj.core.api.Assertions.assertThat;

import dev.lions.unionflow.server.api.dto.membre.CompteAdherentResponse;
import dev.lions.unionflow.server.api.enums.membre.StatutMembre;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.MembreOrganisation;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.repository.CotisationRepository;
import dev.lions.unionflow.server.repository.MembreRepository;
import dev.lions.unionflow.server.repository.mutuelle.credit.DemandeCreditRepository;
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 org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

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

/**
 * Tests complémentaires pour {@link CompteAdherentService#getMonCompte()} —
 * cas limites du filtre sur les {@link dev.lions.unionflow.server.entity.MembreOrganisation} (null, organisation null).
 */
@QuarkusTest
@DisplayName("CompteAdherentService — lambda$buildCompteAdherent$3 branches manquantes (mo null, getOrg null)")
class CompteAdherentServiceLambdaCoverageTest {

    @Inject
    CompteAdherentService service;

    @InjectMock
    SecuriteHelper securiteHelper;

    @InjectMock
    MembreRepository membreRepository;

    @InjectMock
    CotisationRepository cotisationRepository;

    @InjectMock
    CompteEpargneRepository compteEpargneRepository;

    @InjectMock
    DemandeCreditRepository demandeCreditRepository;

    private final UUID TEST_MEMBRE_ID = UUID.randomUUID();
    private final String TEST_EMAIL = "lambda3.coverage@unionflow.test";

    @BeforeEach
    void setup() {
        Mockito.when(securiteHelper.resolveEmail()).thenReturn(TEST_EMAIL);

        // Mocks financiers par défaut pour éviter NPE
        Mockito.when(cotisationRepository.calculerTotalCotisationsPayeesToutTemps(TEST_MEMBRE_ID))
                .thenReturn(BigDecimal.ZERO);
        Mockito.when(cotisationRepository.countPayeesByMembreId(TEST_MEMBRE_ID)).thenReturn(0L);
        Mockito.when(cotisationRepository.countByMembreId(TEST_MEMBRE_ID)).thenReturn(0L);
        Mockito.when(cotisationRepository.countRetardByMembreId(TEST_MEMBRE_ID)).thenReturn(0L);
        Mockito.when(compteEpargneRepository.sumSoldeActuelByMembreId(TEST_MEMBRE_ID))
                .thenReturn(BigDecimal.ZERO);
        Mockito.when(compteEpargneRepository.sumSoldeBloqueByMembreId(TEST_MEMBRE_ID))
                .thenReturn(BigDecimal.ZERO);
        Mockito.when(compteEpargneRepository.countActifsByMembreId(TEST_MEMBRE_ID)).thenReturn(0L);
        Mockito.when(demandeCreditRepository.calculerTotalEncoursParMembre(TEST_MEMBRE_ID))
                .thenReturn(BigDecimal.ZERO);
    }

    private Membre buildMembre() {
        Membre m = new Membre();
        m.setId(TEST_MEMBRE_ID);
        m.setNom("Test");
        m.setPrenom("Lambda3");
        m.setEmail(TEST_EMAIL);
        m.setNumeroMembre("MBR-L3-001");
        m.setActif(true);
        return m;
    }

    @Test
    @DisplayName("lambda$3 — mo == null dans la liste → filtré")
    void buildCompteAdherent_moNull_filtreParLambda3() {
        Membre m = buildMembre();

        List<MembreOrganisation> listeAvecNull = new ArrayList<>();
        listeAvecNull.add(null);

        m.setMembresOrganisations(listeAvecNull);
        Mockito.when(membreRepository.findByEmail(TEST_EMAIL)).thenReturn(Optional.of(m));

        CompteAdherentResponse response = service.getMonCompte();

        // Le mo null a été filtré → orgNom = null (aucun mo valide)
        assertThat(response).isNotNull();
        assertThat(response.getOrganisationNom()).isNull();
    }

    @Test
    @DisplayName("lambda$3 — mo.getOrganisation()==null → filtré")
    void buildCompteAdherent_moGetOrganisationNull_filtreParLambda3() {
        Membre m = buildMembre();

        MembreOrganisation moSansOrg = new MembreOrganisation();
        moSansOrg.setOrganisation(null);  // organisation null → deuxième condition false
        moSansOrg.setStatutMembre(StatutMembre.ACTIF);
        moSansOrg.setActif(true);
        moSansOrg.setDateAdhesion(LocalDate.of(2023, 1, 1));

        m.setMembresOrganisations(List.of(moSansOrg));
        Mockito.when(membreRepository.findByEmail(TEST_EMAIL)).thenReturn(Optional.of(m));

        CompteAdherentResponse response = service.getMonCompte();

        // Le mo avec org null a été filtré → orgNom = null (aucun mo avec org valide)
        assertThat(response).isNotNull();
        assertThat(response.getOrganisationNom()).isNull();
    }

    @Test
    @DisplayName("lambda$3 — liste mixte : null + sans org + avec org → seul le valide sélectionné")
    void buildCompteAdherent_listeMixte_seulLeMoValidePasseLeFiltre() {
        Membre m = buildMembre();

        MembreOrganisation moSansOrg = new MembreOrganisation();
        moSansOrg.setOrganisation(null);
        moSansOrg.setStatutMembre(StatutMembre.ACTIF);
        moSansOrg.setActif(true);
        moSansOrg.setDateAdhesion(LocalDate.of(2020, 1, 1));

        Organisation org = new Organisation();
        org.setNom("Organisation Lambda3 Test");

        MembreOrganisation moAvecOrg = new MembreOrganisation();
        moAvecOrg.setOrganisation(org);
        moAvecOrg.setStatutMembre(StatutMembre.ACTIF);
        moAvecOrg.setActif(true);
        moAvecOrg.setDateAdhesion(LocalDate.of(2024, 6, 1));

        List<MembreOrganisation> liste = new ArrayList<>();
        liste.add(null);
        liste.add(moSansOrg);
        liste.add(moAvecOrg);

        m.setMembresOrganisations(liste);
        Mockito.when(membreRepository.findByEmail(TEST_EMAIL)).thenReturn(Optional.of(m));

        CompteAdherentResponse response = service.getMonCompte();

        // Seul moAvecOrg passe le filtre lambda$3 → orgNom = "Organisation Lambda3 Test"
        assertThat(response).isNotNull();
        assertThat(response.getOrganisationNom()).isEqualTo("Organisation Lambda3 Test");
    }

    @Test
    @DisplayName("buildCompteAdherent — prenom et nom null → nomComplet vide")
    void buildCompteAdherent_prenomEtNomNull_nomCompletVide() {
        Membre m = buildMembre();
        m.setPrenom(null);
        m.setNom(null);
        m.setMembresOrganisations(List.of());

        Mockito.when(membreRepository.findByEmail(TEST_EMAIL)).thenReturn(Optional.of(m));

        CompteAdherentResponse response = service.getMonCompte();

        assertThat(response).isNotNull();
        assertThat(response.getNomComplet()).isEmpty();
    }

    @Test
    @DisplayName("buildCompteAdherent — prenom null mais nom non null → nomComplet = nom seul")
    void buildCompteAdherent_prenomNull_nomNonNull_branchesFalseEtTrue() {
        Membre m = buildMembre();
        m.setPrenom(null);
        m.setNom("Diallo");
        m.setMembresOrganisations(List.of());

        Mockito.when(membreRepository.findByEmail(TEST_EMAIL)).thenReturn(Optional.of(m));

        CompteAdherentResponse response = service.getMonCompte();

        assertThat(response).isNotNull();
        assertThat(response.getNomComplet()).isEqualTo("Diallo");
    }

    @Test
    @DisplayName("buildCompteAdherent — dateCreation non null → dateAdhesion non null")
    void buildCompteAdherent_dateCreationNonNull_dateAdhesionNonNull() {
        Membre m = buildMembre();
        m.setDateCreation(java.time.LocalDateTime.of(2022, 3, 15, 10, 0));
        m.setMembresOrganisations(List.of());

        Mockito.when(membreRepository.findByEmail(TEST_EMAIL)).thenReturn(Optional.of(m));

        CompteAdherentResponse response = service.getMonCompte();

        assertThat(response).isNotNull();
        assertThat(response.getDateAdhesion()).isEqualTo(java.time.LocalDate.of(2022, 3, 15));
    }

    @Test
    @DisplayName("buildCompteAdherent — membresOrganisations vide → orgNom null")
    void buildCompteAdherent_membresOrganisationsVide_orgNomNull() {
        Membre m = buildMembre();
        m.setMembresOrganisations(List.of());

        Mockito.when(membreRepository.findByEmail(TEST_EMAIL)).thenReturn(Optional.of(m));

        CompteAdherentResponse response = service.getMonCompte();

        assertThat(response).isNotNull();
        assertThat(response.getOrganisationNom()).isNull();
    }

    @Test
    @DisplayName("buildCompteAdherent — membresOrganisations null → orgNom null")
    void buildCompteAdherent_membresOrganisationsNull_orgNomNull() {
        Membre m = buildMembre();
        m.setMembresOrganisations(null);

        Mockito.when(membreRepository.findByEmail(TEST_EMAIL)).thenReturn(Optional.of(m));

        CompteAdherentResponse response = service.getMonCompte();

        assertThat(response).isNotNull();
        assertThat(response.getOrganisationNom()).isNull();
    }
}
