package dev.lions.unionflow.server.api.dto.membre;

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

import java.time.LocalDate;
import java.util.List;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

/**
 * Tests unitaires pour MembreSearchCriteria.
 * Couvre toutes les branches : hasAnyCriteria, isValid, sanitize, getDescription.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2026-02-21
 */
@DisplayName("Tests MembreSearchCriteria")
class MembreSearchCriteriaTest {

  @Nested
  @DisplayName("hasAnyCriteria")
  class HasAnyCriteria {

    @Test
    @DisplayName("false quand tous les champs null ou vides")
    void testAucunCritere() {
      MembreSearchCriteria c = new MembreSearchCriteria();
      assertThat(c.hasAnyCriteria()).isFalse();
    }

    @Test
    @DisplayName("true quand query non vide")
    void testQuery() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().query("x").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("false quand query vide après trim")
    void testQueryVide() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().query("   ").build();
      assertThat(c.hasAnyCriteria()).isFalse();
    }

    @Test
    @DisplayName("true quand nom défini")
    void testNom() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().nom("Dupont").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand prenom défini")
    void testPrenom() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().prenom("Marie").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand email défini")
    void testEmail() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().email("@x.com").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand telephone défini")
    void testTelephone() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().telephone("+221").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand organisationIds non vide")
    void testOrganisationIds() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .organisationIds(List.of(UUID.randomUUID()))
          .build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("false quand organisationIds vide")
    void testOrganisationIdsVide() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .organisationIds(List.of())
          .build();
      assertThat(c.hasAnyCriteria()).isFalse();
    }

    @Test
    @DisplayName("true quand roles non vide")
    void testRoles() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().roles(List.of("ADMIN")).build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand statut défini")
    void testStatut() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().statut("ACTIF").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand dateAdhesionMin défini")
    void testDateAdhesionMin() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .dateAdhesionMin(LocalDate.of(2020, 1, 1))
          .build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand dateAdhesionMax défini")
    void testDateAdhesionMax() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .dateAdhesionMax(LocalDate.of(2025, 12, 31))
          .build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand ageMin défini")
    void testAgeMin() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().ageMin(18).build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand ageMax défini")
    void testAgeMax() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().ageMax(65).build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand region défini")
    void testRegion() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().region("Dakar").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand ville défini")
    void testVille() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().ville("Dakar").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand profession défini")
    void testProfession() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().profession("Ingénieur").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand nationalite défini")
    void testNationalite() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().nationalite("Sénégalaise").build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand membreBureau défini")
    void testMembreBureau() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().membreBureau(true).build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("true quand responsable défini")
    void testResponsable() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().responsable(true).build();
      assertThat(c.hasAnyCriteria()).isTrue();
    }
  }

  @Nested
  @DisplayName("isValid")
  class IsValid {

    @Test
    @DisplayName("true quand pas de dates ni âges")
    void testSansContraintes() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().query("x").build();
      assertThat(c.isValid()).isTrue();
    }

    @Test
    @DisplayName("false quand dateAdhesionMin après dateAdhesionMax")
    void testDatesIncoherentes() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .dateAdhesionMin(LocalDate.of(2025, 1, 1))
          .dateAdhesionMax(LocalDate.of(2020, 1, 1))
          .build();
      assertThat(c.isValid()).isFalse();
    }

    @Test
    @DisplayName("true quand dateAdhesionMin avant dateAdhesionMax")
    void testDatesCoherentes() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .dateAdhesionMin(LocalDate.of(2020, 1, 1))
          .dateAdhesionMax(LocalDate.of(2025, 1, 1))
          .build();
      assertThat(c.isValid()).isTrue();
    }

    @Test
    @DisplayName("false quand ageMin > ageMax")
    void testAgesIncoherents() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .ageMin(65)
          .ageMax(18)
          .build();
      assertThat(c.isValid()).isFalse();
    }

    @Test
    @DisplayName("true quand ageMin <= ageMax")
    void testAgesCoherents() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .ageMin(18)
          .ageMax(65)
          .build();
      assertThat(c.isValid()).isTrue();
    }
  }

  @Nested
  @DisplayName("sanitize")
  class Sanitize {

    @Test
    @DisplayName("trim et null si vide")
    void testSanitize() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .query("  ab  ")
          .nom("  ")
          .build();
      c.sanitize();
      assertThat(c.getQuery()).isEqualTo("ab");
      assertThat(c.getNom()).isNull();
    }

    @Test
    @DisplayName("null reste null")
    void testSanitizeNull() {
      MembreSearchCriteria c = new MembreSearchCriteria();
      c.sanitize();
      assertThat(c.getQuery()).isNull();
    }
  }

  @Nested
  @DisplayName("getDescription")
  class GetDescription {

    @Test
    @DisplayName("vide quand aucun critère")
    void testVide() {
      MembreSearchCriteria c = new MembreSearchCriteria();
      assertThat(c.getDescription()).isEmpty();
    }

    @Test
    @DisplayName("contient query")
    void testAvecQuery() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().query("marie").build();
      assertThat(c.getDescription()).contains("marie");
    }

    @Test
    @DisplayName("contient nom et prénom")
    void testAvecNomPrenom() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .nom("Dupont")
          .prenom("Marie")
          .build();
      String d = c.getDescription();
      assertThat(d).contains("Dupont");
      assertThat(d).contains("Marie");
    }

    @Test
    @DisplayName("contient statut")
    void testAvecStatut() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().statut("ACTIF").build();
      assertThat(c.getDescription()).contains("ACTIF");
    }

    @Test
    @DisplayName("contient organisationIds size")
    void testAvecOrganisationIds() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .organisationIds(List.of(UUID.randomUUID(), UUID.randomUUID()))
          .build();
      assertThat(c.getDescription()).contains("2");
    }

    @Test
    @DisplayName("contient roles")
    void testAvecRoles() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .roles(List.of("ADMIN", "USER"))
          .build();
      assertThat(c.getDescription()).contains("ADMIN");
      assertThat(c.getDescription()).contains("USER");
    }

    @Test
    @DisplayName("contient dates et âges")
    void testAvecDatesAges() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .dateAdhesionMin(LocalDate.of(2020, 1, 1))
          .dateAdhesionMax(LocalDate.of(2025, 12, 31))
          .ageMin(18)
          .ageMax(65)
          .build();
      String d = c.getDescription();
      assertThat(d).contains("2020-01-01");
      assertThat(d).contains("2025-12-31");
      assertThat(d).contains("18");
      assertThat(d).contains("65");
    }

    @Test
    @DisplayName("contient region, ville, profession, nationalite")
    void testAvecLieu() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .region("Dakar")
          .ville("Plateau")
          .profession("Ingénieur")
          .nationalite("Sénégalaise")
          .build();
      String d = c.getDescription();
      assertThat(d).contains("Dakar");
      assertThat(d).contains("Plateau");
      assertThat(d).contains("Ingénieur");
      assertThat(d).contains("Sénégalaise");
    }

    @Test
    @DisplayName("contient Membre bureau et Responsable")
    void testAvecMembreBureauResponsable() {
      MembreSearchCriteria c = MembreSearchCriteria.builder()
          .membreBureau(true)
          .responsable(true)
          .build();
      String d = c.getDescription();
      assertThat(d).contains("Membre bureau");
      assertThat(d).contains("Responsable");
    }

    @Test
    @DisplayName("membreBureau false pas affiché")
    void testMembreBureauFalse() {
      MembreSearchCriteria c = MembreSearchCriteria.builder().membreBureau(false).build();
      assertThat(c.getDescription()).doesNotContain("Membre bureau");
    }
  }
}
