package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.assertj.core.api.Assertions.*;
import static org.hamcrest.Matchers.*;

import dev.lions.unionflow.server.entity.Suggestion;
import dev.lions.unionflow.server.entity.SuggestionVote;
import dev.lions.unionflow.server.repository.SuggestionRepository;
import dev.lions.unionflow.server.repository.SuggestionVoteRepository;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.*;

/**
 * Tests d'intégration pour SuggestionResource
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-12-18
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class SuggestionResourceTest {

  private static final String BASE_PATH = "/api/suggestions";

  @Inject
  SuggestionRepository suggestionRepository;
  @Inject
  SuggestionVoteRepository suggestionVoteRepository;

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

  @BeforeEach
  @Transactional
  void setupTestData() {
    utilisateurId1 = UUID.randomUUID();
    utilisateurId2 = UUID.randomUUID();

    // Créer une suggestion de test
    testSuggestion = Suggestion.builder()
        .utilisateurId(utilisateurId1)
        .utilisateurNom("Test User")
        .titre("Suggestion de Test")
        .description("Description de test")
        .categorie("FEATURE")
        .prioriteEstimee("HAUTE")
        .statut("NOUVELLE")
        .nbVotes(0)
        .nbCommentaires(0)
        .nbVues(0)
        .build();
    testSuggestion.setDateCreation(LocalDateTime.now());
    testSuggestion.setDateSoumission(LocalDateTime.now());
    testSuggestion.setActif(true);
    suggestionRepository.persist(testSuggestion);
  }

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

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

  @Test
  @Order(1)
  @DisplayName("Devrait lister toutes les suggestions")
  @TestSecurity(user = "test@unionflow.com", roles = { "USER" })
  void testListerSuggestions() {
    given()
        .when()
        .get(BASE_PATH)
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("$", isA(List.class))
        .body("size()", greaterThanOrEqualTo(1));
  }

  @Test
  @Order(2)
  @DisplayName("Devrait créer une nouvelle suggestion")
  @TestSecurity(user = "test@unionflow.com", roles = { "USER" })
  void testCreerSuggestion() {
    String jsonBody = """
        {
          "utilisateurId": "%s",
          "utilisateurNom": "Nouvel Utilisateur",
          "titre": "Nouvelle Suggestion via REST",
          "description": "Description de la nouvelle suggestion",
          "categorie": "FEATURE",
          "prioriteEstimee": "HAUTE"
        }
        """
        .formatted(utilisateurId2);

    String createdIdString = given()
        .contentType(ContentType.JSON)
        .body(jsonBody)
        .when()
        .post(BASE_PATH)
        .then()
        .statusCode(201)
        .contentType(ContentType.JSON)
        .body("id", notNullValue())
        .body("titre", equalTo("Nouvelle Suggestion via REST"))
        .body("statut", equalTo("NOUVELLE"))
        .body("nbVotes", equalTo(0))
        .extract()
        .path("id");

    // Cleanup
    UUID createdId = UUID.fromString(createdIdString);
    Suggestion created = suggestionRepository.findById(createdId);
    if (created != null) {
      suggestionRepository.delete(created);
    }
  }

  @Test
  @Order(3)
  @DisplayName("Devrait permettre de voter pour une suggestion")
  @TestSecurity(user = "test@unionflow.com", roles = { "USER" })
  void testVoterPourSuggestion() {
    UUID suggestionId = testSuggestion.getId();

    given()
        .contentType(ContentType.JSON)
        .pathParam("id", suggestionId)
        .queryParam("utilisateurId", utilisateurId2)
        .when()
        .post(BASE_PATH + "/{id}/voter")
        .then()
        .statusCode(200);

    // Vérifier que le vote a été créé
    assertThat(suggestionVoteRepository.aDejaVote(suggestionId, utilisateurId2)).isTrue();

    // Vérifier que le compteur a été mis à jour
    Suggestion updated = suggestionRepository.findById(suggestionId);
    assertThat(updated.getNbVotes()).isEqualTo(1);
  }

  @Test
  @Order(4)
  @DisplayName("Ne devrait pas permettre de voter deux fois")
  @TestSecurity(user = "test@unionflow.com", roles = { "USER" })
  void testNePasPermettreVoteMultiple() {
    UUID suggestionId = testSuggestion.getId();

    // Premier vote
    given()
        .contentType(ContentType.JSON)
        .pathParam("id", suggestionId)
        .queryParam("utilisateurId", utilisateurId2)
        .when()
        .post(BASE_PATH + "/{id}/voter")
        .then()
        .statusCode(200);

    // Tentative de vote multiple
    given()
        .contentType(ContentType.JSON)
        .pathParam("id", suggestionId)
        .queryParam("utilisateurId", utilisateurId2)
        .when()
        .post(BASE_PATH + "/{id}/voter")
        .then()
        .statusCode(400)
        .body("error", notNullValue());
  }

  @Test
  @Order(5)
  @DisplayName("Devrait retourner 404 pour une suggestion inexistante")
  @TestSecurity(user = "test@unionflow.com", roles = { "USER" })
  void testVoterPourSuggestionInexistante() {
    UUID suggestionInexistante = UUID.randomUUID();

    given()
        .contentType(ContentType.JSON)
        .pathParam("id", suggestionInexistante)
        .queryParam("utilisateurId", utilisateurId2)
        .when()
        .post(BASE_PATH + "/{id}/voter")
        .then()
        .statusCode(404)
        .body("error", notNullValue());
  }

  @Test
  @Order(6)
  @DisplayName("Devrait obtenir les statistiques")
  @TestSecurity(user = "test@unionflow.com", roles = { "USER" })
  void testObtenirStatistiques() {
    given()
        .when()
        .get(BASE_PATH + "/statistiques")
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("totalSuggestions", greaterThanOrEqualTo(1))
        .body("suggestionsImplementees", notNullValue())
        .body("totalVotes", notNullValue())
        .body("contributeursActifs", notNullValue());
  }

  @Test
  @Order(7)
  @DisplayName("Devrait synchroniser le compteur de votes après plusieurs votes")
  @TestSecurity(user = "test@unionflow.com", roles = { "USER" })
  void testSynchronisationCompteurVotes() {
    UUID suggestionId = testSuggestion.getId();
    UUID utilisateurId3 = UUID.randomUUID();
    UUID utilisateurId4 = UUID.randomUUID();

    // Créer plusieurs votes
    given()
        .contentType(ContentType.JSON)
        .pathParam("id", suggestionId)
        .queryParam("utilisateurId", utilisateurId2)
        .when()
        .post(BASE_PATH + "/{id}/voter")
        .then()
        .statusCode(200);

    given()
        .contentType(ContentType.JSON)
        .pathParam("id", suggestionId)
        .queryParam("utilisateurId", utilisateurId3)
        .when()
        .post(BASE_PATH + "/{id}/voter")
        .then()
        .statusCode(200);

    given()
        .contentType(ContentType.JSON)
        .pathParam("id", suggestionId)
        .queryParam("utilisateurId", utilisateurId4)
        .when()
        .post(BASE_PATH + "/{id}/voter")
        .then()
        .statusCode(200);

    // Vérifier que le compteur est synchronisé
    Suggestion updated = suggestionRepository.findById(suggestionId);
    assertThat(updated.getNbVotes()).isEqualTo(3);
    assertThat(suggestionVoteRepository.compterVotesParSuggestion(suggestionId)).isEqualTo(3);
  }
}
