package dev.lions.unionflow.server.repository;

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

import dev.lions.unionflow.server.entity.Suggestion;
import dev.lions.unionflow.server.entity.SuggestionVote;
import io.quarkus.test.junit.QuarkusTest;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.junit.jupiter.api.*;

/**
 * Tests unitaires pour SuggestionVoteRepository
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-12-18
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class SuggestionVoteRepositoryTest {

  @Inject SuggestionVoteRepository suggestionVoteRepository;
  @Inject SuggestionRepository suggestionRepository;

  private Suggestion testSuggestion;
  private UUID utilisateurId1;
  private UUID utilisateurId2;

  @BeforeEach
  @Transactional
  void setupTestData() {
    // Créer une suggestion de test
    testSuggestion =
        Suggestion.builder()
            .utilisateurId(UUID.randomUUID())
            .utilisateurNom("Test User")
            .titre("Suggestion de Test")
            .description("Description de test")
            .statut("NOUVELLE")
            .nbVotes(0)
            .build();
    testSuggestion.setDateCreation(LocalDateTime.now());
    testSuggestion.setDateSoumission(LocalDateTime.now());
    testSuggestion.setActif(true);
    suggestionRepository.persist(testSuggestion);

    // Créer des IDs utilisateur de test
    utilisateurId1 = UUID.randomUUID();
    utilisateurId2 = UUID.randomUUID();
  }

  @AfterEach
  @Transactional
  void cleanupTestData() {
    // Supprimer tous les votes de test
    if (testSuggestion != null && testSuggestion.getId() != null) {
      List<SuggestionVote> votes =
          suggestionVoteRepository.listerVotesParSuggestion(testSuggestion.getId());
      votes.forEach(vote -> suggestionVoteRepository.delete(vote));
    }

    // Supprimer la suggestion de test
    if (testSuggestion != null && testSuggestion.getId() != null) {
      Suggestion suggestionToDelete = suggestionRepository.findById(testSuggestion.getId());
      if (suggestionToDelete != null) {
        suggestionRepository.delete(suggestionToDelete);
      }
    }
  }

  @Test
  @Order(1)
  @DisplayName("Devrait créer un vote pour une suggestion")
  void testCreerVote() {
    // Given
    UUID suggestionId = testSuggestion.getId();

    // When
    SuggestionVote vote =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId1)
            .dateVote(LocalDateTime.now())
            .build();
    vote.setActif(true);
    suggestionVoteRepository.persist(vote);

    // Then
    assertThat(vote.getId()).isNotNull();
    assertThat(vote.getSuggestionId()).isEqualTo(suggestionId);
    assertThat(vote.getUtilisateurId()).isEqualTo(utilisateurId1);
    assertThat(vote.getDateVote()).isNotNull();
    assertThat(vote.getActif()).isTrue();
  }

  @Test
  @Order(2)
  @DisplayName("Devrait vérifier qu'un utilisateur a déjà voté")
  void testADejaVote() {
    // Given
    UUID suggestionId = testSuggestion.getId();

    // Créer un vote
    SuggestionVote vote =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId1)
            .dateVote(LocalDateTime.now())
            .build();
    vote.setActif(true);
    suggestionVoteRepository.persist(vote);

    // When/Then
    assertThat(suggestionVoteRepository.aDejaVote(suggestionId, utilisateurId1)).isTrue();
    assertThat(suggestionVoteRepository.aDejaVote(suggestionId, utilisateurId2)).isFalse();
  }

  @Test
  @Order(3)
  @DisplayName("Devrait trouver un vote spécifique")
  void testTrouverVote() {
    // Given
    UUID suggestionId = testSuggestion.getId();

    SuggestionVote vote =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId1)
            .dateVote(LocalDateTime.now())
            .build();
    vote.setActif(true);
    suggestionVoteRepository.persist(vote);

    // When
    Optional<SuggestionVote> foundVote =
        suggestionVoteRepository.trouverVote(suggestionId, utilisateurId1);

    // Then
    assertThat(foundVote).isPresent();
    assertThat(foundVote.get().getSuggestionId()).isEqualTo(suggestionId);
    assertThat(foundVote.get().getUtilisateurId()).isEqualTo(utilisateurId1);
  }

  @Test
  @Order(4)
  @DisplayName("Devrait compter les votes pour une suggestion")
  void testCompterVotesParSuggestion() {
    // Given
    UUID suggestionId = testSuggestion.getId();

    // Créer plusieurs votes
    SuggestionVote vote1 =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId1)
            .dateVote(LocalDateTime.now())
            .build();
    vote1.setActif(true);
    suggestionVoteRepository.persist(vote1);

    SuggestionVote vote2 =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId2)
            .dateVote(LocalDateTime.now())
            .build();
    vote2.setActif(true);
    suggestionVoteRepository.persist(vote2);

    // When
    long count = suggestionVoteRepository.compterVotesParSuggestion(suggestionId);

    // Then
    assertThat(count).isEqualTo(2);
  }

  @Test
  @Order(5)
  @DisplayName("Devrait lister les votes pour une suggestion")
  void testListerVotesParSuggestion() {
    // Given
    UUID suggestionId = testSuggestion.getId();

    SuggestionVote vote1 =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId1)
            .dateVote(LocalDateTime.now().minusHours(1))
            .build();
    vote1.setActif(true);
    suggestionVoteRepository.persist(vote1);

    SuggestionVote vote2 =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId2)
            .dateVote(LocalDateTime.now())
            .build();
    vote2.setActif(true);
    suggestionVoteRepository.persist(vote2);

    // When
    List<SuggestionVote> votes = suggestionVoteRepository.listerVotesParSuggestion(suggestionId);

    // Then
    assertThat(votes).hasSize(2);
    // Vérifier que les votes sont triés par date décroissante (le plus récent en premier)
    assertThat(votes.get(0).getUtilisateurId()).isEqualTo(utilisateurId2);
    assertThat(votes.get(1).getUtilisateurId()).isEqualTo(utilisateurId1);
  }

  @Test
  @Order(6)
  @DisplayName("Devrait lister les votes d'un utilisateur")
  void testListerVotesParUtilisateur() {
    // Given
    UUID suggestionId = testSuggestion.getId();
    UUID autreSuggestionId = UUID.randomUUID();

    // Créer une autre suggestion
    Suggestion autreSuggestion =
        Suggestion.builder()
            .utilisateurId(UUID.randomUUID())
            .titre("Autre Suggestion")
            .statut("NOUVELLE")
            .build();
    autreSuggestion.setDateCreation(LocalDateTime.now());
    autreSuggestion.setActif(true);
    suggestionRepository.persist(autreSuggestion);
    autreSuggestionId = autreSuggestion.getId();

    // Créer des votes pour les deux suggestions par le même utilisateur
    SuggestionVote vote1 =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId1)
            .dateVote(LocalDateTime.now().minusHours(1))
            .build();
    vote1.setActif(true);
    suggestionVoteRepository.persist(vote1);

    SuggestionVote vote2 =
        SuggestionVote.builder()
            .suggestionId(autreSuggestionId)
            .utilisateurId(utilisateurId1)
            .dateVote(LocalDateTime.now())
            .build();
    vote2.setActif(true);
    suggestionVoteRepository.persist(vote2);

    // When
    List<SuggestionVote> votes = suggestionVoteRepository.listerVotesParUtilisateur(utilisateurId1);

    // Then
    assertThat(votes).hasSize(2);
    // Vérifier que les votes sont triés par date décroissante
    assertThat(votes.get(0).getSuggestionId()).isEqualTo(autreSuggestionId);
    assertThat(votes.get(1).getSuggestionId()).isEqualTo(suggestionId);

    // Cleanup
    suggestionVoteRepository.delete(vote2);
    suggestionRepository.delete(autreSuggestion);
  }

  @Test
  @Order(7)
  @DisplayName("findById retourne null pour UUID inexistant")
  void testFindByIdInexistant() {
    assertThat(suggestionVoteRepository.findById(UUID.randomUUID())).isNull();
  }

  @Test
  @Order(8)
  @DisplayName("listAll retourne une liste")
  void testListAll() {
    List<SuggestionVote> list = suggestionVoteRepository.listAll();
    assertThat(list).isNotNull();
  }

  @Test
  @Order(9)
  @DisplayName("count retourne un nombre >= 0")
  void testCount() {
    assertThat(suggestionVoteRepository.count()).isGreaterThanOrEqualTo(0L);
  }

  @Test
  @Order(10)
  @DisplayName("trouverVote retourne empty pour suggestion/utilisateur sans vote")
  void testTrouverVoteInexistant() {
    Optional<SuggestionVote> opt =
        suggestionVoteRepository.trouverVote(testSuggestion.getId(), UUID.randomUUID());
    assertThat(opt).isEmpty();
  }

  @Test
  @Order(11)
  @DisplayName("compterVotesParSuggestion retourne 0 pour suggestion sans vote")
  void testCompterVotesSuggestionSansVote() {
    // Utiliser une suggestion sans vote (créée dans le test)
    Suggestion s =
        Suggestion.builder()
            .utilisateurId(UUID.randomUUID())
            .titre("Sans vote")
            .statut("NOUVELLE")
            .build();
    s.setDateCreation(LocalDateTime.now());
    s.setActif(true);
    suggestionRepository.persist(s);
    long count = suggestionVoteRepository.compterVotesParSuggestion(s.getId());
    assertThat(count).isEqualTo(0);
    suggestionRepository.delete(s);
  }

  @Test
  @Order(12)
  @DisplayName("Ne devrait pas compter les votes inactifs")
  void testNePasCompterVotesInactifs() {
    // Given
    UUID suggestionId = testSuggestion.getId();

    // Créer un vote actif
    SuggestionVote voteActif =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId1)
            .dateVote(LocalDateTime.now())
            .build();
    voteActif.setActif(true);
    suggestionVoteRepository.persist(voteActif);

    // Créer un vote inactif
    SuggestionVote voteInactif =
        SuggestionVote.builder()
            .suggestionId(suggestionId)
            .utilisateurId(utilisateurId2)
            .dateVote(LocalDateTime.now())
            .build();
    voteInactif.setActif(false);
    suggestionVoteRepository.persist(voteInactif);

    // When
    long count = suggestionVoteRepository.compterVotesParSuggestion(suggestionId);

    // Then
    assertThat(count).isEqualTo(1); // Seul le vote actif est compté
  }
}

