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

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

import java.time.LocalDate;
import java.util.Arrays;
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;

@DisplayName("Tests pour MembreSearchCriteria")
class MembreSearchCriteriaTest {

  @Test
  @DisplayName("Test de base - classe peut être instanciée")
  void testClasseInstanciable() {
    MembreSearchCriteria criteria = new MembreSearchCriteria();
    assertThat(criteria).isNotNull();
  }

  @Nested
  @DisplayName("Tests de construction")
  class ConstructionTests {

    @Test
    @DisplayName("Constructeur par défaut")
    void testConstructeurParDefaut() {
      MembreSearchCriteria criteria = new MembreSearchCriteria();

      assertThat(criteria.getIncludeInactifs()).isFalse();
    }

    @Test
    @DisplayName("Builder pattern")
    void testBuilder() {
      UUID orgId = UUID.randomUUID();
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .query("test")
          .nom("Dupont")
          .prenom("Jean")
          .email("jean@example.com")
          .telephone("+221771234567")
          .organisationIds(List.of(orgId))
          .roles(List.of("PRESIDENT"))
          .statut("ACTIF")
          .dateAdhesionMin(LocalDate.of(2020, 1, 1))
          .dateAdhesionMax(LocalDate.of(2025, 12, 31))
          .ageMin(18)
          .ageMax(65)
          .region("Dakar")
          .ville("Dakar")
          .profession("Ingénieur")
          .nationalite("Sénégalaise")
          .membreBureau(true)
          .responsable(false)
          .includeInactifs(true)
          .build();

      assertThat(criteria.getQuery()).isEqualTo("test");
      assertThat(criteria.getNom()).isEqualTo("Dupont");
      assertThat(criteria.getPrenom()).isEqualTo("Jean");
      assertThat(criteria.getEmail()).isEqualTo("jean@example.com");
      assertThat(criteria.getTelephone()).isEqualTo("+221771234567");
      assertThat(criteria.getOrganisationIds()).containsExactly(orgId);
      assertThat(criteria.getRoles()).containsExactly("PRESIDENT");
      assertThat(criteria.getStatut()).isEqualTo("ACTIF");
      assertThat(criteria.getDateAdhesionMin()).isEqualTo(LocalDate.of(2020, 1, 1));
      assertThat(criteria.getDateAdhesionMax()).isEqualTo(LocalDate.of(2025, 12, 31));
      assertThat(criteria.getAgeMin()).isEqualTo(18);
      assertThat(criteria.getAgeMax()).isEqualTo(65);
      assertThat(criteria.getRegion()).isEqualTo("Dakar");
      assertThat(criteria.getVille()).isEqualTo("Dakar");
      assertThat(criteria.getProfession()).isEqualTo("Ingénieur");
      assertThat(criteria.getNationalite()).isEqualTo("Sénégalaise");
      assertThat(criteria.getMembreBureau()).isTrue();
      assertThat(criteria.getResponsable()).isFalse();
      assertThat(criteria.getIncludeInactifs()).isTrue();
    }
  }

  @Nested
  @DisplayName("Tests hasAnyCriteria")
  class HasAnyCriteriaTests {

    @Test
    @DisplayName("hasAnyCriteria - avec query")
    void testHasAnyCriteriaAvecQuery() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .query("test")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec nom")
    void testHasAnyCriteriaAvecNom() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .nom("Dupont")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec prenom")
    void testHasAnyCriteriaAvecPrenom() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .prenom("Jean")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec email")
    void testHasAnyCriteriaAvecEmail() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .email("jean@example.com")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec telephone")
    void testHasAnyCriteriaAvecTelephone() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .telephone("+221771234567")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec organisationIds")
    void testHasAnyCriteriaAvecOrganisationIds() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .organisationIds(List.of(UUID.randomUUID()))
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec roles")
    void testHasAnyCriteriaAvecRoles() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .roles(List.of("PRESIDENT"))
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec statut")
    void testHasAnyCriteriaAvecStatut() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .statut("ACTIF")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec dateAdhesionMin")
    void testHasAnyCriteriaAvecDateAdhesionMin() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .dateAdhesionMin(LocalDate.of(2020, 1, 1))
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec dateAdhesionMax")
    void testHasAnyCriteriaAvecDateAdhesionMax() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .dateAdhesionMax(LocalDate.of(2025, 12, 31))
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec ageMin")
    void testHasAnyCriteriaAvecAgeMin() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .ageMin(18)
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec ageMax")
    void testHasAnyCriteriaAvecAgeMax() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .ageMax(65)
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec region")
    void testHasAnyCriteriaAvecRegion() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .region("Dakar")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec ville")
    void testHasAnyCriteriaAvecVille() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .ville("Dakar")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec profession")
    void testHasAnyCriteriaAvecProfession() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .profession("Ingénieur")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec nationalite")
    void testHasAnyCriteriaAvecNationalite() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .nationalite("Sénégalaise")
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec membreBureau")
    void testHasAnyCriteriaAvecMembreBureau() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .membreBureau(true)
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec responsable")
    void testHasAnyCriteriaAvecResponsable() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .responsable(true)
          .build();

