package dev.lions.unionflow.server.mapper.tontine;

import dev.lions.unionflow.server.api.dto.tontine.TontineRequest;
import dev.lions.unionflow.server.api.dto.tontine.TontineResponse;
import dev.lions.unionflow.server.api.enums.tontine.FrequenceTour;
import dev.lions.unionflow.server.api.enums.tontine.TypeTontine;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.tontine.Tontine;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import dev.lions.unionflow.server.entity.tontine.TourTontine;

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 TontineMapperTest {

    @Inject
    TontineMapper 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 orgId = UUID.randomUUID();
        Organisation org = new Organisation();
        org.setId(orgId);
        Tontine entity = Tontine.builder()
                .organisation(org)
                .nom("Tontine 2025")
                .description("Description")
                .typeTontine(TypeTontine.ROTATIVE_CLASSIQUE)
                .frequence(FrequenceTour.MENSUELLE)
                .montantMiseParTour(new BigDecimal("10000"))
                .limiteParticipants(20)
                .calendrierTours(Collections.emptyList())
                .build();
        entity.setId(UUID.randomUUID());

        TontineResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getOrganisationId()).isEqualTo(orgId.toString());
        assertThat(dto.getNom()).isEqualTo("Tontine 2025");
        assertThat(dto.getDescription()).isEqualTo("Description");
        assertThat(dto.getTypeTontine()).isEqualTo(TypeTontine.ROTATIVE_CLASSIQUE);
        assertThat(dto.getFrequence()).isEqualTo(FrequenceTour.MENSUELLE);
        assertThat(dto.getMontantMiseParTour()).isEqualByComparingTo("10000");
        assertThat(dto.getLimiteParticipants()).isEqualTo(20);
    }

    @Test
    @DisplayName("toEntity mappe Request vers entity")
    void toEntity_mapsRequestToEntity() {
        TontineRequest request = TontineRequest.builder()
                .nom("Tontine test")
                .description("Desc")
                .organisationId(UUID.randomUUID().toString())
                .typeTontine(TypeTontine.ROTATIVE_CLASSIQUE)
                .frequence(FrequenceTour.HEBDOMADAIRE)
                .dateDebutPrevue(LocalDate.now().plusDays(7))
                .montantMiseParTour(new BigDecimal("5000"))
                .limiteParticipants(10)
                .build();

        Tontine entity = mapper.toEntity(request);

        assertThat(entity).isNotNull();
        assertThat(entity.getNom()).isEqualTo("Tontine test");
        assertThat(entity.getTypeTontine()).isEqualTo(TypeTontine.ROTATIVE_CLASSIQUE);
        assertThat(entity.getFrequence()).isEqualTo(FrequenceTour.HEBDOMADAIRE);
        assertThat(entity.getOrganisation()).isNull();
        assertThat(entity.getCalendrierTours()).isNotNull().isEmpty();
    }

    @Test
    @DisplayName("updateEntityFromDto met à jour l'entité cible")
    void updateEntityFromDto_updatesTarget() {
        Tontine entity = Tontine.builder()
                .nom("Ancienne")
                .montantMiseParTour(BigDecimal.ZERO)
                .build();
        TontineRequest request = TontineRequest.builder()
                .nom("Nouvelle tontine")
                .organisationId(UUID.randomUUID().toString())
                .typeTontine(TypeTontine.ROTATIVE_CLASSIQUE)
                .frequence(FrequenceTour.MENSUELLE)
                .dateDebutPrevue(LocalDate.now())
                .montantMiseParTour(new BigDecimal("15000"))
                .limiteParticipants(15)
                .build();

        mapper.updateEntityFromDto(request, entity);

        assertThat(entity.getNom()).isEqualTo("Nouvelle tontine");
        assertThat(entity.getMontantMiseParTour()).isEqualByComparingTo("15000");
        assertThat(entity.getLimiteParticipants()).isEqualTo(15);
    }

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

    @Test
    @DisplayName("updateEntityFromDto avec request null ne modifie pas l'entité (branche null-guard)")
    void updateEntityFromDto_requestNull_entityInchangee() {
        Tontine entity = Tontine.builder()
                .nom("Invariante")
                .montantMiseParTour(new BigDecimal("9999"))
                .build();

        mapper.updateEntityFromDto(null, entity);

        assertThat(entity.getNom()).isEqualTo("Invariante");
        assertThat(entity.getMontantMiseParTour()).isEqualByComparingTo("9999");
    }

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

    @Test
    @DisplayName("toDto avec calendrierTours null retourne calendrierTours null dans le DTO")
    void toDto_calendrierToursNull_returnsDtoWithNullCalendrier() {
        Tontine entity = new Tontine();
        entity.setId(UUID.randomUUID());
        entity.setNom("Tontine sans calendrier");
        entity.setTypeTontine(TypeTontine.ROTATIVE_CLASSIQUE);
        entity.setFrequence(FrequenceTour.MENSUELLE);
        entity.setCalendrierTours(null);

        TontineResponse dto = mapper.toDto(entity);

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

    @Test
    @DisplayName("toDto avec calendrierTours vide retourne liste vide dans le DTO")
    void toDto_calendrierToursVide_returnsDtoWithEmptyCalendrier() {
        Tontine entity = Tontine.builder()
                .nom("Tontine vide")
                .typeTontine(TypeTontine.ROTATIVE_CLASSIQUE)
                .frequence(FrequenceTour.MENSUELLE)
                .calendrierTours(Collections.emptyList())
                .build();
        entity.setId(UUID.randomUUID());

        TontineResponse dto = mapper.toDto(entity);

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

    @Test
    @DisplayName("toDto avec calendrierTours contenant des tours mappe chaque élément")
    void toDto_calendrierToursAvecElements_mappeChaqueTour() {
        TourTontine tour1 = TourTontine.builder()
                .ordreTour(1)
                .dateOuvertureCotisations(LocalDate.now())
                .montantCible(new BigDecimal("50000"))
                .cagnotteCollectee(BigDecimal.ZERO)
                .statutInterne("A_VENIR")
                .build();
        tour1.setId(UUID.randomUUID());

        TourTontine tour2 = TourTontine.builder()
                .ordreTour(2)
                .dateOuvertureCotisations(LocalDate.now().plusMonths(1))
                .montantCible(new BigDecimal("50000"))
                .cagnotteCollectee(BigDecimal.ZERO)
                .statutInterne("A_VENIR")
                .build();
        tour2.setId(UUID.randomUUID());

        Tontine entity = new Tontine();
        entity.setId(UUID.randomUUID());
        entity.setNom("Tontine avec tours");
        entity.setTypeTontine(TypeTontine.ROTATIVE_CLASSIQUE);
        entity.setFrequence(FrequenceTour.MENSUELLE);
        entity.setCalendrierTours(List.of(tour1, tour2));

        TontineResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getCalendrierTours()).isNotNull().hasSize(2);
        assertThat(dto.getCalendrierTours().get(0).getOrdreTour()).isEqualTo(1);
        assertThat(dto.getCalendrierTours().get(1).getOrdreTour()).isEqualTo(2);
    }
}
