package dev.lions.unionflow.server.service.mutuelle.credit;

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.Mockito.*;

import dev.lions.unionflow.server.api.dto.mutuelle.credit.DemandeCreditRequest;
import dev.lions.unionflow.server.api.dto.mutuelle.credit.DemandeCreditResponse;
import dev.lions.unionflow.server.api.dto.mutuelle.credit.GarantieDemandeDTO;
import dev.lions.unionflow.server.api.enums.mutuelle.credit.StatutDemandeCredit;
import dev.lions.unionflow.server.api.enums.mutuelle.credit.TypeGarantie;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.mutuelle.credit.DemandeCredit;
import dev.lions.unionflow.server.entity.mutuelle.credit.GarantieDemande;
import dev.lions.unionflow.server.entity.mutuelle.epargne.CompteEpargne;
import dev.lions.unionflow.server.mapper.mutuelle.credit.DemandeCreditMapper;
import dev.lions.unionflow.server.mapper.mutuelle.credit.GarantieDemandeMapper;
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.AuditService;
import dev.lions.unionflow.server.service.mutuelle.epargne.TransactionEpargneService;
import io.quarkus.test.InjectMock;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.ws.rs.NotFoundException;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@QuarkusTest
class DemandeCreditServiceTest {

        @Inject
        DemandeCreditService service;

        @InjectMock
        DemandeCreditRepository repository;

        @InjectMock
        MembreRepository membreRepository;

        @InjectMock
        CompteEpargneRepository compteEpargneRepository;

        @InjectMock
        DemandeCreditMapper mapper;

        @InjectMock
        GarantieDemandeMapper garantieMapper;

        @InjectMock
        TransactionEpargneService transactionEpargneService;

        @InjectMock
        AuditService auditService;

        // ------------------------------------------------------------------ //
        // soumettreDemande                                                     //
        // ------------------------------------------------------------------ //

