package dev.lions.unionflow.server.resource;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.*;

import dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.security.TestSecurity;
import io.restassured.http.ContentType;
import java.time.LocalDate;
import java.util.List;
import org.junit.jupiter.api.*;

/**
 * Tests d'intégration pour l'endpoint de recherche avancée des membres
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-19
 */
@QuarkusTest
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class MembreResourceAdvancedSearchTest {

  private static final String ADVANCED_SEARCH_ENDPOINT = "/api/membres/search/advanced";

  @Test
  @Order(1)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit fonctionner avec critères valides")
  void testAdvancedSearchWithValidCriteria() {
    MembreSearchCriteria criteria =
        MembreSearchCriteria.builder().query("marie").statut("ACTIF").ageMin(20).ageMax(50).build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .queryParam("page", 0)
        .queryParam("size", 20)
        .queryParam("sort", "nom")
        .queryParam("direction", "asc")
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("membres", notNullValue())
        .body("totalElements", greaterThanOrEqualTo(0))
        .body("totalPages", greaterThanOrEqualTo(0))
        .body("currentPage", equalTo(0))
        .body("pageSize", equalTo(20))
        .body("hasNext", notNullValue())
        .body("hasPrevious", equalTo(false))
        .body("isFirst", equalTo(true))
        .body("executionTimeMs", greaterThanOrEqualTo(0))
        .body("statistics", notNullValue())
        .body("statistics.membresActifs", greaterThanOrEqualTo(0))
        .body("statistics.membresInactifs", greaterThanOrEqualTo(0))
        .body("criteria.query", equalTo("marie"))
        .body("criteria.statut", equalTo("ACTIF"));
  }

  @Test
  @Order(2)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit fonctionner avec critères multiples")
  void testAdvancedSearchWithMultipleCriteria() {
    MembreSearchCriteria criteria =
        MembreSearchCriteria.builder()
            .email("@unionflow.com")
            .dateAdhesionMin(LocalDate.of(2020, 1, 1))
            .dateAdhesionMax(LocalDate.of(2025, 12, 31))
            .roles(List.of("ADMIN", "SUPER_ADMIN"))
            .includeInactifs(false)
            .build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .queryParam("page", 0)
        .queryParam("size", 10)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("membres", notNullValue())
        .body("totalElements", greaterThanOrEqualTo(0))
        .body("criteria.email", equalTo("@unionflow.com"))
        .body("criteria.roles", hasItems("ADMIN", "SUPER_ADMIN"))
        .body("criteria.includeInactifs", equalTo(false));
  }

  @Test
  @Order(3)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit gérer la pagination")
  void testAdvancedSearchPagination() {
    MembreSearchCriteria criteria =
        MembreSearchCriteria.builder()
            .statut("ACTIF") // Ajouter un critère valide
            .includeInactifs(true) // Inclure tous les membres
            .build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .queryParam("page", 0)
        .queryParam("size", 2) // Petite taille pour tester la pagination
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("currentPage", equalTo(0))
        .body("pageSize", equalTo(2))
        .body("isFirst", equalTo(true))
        .body("hasPrevious", equalTo(false));
  }

  @Test
  @Order(4)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit gérer le tri")
  void testAdvancedSearchSorting() {
    MembreSearchCriteria criteria = MembreSearchCriteria.builder().statut("ACTIF").build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .queryParam("sort", "nom")
        .queryParam("direction", "desc")
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("membres", notNullValue());
  }

  @Test
  @Order(5)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit retourner 400 pour critères vides")
  void testAdvancedSearchWithEmptyCriteria() {
    MembreSearchCriteria emptyCriteria = MembreSearchCriteria.builder().build();

    given()
        .contentType(ContentType.JSON)
        .body(emptyCriteria)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(400)
        .contentType(ContentType.JSON)
        .body("error", notNullValue());
  }

  @Test
  @Order(6)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit retourner 400 pour critères invalides")
  void testAdvancedSearchWithInvalidCriteria() {
    MembreSearchCriteria invalidCriteria =
        MembreSearchCriteria.builder()
            .ageMin(50)
            .ageMax(30) // Âge max < âge min
            .build();

    given()
        .contentType(ContentType.JSON)
        .body(invalidCriteria)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(400)
        .contentType(ContentType.JSON)
        .body("error", notNullValue());
  }

  @Test
  @Order(7)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit retourner 400 pour body null")
  void testAdvancedSearchWithNullBody() {
    given()
        .contentType(ContentType.JSON)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(400);
  }

  @Test
  @Order(8)
  @TestSecurity(
      user = "marie.active@unionflow.com",
      roles = {"MEMBRE_ACTIF"})
  @DisplayName("POST /api/membres/search/advanced doit retourner 403 pour utilisateur non autorisé")
  void testAdvancedSearchUnauthorized() {
    MembreSearchCriteria criteria = MembreSearchCriteria.builder().query("test").build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(403);
  }

  @Test
  @Order(9)
  @DisplayName(
      "POST /api/membres/search/advanced doit retourner 401 pour utilisateur non authentifié")
  void testAdvancedSearchUnauthenticated() {
    MembreSearchCriteria criteria = MembreSearchCriteria.builder().query("test").build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(401);
  }

  @Test
  @Order(10)
  @TestSecurity(
      user = "admin@unionflow.com",
      roles = {"ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit fonctionner pour ADMIN")
  void testAdvancedSearchForAdmin() {
    MembreSearchCriteria criteria = MembreSearchCriteria.builder().statut("ACTIF").build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("membres", notNullValue());
  }

  @Test
  @Order(11)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit inclure le temps d'exécution")
  void testAdvancedSearchExecutionTime() {
    MembreSearchCriteria criteria = MembreSearchCriteria.builder().query("test").build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("executionTimeMs", greaterThanOrEqualTo(0))
        .body("executionTimeMs", lessThan(5000)); // Moins de 5 secondes
  }

  @Test
  @Order(12)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit retourner des statistiques complètes")
  void testAdvancedSearchStatistics() {
    MembreSearchCriteria criteria = 
        MembreSearchCriteria.builder()
            .statut("ACTIF") // Ajouter un critère valide
            .includeInactifs(true)
            .build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("statistics", notNullValue())
        .body("statistics.membresActifs", greaterThanOrEqualTo(0))
        .body("statistics.membresInactifs", greaterThanOrEqualTo(0))
        .body("statistics.ageMoyen", notNullValue())
        .body("statistics.ageMin", greaterThanOrEqualTo(0))
        .body("statistics.ageMax", greaterThanOrEqualTo(0))
        .body("statistics.nombreOrganisations", greaterThanOrEqualTo(0))
        .body("statistics.ancienneteMoyenne", notNullValue());
  }

  @Test
  @Order(13)
  @TestSecurity(
      user = "superadmin@unionflow.com",
      roles = {"SUPER_ADMIN"})
  @DisplayName("POST /api/membres/search/advanced doit gérer les caractères spéciaux")
  void testAdvancedSearchWithSpecialCharacters() {
    MembreSearchCriteria criteria =
        MembreSearchCriteria.builder().query("marie-josé").nom("o'connor").build();

    given()
        .contentType(ContentType.JSON)
        .body(criteria)
        .when()
        .post(ADVANCED_SEARCH_ENDPOINT)
        .then()
        .statusCode(200)
        .contentType(ContentType.JSON)
        .body("membres", notNullValue());
  }
}
