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

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

import dev.lions.unionflow.server.api.dto.mutuelle.epargne.TransactionEpargneRequest;
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.StatutCompteEpargne;
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeTransactionEpargne;
import dev.lions.unionflow.server.entity.mutuelle.epargne.CompteEpargne;
import dev.lions.unionflow.server.mapper.mutuelle.epargne.TransactionEpargneMapper;
import dev.lions.unionflow.server.repository.ParametresLcbFtRepository;
import dev.lions.unionflow.server.repository.mutuelle.epargne.CompteEpargneRepository;
import dev.lions.unionflow.server.repository.mutuelle.epargne.TransactionEpargneRepository;
import dev.lions.unionflow.server.service.AlerteLcbFtService;
import dev.lions.unionflow.server.service.AuditService;

import java.math.BigDecimal;
import java.util.Optional;
import java.util.UUID;

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

/**
 * Couvre la branche L229 false de validerLcbFtSiSeuilAtteint :
 * getMontant()==null → return immédiat (branche A=true du || ).
 * Utilise @InjectMocks pour bypasser @Transactional CDI et appeler directement
 * la méthode sans interception.
 */
@ExtendWith(MockitoExtension.class)
class TransactionEpargneServiceLcbFtBranchesTest {

    @Mock
    TransactionEpargneRepository transactionEpargneRepository;

    @Mock
    CompteEpargneRepository compteEpargneRepository;

    @Mock
    TransactionEpargneMapper transactionEpargneMapper;

    @Mock
    ParametresLcbFtRepository parametresLcbFtRepository;

    @Mock
    AuditService auditService;

    @Mock
    AlerteLcbFtService alerteLcbFtService;

    @InjectMocks
    TransactionEpargneService transactionEpargneService;

    @Test
    @DisplayName("validerLcbFtSiSeuilAtteint L229 B=true — seuil null → return immédiat (branche seuil==null)")
    void validerLcbFt_seuilNull_l229BTrueBranch() throws Exception {
        // Appel direct via réflexion avec seuil=null → L229: getMontant()!=null (A=false),
        // seuil==null (B=true) → true → return immédiatement
        java.lang.reflect.Method validerMethod = TransactionEpargneService.class.getDeclaredMethod(
                "validerLcbFtSiSeuilAtteint",
                dev.lions.unionflow.server.api.dto.mutuelle.epargne.TransactionEpargneRequest.class,
                java.math.BigDecimal.class);
        validerMethod.setAccessible(true);

        dev.lions.unionflow.server.api.dto.mutuelle.epargne.TransactionEpargneRequest request =
                dev.lions.unionflow.server.api.dto.mutuelle.epargne.TransactionEpargneRequest.builder()
                        .compteId(java.util.UUID.randomUUID().toString())
                        .typeTransaction(TypeTransactionEpargne.DEPOT)
                        .montant(java.math.BigDecimal.TEN)
                        .build();

        // Invoke avec seuil=null → L229: seuil==null → true → return (pas d'exception)
        validerMethod.invoke(transactionEpargneService, request, (java.math.BigDecimal) null);
    }

    @Test
    @DisplayName("validerLcbFtSiSeuilAtteint L229 A=true — montant null → return immédiat, puis NPE sur add(null)")
    void executerTransaction_montantNull_validerLcbFt_L229ATrueBranch() {
        UUID compteId = UUID.randomUUID();
        CompteEpargne compte = new CompteEpargne();
        compte.setId(compteId);
        compte.setStatut(StatutCompteEpargne.ACTIF);
        compte.setSoldeActuel(new BigDecimal("1000"));
        compte.setSoldeBloque(BigDecimal.ZERO);

        when(compteEpargneRepository.findByIdOptional(compteId)).thenReturn(Optional.of(compte));
        when(parametresLcbFtRepository.getSeuilJustification(any(), any()))
                .thenReturn(Optional.of(new BigDecimal("500000")));

        // montant=null → validerLcbFtSiSeuilAtteint L229: getMontant()==null → true → return
        // Puis L80: montant = null → L85: soldeAvant.add(null) → NullPointerException
        TransactionEpargneRequest request = TransactionEpargneRequest.builder()
                .compteId(compteId.toString())
                .typeTransaction(TypeTransactionEpargne.DEPOT)
                .montant(null)
                .build();

        assertThatThrownBy(() -> transactionEpargneService.executerTransaction(request))
                .isInstanceOf(Exception.class);
    }
}
