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

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.enums.mutuelle.credit.StatutEcheanceCredit;
import dev.lions.unionflow.server.api.enums.mutuelle.credit.TypeCredit;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.entity.mutuelle.credit.DemandeCredit;
import dev.lions.unionflow.server.entity.mutuelle.credit.EcheanceCredit;
import dev.lions.unionflow.server.entity.mutuelle.epargne.CompteEpargne;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

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

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

@QuarkusTest
class DemandeCreditMapperTest {

    @Inject
    DemandeCreditMapper mapper;

    @Test
    @DisplayName("toDto avec null retourne null")
    void toDto_null_returnsNull() {
        assertThat(mapper.toDto(null)).isNull();
    }

    @Test
    @DisplayName("toEntity avec null retourne null")
    void toEntity_null_returnsNull() {
        assertThat(mapper.toEntity(null)).isNull();
    }

    @Test
    @DisplayName("toDto mappe entity vers DTO")
    void toDto_mapsEntityToDto() {
        UUID membreId = UUID.randomUUID();
        UUID compteId = UUID.randomUUID();
        Membre membre = new Membre();
        membre.setId(membreId);
        CompteEpargne compte = new CompteEpargne();
        compte.setId(compteId);
        DemandeCredit entity = DemandeCredit.builder()
                .membre(membre)
                .compteLie(compte)
                .typeCredit(TypeCredit.CONSOMMATION)
                .montantDemande(new BigDecimal("500000"))
                .dureeMoisDemande(12)
                .justificationDetaillee("Projet")
                .echeancier(Collections.emptyList())
                .build();
        entity.setId(UUID.randomUUID());

        DemandeCreditResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getMembreId()).isEqualTo(membreId.toString());
        assertThat(dto.getCompteLieId()).isEqualTo(compteId.toString());
        assertThat(dto.getTypeCredit()).isEqualTo(TypeCredit.CONSOMMATION);
        assertThat(dto.getMontantDemande()).isEqualByComparingTo("500000");
        assertThat(dto.getDureeMoisDemande()).isEqualTo(12);
    }

    @Test
    @DisplayName("toEntity mappe Request vers entity (dureeMois -> dureeMoisDemande)")
    void toEntity_mapsRequestToEntity() {
        DemandeCreditRequest request = DemandeCreditRequest.builder()
                .membreId(UUID.randomUUID().toString())
                .typeCredit(TypeCredit.CONSOMMATION)
                .montantDemande(new BigDecimal("300000"))
                .dureeMois(24)
                .compteLieId(UUID.randomUUID().toString())
                .justificationDetaillee("Projet détaillé")
                .garantiesProposees(Collections.emptyList())
                .build();

        DemandeCredit entity = mapper.toEntity(request);

        assertThat(entity).isNotNull();
        assertThat(entity.getTypeCredit()).isEqualTo(TypeCredit.CONSOMMATION);
        assertThat(entity.getMontantDemande()).isEqualByComparingTo("300000");
        assertThat(entity.getDureeMoisDemande()).isEqualTo(24);
        assertThat(entity.getJustificationDetaillee()).isEqualTo("Projet détaillé");
        assertThat(entity.getMembre()).isNull();
        assertThat(entity.getCompteLie()).isNull();
    }

    @Test
    @DisplayName("updateEntityFromDto met à jour l'entité cible")
    void updateEntityFromDto_updatesTarget() {
        DemandeCredit entity = DemandeCredit.builder()
                .typeCredit(TypeCredit.CONSOMMATION)
                .montantDemande(BigDecimal.ONE)
                .dureeMoisDemande(6)
                .build();
        DemandeCreditRequest request = DemandeCreditRequest.builder()
                .membreId(UUID.randomUUID().toString())
                .typeCredit(TypeCredit.PROFESSIONNEL)
                .montantDemande(new BigDecimal("200000"))
                .dureeMois(18)
                .justificationDetaillee("Nouvelle justification")
                .build();

        mapper.updateEntityFromDto(request, entity);

        assertThat(entity.getTypeCredit()).isEqualTo(TypeCredit.PROFESSIONNEL);
        assertThat(entity.getMontantDemande()).isEqualByComparingTo("200000");
        assertThat(entity.getJustificationDetaillee()).isEqualTo("Nouvelle justification");
    }

    // =========================================================================
    // Branche manquante : updateEntityFromDto — request null → early return (ligne 87)
    // =========================================================================

    @Test
    @DisplayName("updateEntityFromDto avec request null ne modifie pas l'entité (branche null-guard)")
    void updateEntityFromDto_requestNull_entityInchangee() {
        DemandeCredit entity = DemandeCredit.builder()
                .typeCredit(TypeCredit.CONSOMMATION)
                .montantDemande(new BigDecimal("50000"))
                .dureeMoisDemande(12)
                .build();

        mapper.updateEntityFromDto(null, entity);

        assertThat(entity.getTypeCredit()).isEqualTo(TypeCredit.CONSOMMATION);
        assertThat(entity.getMontantDemande()).isEqualByComparingTo("50000");
        assertThat(entity.getDureeMoisDemande()).isEqualTo(12);
    }

    // ==================== Tests: echeanceCreditListToEcheanceCreditDTOList (branches manquantes) ====================

    @Test
    @DisplayName("toDto avec echeancier null retourne echeancier null dans le DTO")
    void toDto_echeancierNull_returnsDtoWithNullEcheancier() {
        DemandeCredit entity = new DemandeCredit();
        entity.setId(UUID.randomUUID());
        entity.setTypeCredit(TypeCredit.CONSOMMATION);
        entity.setMontantDemande(new BigDecimal("100000"));
        entity.setEcheancier(null);

        DemandeCreditResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getEcheancier()).isNull();
    }

    @Test
    @DisplayName("toDto avec echeancier vide retourne liste vide dans le DTO")
    void toDto_echeancierVide_returnsDtoWithEmptyEcheancier() {
        DemandeCredit entity = DemandeCredit.builder()
                .typeCredit(TypeCredit.CONSOMMATION)
                .montantDemande(new BigDecimal("200000"))
                .dureeMoisDemande(12)
                .echeancier(Collections.emptyList())
                .build();
        entity.setId(UUID.randomUUID());

        DemandeCreditResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getEcheancier()).isNotNull().isEmpty();
    }

    @Test
    @DisplayName("toDto avec echeancier contenant des échéances mappe chaque élément")
    void toDto_echeancierAvecElements_mappeChaquEcheance() {
        EcheanceCredit echeance1 = EcheanceCredit.builder()
                .ordre(1)
                .dateEcheancePrevue(LocalDate.now().plusMonths(1))
                .capitalAmorti(new BigDecimal("10000"))
                .interetsDeLaPeriode(new BigDecimal("500"))
                .montantTotalExigible(new BigDecimal("10500"))
                .capitalRestantDu(new BigDecimal("90000"))
                .statut(StatutEcheanceCredit.A_VENIR)
                .build();
        echeance1.setId(UUID.randomUUID());

        EcheanceCredit echeance2 = EcheanceCredit.builder()
                .ordre(2)
                .dateEcheancePrevue(LocalDate.now().plusMonths(2))
                .capitalAmorti(new BigDecimal("10000"))
                .interetsDeLaPeriode(new BigDecimal("450"))
                .montantTotalExigible(new BigDecimal("10450"))
                .capitalRestantDu(new BigDecimal("80000"))
                .statut(StatutEcheanceCredit.A_VENIR)
                .build();
        echeance2.setId(UUID.randomUUID());

        DemandeCredit entity = new DemandeCredit();
        entity.setId(UUID.randomUUID());
        entity.setTypeCredit(TypeCredit.CONSOMMATION);
        entity.setMontantDemande(new BigDecimal("100000"));
        entity.setEcheancier(List.of(echeance1, echeance2));

        DemandeCreditResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getEcheancier()).isNotNull().hasSize(2);
        assertThat(dto.getEcheancier().get(0).getOrdre()).isEqualTo(1);
        assertThat(dto.getEcheancier().get(1).getOrdre()).isEqualTo(2);
    }
}