      assertThat(criteria.hasAnyCriteria()).isTrue();
    }

    @Test
    @DisplayName("hasAnyCriteria - sans critères")
    void testHasAnyCriteriaSansCriteres() {
      MembreSearchCriteria criteria = new MembreSearchCriteria();

      assertThat(criteria.hasAnyCriteria()).isFalse();
    }

    @Test
    @DisplayName("hasAnyCriteria - avec chaînes vides")
    void testHasAnyCriteriaAvecChainesVides() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .query("   ")
          .nom("")
          .build();

      assertThat(criteria.hasAnyCriteria()).isFalse();
    }
  }

  @Nested
  @DisplayName("Tests isValid")
  class IsValidTests {

    @Test
    @DisplayName("isValid - critères valides")
    void testIsValid() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .dateAdhesionMin(LocalDate.of(2020, 1, 1))
          .dateAdhesionMax(LocalDate.of(2025, 12, 31))
          .ageMin(18)
          .ageMax(65)
          .build();

      assertThat(criteria.isValid()).isTrue();
    }

    @Test
    @DisplayName("isValid - dates incohérentes")
    void testIsValidDatesIncoherentes() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .dateAdhesionMin(LocalDate.of(2025, 12, 31))
          .dateAdhesionMax(LocalDate.of(2020, 1, 1))
          .build();

      assertThat(criteria.isValid()).isFalse();
    }

    @Test
    @DisplayName("isValid - âges incohérents")
    void testIsValidAgesIncoherents() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .ageMin(65)
          .ageMax(18)
          .build();

      assertThat(criteria.isValid()).isFalse();
    }

    @Test
    @DisplayName("isValid - sans critères")
    void testIsValidSansCriteres() {
      MembreSearchCriteria criteria = new MembreSearchCriteria();

      assertThat(criteria.isValid()).isTrue();
    }
  }

  @Nested
  @DisplayName("Tests sanitize")
  class SanitizeTests {

    @Test
    @DisplayName("sanitize - nettoie les chaînes")
    void testSanitize() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .query("  test  ")
          .nom("  Dupont  ")
          .prenom("")
          .email("  jean@example.com  ")
          .build();

      criteria.sanitize();

      assertThat(criteria.getQuery()).isEqualTo("test");
      assertThat(criteria.getNom()).isEqualTo("Dupont");
      assertThat(criteria.getPrenom()).isNull();
      assertThat(criteria.getEmail()).isEqualTo("jean@example.com");
    }

    @Test
    @DisplayName("sanitize - avec valeurs null")
    void testSanitizeAvecNull() {
      MembreSearchCriteria criteria = new MembreSearchCriteria();

      criteria.sanitize();

      assertThat(criteria.getQuery()).isNull();
      assertThat(criteria.getNom()).isNull();
    }
  }

  @Nested
  @DisplayName("Tests getDescription")
  class GetDescriptionTests {

    @Test
    @DisplayName("getDescription - avec tous les critères")
    void testGetDescriptionAvecTousCriteres() {
      UUID orgId = UUID.randomUUID();
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .query("test")
          .nom("Dupont")
          .prenom("Jean")
          .email("jean@example.com")
          .statut("ACTIF")
          .organisationIds(List.of(orgId))
          .roles(List.of("PRESIDENT", "SECRETAIRE"))
          .dateAdhesionMin(LocalDate.of(2020, 1, 1))
          .dateAdhesionMax(LocalDate.of(2025, 12, 31))
          .ageMin(18)
          .ageMax(65)
          .region("Dakar")
          .ville("Dakar")
          .profession("Ingénieur")
          .nationalite("Sénégalaise")
          .membreBureau(true)
          .responsable(true)
          .build();

      String description = criteria.getDescription();

      assertThat(description).contains("Recherche: 'test'");
      assertThat(description).contains("Nom: 'Dupont'");
      assertThat(description).contains("Prénom: 'Jean'");
      assertThat(description).contains("Email: 'jean@example.com'");
      assertThat(description).contains("Statut: ACTIF");
      assertThat(description).contains("Organisations: 1");
      assertThat(description).contains("Rôles: PRESIDENT, SECRETAIRE");
      assertThat(description).contains("Adhésion >=");
      assertThat(description).contains("Adhésion <=");
      assertThat(description).contains("Âge >=");
      assertThat(description).contains("Âge <=");
      assertThat(description).contains("Région: 'Dakar'");
      assertThat(description).contains("Ville: 'Dakar'");
      assertThat(description).contains("Profession: 'Ingénieur'");
      assertThat(description).contains("Nationalité: 'Sénégalaise'");
      assertThat(description).contains("Membre bureau");
      assertThat(description).contains("Responsable");
    }

    @Test
    @DisplayName("getDescription - sans critères")
    void testGetDescriptionSansCriteres() {
      MembreSearchCriteria criteria = new MembreSearchCriteria();

      String description = criteria.getDescription();

      assertThat(description).isEmpty();
    }
  }
}

