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

import dev.lions.unionflow.server.api.dto.vote.CampagneVoteRequest;
import dev.lions.unionflow.server.api.dto.vote.CampagneVoteResponse;
import dev.lions.unionflow.server.api.enums.vote.ModeScrutin;
import dev.lions.unionflow.server.api.enums.vote.TypeVote;
import dev.lions.unionflow.server.entity.Organisation;
import dev.lions.unionflow.server.entity.vote.CampagneVote;
import dev.lions.unionflow.server.entity.vote.Candidat;
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.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

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

@QuarkusTest
class CampagneVoteMapperTest {

    @Inject
    CampagneVoteMapper 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);
        CampagneVote entity = CampagneVote.builder()
                .organisation(org)
                .titre("Élection bureau 2025")
                .descriptionOuResolution("Résolution")
                .typeVote(TypeVote.ELECTION_BUREAU)
                .modeScrutin(ModeScrutin.MAJORITAIRE_UN_TOUR)
                .dateOuverture(LocalDateTime.now().plusDays(1))
                .dateFermeture(LocalDateTime.now().plusDays(2))
                .restreindreMembresAJour(true)
                .autoriserVoteBlanc(true)
                .candidats(Collections.emptyList())
                .build();
        entity.setId(UUID.randomUUID());

        CampagneVoteResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getOrganisationId()).isEqualTo(orgId.toString());
        assertThat(dto.getTitre()).isEqualTo("Élection bureau 2025");
        assertThat(dto.getDescriptionOuResolution()).isEqualTo("Résolution");
        assertThat(dto.getTypeVote()).isEqualTo(TypeVote.ELECTION_BUREAU);
        assertThat(dto.getModeScrutin()).isEqualTo(ModeScrutin.MAJORITAIRE_UN_TOUR);
        assertThat(dto.getRestreindreMembresAJour()).isTrue();
    }

    @Test
    @DisplayName("toEntity mappe Request vers entity")
    void toEntity_mapsRequestToEntity() {
        CampagneVoteRequest request = CampagneVoteRequest.builder()
                .titre("Vote test")
                .descriptionOuResolution("Desc")
                .organisationId(UUID.randomUUID().toString())
                .typeVote(TypeVote.REFERENDUM)
                .modeScrutin(ModeScrutin.BUREAU_CONSENSUEL)
                .dateOuverture(LocalDateTime.now().plusDays(1))
                .dateFermeture(LocalDateTime.now().plusDays(2))
                .restreindreMembresAJour(false)
                .autoriserVoteBlanc(true)
                .build();

        CampagneVote entity = mapper.toEntity(request);

        assertThat(entity).isNotNull();
        assertThat(entity.getTitre()).isEqualTo("Vote test");
        assertThat(entity.getDescriptionOuResolution()).isEqualTo("Desc");
        assertThat(entity.getTypeVote()).isEqualTo(TypeVote.REFERENDUM);
        assertThat(entity.getOrganisation()).isNull();
        assertThat(entity.getCandidats()).isNotNull().isEmpty();
    }

    @Test
    @DisplayName("updateEntityFromDto met à jour l'entité cible")
    void updateEntityFromDto_updatesTarget() {
        CampagneVote entity = CampagneVote.builder()
                .titre("Ancien")
                .typeVote(TypeVote.ELECTION_BUREAU)
                .build();
        CampagneVoteRequest request = CampagneVoteRequest.builder()
                .titre("Nouveau titre")
                .organisationId(UUID.randomUUID().toString())
                .typeVote(TypeVote.REFERENDUM)
                .modeScrutin(ModeScrutin.MAJORITAIRE_UN_TOUR)
                .dateOuverture(LocalDateTime.now().plusDays(1))
                .dateFermeture(LocalDateTime.now().plusDays(2))
                .build();

        mapper.updateEntityFromDto(request, entity);

        assertThat(entity.getTitre()).isEqualTo("Nouveau titre");
        assertThat(entity.getTypeVote()).isEqualTo(TypeVote.REFERENDUM);
    }

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

    @Test
    @DisplayName("updateEntityFromDto avec request null ne modifie pas l'entité (branche null-guard)")
    void updateEntityFromDto_requestNull_entityInchangee() {
        CampagneVote entity = CampagneVote.builder()
                .titre("Invariant")
                .typeVote(TypeVote.ELECTION_BUREAU)
                .modeScrutin(ModeScrutin.MAJORITAIRE_UN_TOUR)
                .build();

        mapper.updateEntityFromDto(null, entity);

        assertThat(entity.getTitre()).isEqualTo("Invariant");
        assertThat(entity.getTypeVote()).isEqualTo(TypeVote.ELECTION_BUREAU);
    }

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

    @Test
    @DisplayName("toDto avec candidats null retourne candidatsExposes null dans le DTO")
    void toDto_candidatsNull_returnsDtoWithNullCandidats() {
        CampagneVote entity = new CampagneVote();
        entity.setId(UUID.randomUUID());
        entity.setTitre("Vote sans candidats");
        entity.setTypeVote(TypeVote.REFERENDUM);
        entity.setModeScrutin(ModeScrutin.MAJORITAIRE_UN_TOUR);
        entity.setCandidats(null);

        CampagneVoteResponse dto = mapper.toDto(entity);

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

    @Test
    @DisplayName("toDto avec candidats vide retourne liste vide dans le DTO")
    void toDto_candidatsVide_returnsDtoWithEmptyCandidats() {
        CampagneVote entity = CampagneVote.builder()
                .titre("Vote vide")
                .typeVote(TypeVote.ELECTION_BUREAU)
                .modeScrutin(ModeScrutin.MAJORITAIRE_UN_TOUR)
                .candidats(Collections.emptyList())
                .build();
        entity.setId(UUID.randomUUID());

        CampagneVoteResponse dto = mapper.toDto(entity);

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

    @Test
    @DisplayName("toDto avec candidats contenant des éléments mappe chaque candidat")
    void toDto_candidatsAvecElements_mappeChaqueCandidats() {
        Candidat candidat1 = Candidat.builder()
                .nomCandidatureOuChoix("Jean Dupont")
                .professionDeFoi("Pour un renouveau associatif")
                .nombreDeVoix(0)
                .pourcentageObtenu(BigDecimal.ZERO)
                .build();
        candidat1.setId(UUID.randomUUID());

        Candidat candidat2 = Candidat.builder()
                .nomCandidatureOuChoix("Marie Martin")
                .professionDeFoi("Pour la transparence")
                .nombreDeVoix(0)
                .pourcentageObtenu(BigDecimal.ZERO)
                .build();
        candidat2.setId(UUID.randomUUID());

        CampagneVote entity = new CampagneVote();
        entity.setId(UUID.randomUUID());
        entity.setTitre("Élection avec candidats");
        entity.setTypeVote(TypeVote.ELECTION_BUREAU);
        entity.setModeScrutin(ModeScrutin.MAJORITAIRE_UN_TOUR);
        entity.setCandidats(List.of(candidat1, candidat2));

        CampagneVoteResponse dto = mapper.toDto(entity);

        assertThat(dto).isNotNull();
        assertThat(dto.getCandidatsExposes()).isNotNull().hasSize(2);
        assertThat(dto.getCandidatsExposes().get(0).getNomCandidatureOuChoix()).isEqualTo("Jean Dupont");
        assertThat(dto.getCandidatsExposes().get(1).getNomCandidatureOuChoix()).isEqualTo("Marie Martin");
    }
}