        @Test
        @DisplayName("soumettreDemande initialise le statut et génère un numéro de dossier")
        void soumettreDemande_success() {
                UUID membreId = UUID.randomUUID();
                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                membre.setDateVerificationIdentite(LocalDate.now().minusDays(30));

                DemandeCredit entity = new DemandeCredit();

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));
                when(mapper.toEntity(request)).thenReturn(entity);
                when(mapper.toDto(entity)).thenReturn(null);

                service.soumettreDemande(request);

                assertThat(entity.getStatut()).isEqualTo(StatutDemandeCredit.SOUMISE);
                assertThat(entity.getDateSoumission()).isEqualTo(LocalDate.now());
                assertThat(entity.getNumeroDossier()).startsWith("CRD-");
                verify(repository).persist(any(DemandeCredit.class));
        }

        @Test
        @DisplayName("soumettreDemande avec compteLieId non nul charge le compte épargne")
        void soumettreDemande_avecCompteLie_chargeCompte() {
                UUID membreId = UUID.randomUUID();
                UUID compteId = UUID.randomUUID();

                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());
                request.setCompteLieId(compteId.toString());

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                membre.setDateVerificationIdentite(LocalDate.now().minusDays(30));

                CompteEpargne compte = new CompteEpargne();
                compte.setId(compteId);

                DemandeCredit entity = new DemandeCredit();

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));
                when(mapper.toEntity(request)).thenReturn(entity);
                when(compteEpargneRepository.findByIdOptional(compteId)).thenReturn(Optional.of(compte));
                when(mapper.toDto(entity)).thenReturn(new DemandeCreditResponse());

                service.soumettreDemande(request);

                assertThat(entity.getCompteLie()).isSameAs(compte);
                verify(repository).persist(entity);
        }

        @Test
        @DisplayName("soumettreDemande avec compteLieId inexistant lance NotFoundException")
        void soumettreDemande_compteInexistant_throws() {
                UUID membreId = UUID.randomUUID();
                UUID compteId = UUID.randomUUID();

                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());
                request.setCompteLieId(compteId.toString());

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                membre.setDateVerificationIdentite(LocalDate.now().minusDays(30));

                DemandeCredit entity = new DemandeCredit();

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));
                when(mapper.toEntity(request)).thenReturn(entity);
                when(compteEpargneRepository.findByIdOptional(compteId)).thenReturn(Optional.empty());

                assertThatThrownBy(() -> service.soumettreDemande(request))
                        .isInstanceOf(NotFoundException.class)
                        .hasMessageContaining(compteId.toString());
        }

        @Test
        @DisplayName("soumettreDemande avec garantiesProposees non nulles les attache à la demande")
        void soumettreDemande_avecGaranties_attacheGaranties() {
                UUID membreId = UUID.randomUUID();

                GarantieDemandeDTO garantieDto = GarantieDemandeDTO.builder()
                        .typeGarantie(TypeGarantie.MATERIELLE)
                        .valeurEstimee(BigDecimal.valueOf(5000000))
                        .referenceOuDescription("Véhicule Toyota AA-1234-BB")
                        .build();

                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());
                request.setGarantiesProposees(List.of(garantieDto));

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                membre.setDateVerificationIdentite(LocalDate.now().minusDays(30));

                DemandeCredit entity = new DemandeCredit();
                GarantieDemande garantieEntity = new GarantieDemande();

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));
                when(mapper.toEntity(request)).thenReturn(entity);
                when(garantieMapper.toEntity(garantieDto)).thenReturn(garantieEntity);
                when(mapper.toDto(entity)).thenReturn(new DemandeCreditResponse());

                service.soumettreDemande(request);

                assertThat(entity.getGaranties()).contains(garantieEntity);
                assertThat(garantieEntity.getDemandeCredit()).isSameAs(entity);
                verify(repository).persist(entity);
        }

        // ------------------------------------------------------------------ //
        // getDemandeById                                                       //
        // ------------------------------------------------------------------ //

        @Test
        @DisplayName("getDemandeById inexistant lance NotFoundException")
        void getDemandeById_inexistant_throws() {
                UUID id = UUID.randomUUID();
                when(repository.findByIdOptional(id)).thenReturn(Optional.empty());

                assertThatThrownBy(() -> service.getDemandeById(id))
                        .isInstanceOf(NotFoundException.class)
                        .hasMessageContaining(id.toString());
        }

        @Test
        @DisplayName("getDemandeById existant retourne le DTO")
        void getDemandeById_existant_returnsDto() {
                UUID id = UUID.randomUUID();
                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                DemandeCreditResponse dto = new DemandeCreditResponse();

                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
                when(mapper.toDto(entity)).thenReturn(dto);

                assertThat(service.getDemandeById(id)).isSameAs(dto);
        }

        // ------------------------------------------------------------------ //
        // getDemandesByMembre                                                  //
        // ------------------------------------------------------------------ //

        @Test
        @DisplayName("getDemandesByMembre retourne les demandes du membre")
        @SuppressWarnings("unchecked")
        void getDemandesByMembre_returnsListe() {
                UUID membreId = UUID.randomUUID();
                DemandeCredit entity = new DemandeCredit();
                entity.setId(UUID.randomUUID());
                DemandeCreditResponse dto = new DemandeCreditResponse();

                io.quarkus.hibernate.orm.panache.PanacheQuery<DemandeCredit> mockQuery =
                        mock(io.quarkus.hibernate.orm.panache.PanacheQuery.class);
                when(repository.find("membre.id = ?1 and actif = true", membreId)).thenReturn(mockQuery);
                when(mockQuery.stream()).thenReturn(Stream.of(entity));
                when(mapper.toDto(entity)).thenReturn(dto);

                List<DemandeCreditResponse> result = service.getDemandesByMembre(membreId);
                assertThat(result).isNotNull().hasSize(1).contains(dto);
        }

        // ------------------------------------------------------------------ //
        // changerStatut                                                        //
        // ------------------------------------------------------------------ //

        @Test
        @DisplayName("changerStatut id inexistant lance NotFoundException")
        void changerStatut_inexistant_throws() {
                UUID id = UUID.randomUUID();
                when(repository.findByIdOptional(id)).thenReturn(Optional.empty());

                assertThatThrownBy(() -> service.changerStatut(id, StatutDemandeCredit.REJETEE, "Refus"))
                        .isInstanceOf(NotFoundException.class);
        }

        @Test
        @DisplayName("changerStatut REJETEE met à jour et retourne DTO")
        void changerStatut_rejetee_returnsDto() {
                UUID id = UUID.randomUUID();
                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                DemandeCreditResponse dto = new DemandeCreditResponse();
                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
                when(mapper.toDto(entity)).thenReturn(dto);

                assertThat(service.changerStatut(id, StatutDemandeCredit.REJETEE, "Notes")).isSameAs(dto);
                assertThat(entity.getStatut()).isEqualTo(StatutDemandeCredit.REJETEE);
                assertThat(entity.getNotesComite()).isEqualTo("Notes");
        }

        @Test
        @DisplayName("changerStatut APPROUVEE avec montant et durée déjà fixés conserve les valeurs")
        void changerStatut_approuvee_avecMontantDejaFixe() {
                UUID id = UUID.randomUUID();
                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                entity.setMontantApprouve(BigDecimal.valueOf(50000));
                entity.setDureeMoisApprouvee(6);
                entity.setMontantDemande(BigDecimal.valueOf(100000));
                entity.setDureeMoisDemande(12);
                DemandeCreditResponse dto = new DemandeCreditResponse();
                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
                when(mapper.toDto(entity)).thenReturn(dto);

                DemandeCreditResponse result = service.changerStatut(id, StatutDemandeCredit.APPROUVEE, "Partiel");

                assertThat(result).isSameAs(dto);
                assertThat(entity.getMontantApprouve()).isEqualByComparingTo(BigDecimal.valueOf(50000));
                assertThat(entity.getDureeMoisApprouvee()).isEqualTo(6);
        }

        @Test
        @DisplayName("changerStatut APPROUVEE sans montant applique montant demandé par défaut")
        void changerStatut_approuvee_sansMontant_appliqueMontantDemande() {
                UUID id = UUID.randomUUID();
                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                entity.setMontantApprouve(null);
                entity.setDureeMoisApprouvee(null);
                entity.setMontantDemande(BigDecimal.valueOf(75000));
                entity.setDureeMoisDemande(9);
                DemandeCreditResponse dto = new DemandeCreditResponse();
                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
                when(mapper.toDto(entity)).thenReturn(dto);

                service.changerStatut(id, StatutDemandeCredit.APPROUVEE, "OK par défaut");

                assertThat(entity.getMontantApprouve()).isEqualByComparingTo(BigDecimal.valueOf(75000));
                assertThat(entity.getDureeMoisApprouvee()).isEqualTo(9);
        }

        @Test
        @DisplayName("changerStatut APPROUVEE avec compte épargne lié et garantie EPARGNE_BLOQUEE déclenche retenue")
        void changerStatut_approuvee_avecCompteEtGarantieBloquee_declencheRetenue() {
                UUID id = UUID.randomUUID();
                UUID compteId = UUID.randomUUID();

                CompteEpargne compte = new CompteEpargne();
                compte.setId(compteId);

                GarantieDemande garantie = new GarantieDemande();
                garantie.setTypeGarantie(TypeGarantie.EPARGNE_BLOQUEE);
                garantie.setValeurEstimee(BigDecimal.valueOf(200000));

                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                entity.setMontantApprouve(BigDecimal.valueOf(500000));
                entity.setDureeMoisApprouvee(12);
                entity.setMontantDemande(BigDecimal.valueOf(500000));
                entity.setDureeMoisDemande(12);
                entity.setNumeroDossier("CRD-TESTBLQ");
                entity.setCompteLie(compte);
                entity.getGaranties().add(garantie);

                DemandeCreditResponse dto = new DemandeCreditResponse();
                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
                when(mapper.toDto(entity)).thenReturn(dto);

                DemandeCreditResponse result = service.changerStatut(id, StatutDemandeCredit.APPROUVEE, "Avec garantie");

                assertThat(result).isSameAs(dto);
                // Le service doit déclencher executerTransaction pour la retenue de garantie
                verify(transactionEpargneService).executerTransaction(any());
        }

        @Test
        @DisplayName("changerStatut APPROUVEE avec compte épargne lié mais garantie non EPARGNE_BLOQUEE ne déclenche pas retenue")
        void changerStatut_approuvee_avecCompteEtGarantieAutre_neDeclencheParRetenue() {
                UUID id = UUID.randomUUID();
                UUID compteId = UUID.randomUUID();

                CompteEpargne compte = new CompteEpargne();
                compte.setId(compteId);

                GarantieDemande garantie = new GarantieDemande();
                garantie.setTypeGarantie(TypeGarantie.MATERIELLE);
                garantie.setValeurEstimee(BigDecimal.valueOf(1000000));

                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                entity.setMontantApprouve(BigDecimal.valueOf(300000));
                entity.setDureeMoisApprouvee(6);
                entity.setMontantDemande(BigDecimal.valueOf(300000));
                entity.setDureeMoisDemande(6);
                entity.setNumeroDossier("CRD-TESTMAT");
                entity.setCompteLie(compte);
                entity.getGaranties().add(garantie);

                DemandeCreditResponse dto = new DemandeCreditResponse();
                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
                when(mapper.toDto(entity)).thenReturn(dto);

                service.changerStatut(id, StatutDemandeCredit.APPROUVEE, "Garantie matérielle");

                verify(transactionEpargneService, never()).executerTransaction(any());
        }

        // ------------------------------------------------------------------ //
        // approuver                                                            //
        // ------------------------------------------------------------------ //

        @Test
        @DisplayName("approuver id inexistant lance NotFoundException")
        void approuver_inexistant_throws() {
                UUID id = UUID.randomUUID();
                when(repository.findByIdOptional(id)).thenReturn(Optional.empty());

                assertThatThrownBy(() -> service.approuver(id, BigDecimal.valueOf(100000), 12, BigDecimal.TEN, "OK"))
                        .isInstanceOf(NotFoundException.class);
        }

        @Test
        @DisplayName("approuver existant met à jour les conditions définitives")
        void approuver_existant_metAJourConditions() {
                UUID id = UUID.randomUUID();
                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                entity.setMontantDemande(BigDecimal.valueOf(100000));
                entity.setDureeMoisDemande(12);
                entity.setMontantApprouve(null);
                entity.setDureeMoisApprouvee(null);
                DemandeCreditResponse dto = new DemandeCreditResponse();
                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
                when(mapper.toDto(entity)).thenReturn(dto);

                DemandeCreditResponse result = service.approuver(
                        id, BigDecimal.valueOf(80000), 10, BigDecimal.valueOf(5), "Approuvé");

                assertThat(result).isSameAs(dto);
                assertThat(entity.getMontantApprouve()).isEqualByComparingTo(BigDecimal.valueOf(80000));
                assertThat(entity.getDureeMoisApprouvee()).isEqualTo(10);
                assertThat(entity.getTauxInteretAnnuel()).isEqualByComparingTo(BigDecimal.valueOf(5));
        }

        // ------------------------------------------------------------------ //
        // decaisser                                                            //
        // ------------------------------------------------------------------ //

        @Test
        @DisplayName("decaisser sans statut APPROUVEE lance IllegalStateException")
        void decaisser_nonApprouvee_throws() {
                UUID id = UUID.randomUUID();
                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                entity.setStatut(StatutDemandeCredit.SOUMISE);
                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));

                assertThatThrownBy(() -> service.decaisser(id, LocalDate.now().plusMonths(1)))
                        .isInstanceOf(IllegalStateException.class)
                        .hasMessageContaining("APPROUVEE");
        }

        @Test
        @DisplayName("decaisser sans compte lié lance IllegalStateException")
        void decaisser_sansCompte_throws() {
                UUID id = UUID.randomUUID();
                UUID membreId = UUID.randomUUID();

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                membre.setDateVerificationIdentite(LocalDate.now().minusDays(30));

                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                entity.setStatut(StatutDemandeCredit.APPROUVEE);
                entity.setMontantApprouve(BigDecimal.valueOf(100000));
                entity.setMembre(membre);
                entity.setCompteLie(null);
                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));

                assertThatThrownBy(() -> service.decaisser(id, LocalDate.now().plusMonths(1)))
                        .isInstanceOf(IllegalStateException.class)
                        .hasMessageContaining("compte");
        }

        @Test
        @DisplayName("decaisser success génère un échéancier et vire les fonds")
        void decaisser_success_genereEcheancierEtVire() {
                UUID id = UUID.randomUUID();
                UUID membreId = UUID.randomUUID();
                UUID compteId = UUID.randomUUID();

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                membre.setDateVerificationIdentite(LocalDate.now().minusDays(30));

                CompteEpargne compte = new CompteEpargne();
                compte.setId(compteId);

                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                entity.setStatut(StatutDemandeCredit.APPROUVEE);
                entity.setMontantApprouve(BigDecimal.valueOf(120000));
                entity.setDureeMoisApprouvee(3);
                // Taux non nul pour couvrir la branche taux > 0 dans genererEcheancier
                entity.setTauxInteretAnnuel(BigDecimal.valueOf(12));
                entity.setNumeroDossier("CRD-DECATEST");
                entity.setMembre(membre);
                entity.setCompteLie(compte);

                DemandeCreditResponse dto = new DemandeCreditResponse();
                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
                when(mapper.toDto(entity)).thenReturn(dto);

                LocalDate premierEcheance = LocalDate.now().plusMonths(1);
                DemandeCreditResponse result = service.decaisser(id, premierEcheance);

                assertThat(result).isSameAs(dto);
                assertThat(entity.getStatut()).isEqualTo(StatutDemandeCredit.DECAISSEE);
                assertThat(entity.getDatePremierEcheance()).isEqualTo(premierEcheance);
                // Un virement a été fait sur le compte
                verify(transactionEpargneService, atLeastOnce()).executerTransaction(any());
                // L'échéancier a été généré (3 mois)
                assertThat(entity.getEcheancier()).hasSize(3);
        }

        @Test
        @DisplayName("decaisser avec taux zéro génère un échéancier sans intérêts")
        void decaisser_avecTauxZero_genereEcheancierSansInterets() {
                UUID id = UUID.randomUUID();
                UUID membreId = UUID.randomUUID();
                UUID compteId = UUID.randomUUID();

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                membre.setDateVerificationIdentite(LocalDate.now().minusDays(30));

                CompteEpargne compte = new CompteEpargne();
                compte.setId(compteId);

                DemandeCredit entity = new DemandeCredit();
                entity.setId(id);
                entity.setStatut(StatutDemandeCredit.APPROUVEE);
                entity.setMontantApprouve(BigDecimal.valueOf(60000));
                entity.setDureeMoisApprouvee(3);
                // Taux zéro pour couvrir la branche if (tauxMensuel == 0)
                entity.setTauxInteretAnnuel(BigDecimal.ZERO);
                entity.setNumeroDossier("CRD-ZEROTAUX");
                entity.setMembre(membre);
                entity.setCompteLie(compte);

                when(repository.findByIdOptional(id)).thenReturn(Optional.of(entity));
                when(mapper.toDto(entity)).thenReturn(new DemandeCreditResponse());

                service.decaisser(id, LocalDate.now().plusMonths(1));

                assertThat(entity.getEcheancier()).hasSize(3);
                // Coût total = 0 (pas d'intérêts)
                assertThat(entity.getCoutTotalCredit()).isEqualByComparingTo(BigDecimal.ZERO);
        }

        // ------------------------------------------------------------------ //
        // verifierConformiteKyc — branches KYC (couverts via soumettreDemande) //
        // ------------------------------------------------------------------ //

        @Test
        @DisplayName("soumettreDemande avec KYC null lance IllegalStateException")
        void soumettreDemande_kycNull_throws() {
                UUID membreId = UUID.randomUUID();
                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc(null);

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));

                assertThatThrownBy(() -> service.soumettreDemande(request))
                        .isInstanceOf(IllegalStateException.class)
                        .hasMessageContaining("KYC");
        }

        @Test
        @DisplayName("soumettreDemande avec KYC non VERIFIE lance IllegalStateException")
        void soumettreDemande_kycNonVerifie_throws() {
                UUID membreId = UUID.randomUUID();
                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("EN_ATTENTE");

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));

                assertThatThrownBy(() -> service.soumettreDemande(request))
                        .isInstanceOf(IllegalStateException.class)
                        .hasMessageContaining("KYC");
        }

        @Test
        @DisplayName("soumettreDemande avec date vérification null lance IllegalStateException")
        void soumettreDemande_dateVerificationNull_throws() {
                UUID membreId = UUID.randomUUID();
                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                membre.setDateVerificationIdentite(null);

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));

                assertThatThrownBy(() -> service.soumettreDemande(request))
                        .isInstanceOf(IllegalStateException.class)
                        .hasMessageContaining("identité");
        }

        @Test
        @DisplayName("soumettreDemande avec date vérification expirée lance IllegalStateException")
        void soumettreDemande_dateVerificationExpiree_throws() {
                UUID membreId = UUID.randomUUID();
                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                // Date de plus d'un an
                membre.setDateVerificationIdentite(LocalDate.now().minusYears(2));

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));

                assertThatThrownBy(() -> service.soumettreDemande(request))
                        .isInstanceOf(IllegalStateException.class)
                        .hasMessageContaining("expiré");
        }

        @Test
        @DisplayName("soumettreDemande avec compteLieId vide (empty string) → condition L83 false → pas de compte chargé")
        void soumettreDemande_compteLieIdVide_conditionFalse() {
                UUID membreId = UUID.randomUUID();

                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());
                request.setCompteLieId(""); // non-null mais isEmpty() → L83: != null true, isEmpty() true → condition false

                Membre membre = new Membre();
                membre.setId(membreId);
                membre.setStatutKyc("VERIFIE");
                membre.setDateVerificationIdentite(LocalDate.now().minusDays(30));

                DemandeCredit entity = new DemandeCredit();

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.of(membre));
                when(mapper.toEntity(request)).thenReturn(entity);
                when(mapper.toDto(entity)).thenReturn(new DemandeCreditResponse());

                service.soumettreDemande(request);

                // Pas de compte lié car compteLieId vide → condition false → compte non chargé
                assertThat(entity.getCompteLie()).isNull();
                verify(repository).persist(entity);
        }

        @Test
        @DisplayName("soumettreDemande avec membre inexistant lance NotFoundException")
        void soumettreDemande_membreInexistant_throws() {
                UUID membreId = UUID.randomUUID();
                DemandeCreditRequest request = new DemandeCreditRequest();
                request.setMembreId(membreId.toString());

                when(membreRepository.findByIdOptional(membreId)).thenReturn(Optional.empty());

                assertThatThrownBy(() -> service.soumettreDemande(request))
                        .isInstanceOf(NotFoundException.class)
                        .hasMessageContaining(membreId.toString());
        }
}
