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

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

import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

@DisplayName("Tests pour MembreSearchResultDTO")
class MembreSearchResultDTOTest {

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

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

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

      assertThat(result).isNotNull();
    }

    @Test
    @DisplayName("Builder pattern")
    void testBuilder() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .membres(List.of(new MembreDTO()))
          .totalElements(100L)
          .totalPages(5)
          .currentPage(0)
          .pageSize(20)
          .numberOfElements(20)
          .hasNext(true)
          .hasPrevious(false)
          .isFirst(true)
          .isLast(false)
          .executionTimeMs(45L)
          .build();

      assertThat(result.getMembres()).hasSize(1);
      assertThat(result.getTotalElements()).isEqualTo(100L);
      assertThat(result.getTotalPages()).isEqualTo(5);
      assertThat(result.getCurrentPage()).isEqualTo(0);
      assertThat(result.getPageSize()).isEqualTo(20);
      assertThat(result.getNumberOfElements()).isEqualTo(20);
      assertThat(result.isHasNext()).isTrue();
      assertThat(result.isHasPrevious()).isFalse();
      assertThat(result.isFirst()).isTrue();
      assertThat(result.isLast()).isFalse();
      assertThat(result.getExecutionTimeMs()).isEqualTo(45L);
    }
  }

  @Nested
  @DisplayName("Tests calculatePaginationFlags")
  class CalculatePaginationFlagsTests {

    @Test
    @DisplayName("calculatePaginationFlags - première page")
    void testCalculatePaginationFlagsPremierePage() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .currentPage(0)
          .totalPages(5)
          .membres(List.of(new MembreDTO(), new MembreDTO()))
          .build();

      result.calculatePaginationFlags();

      assertThat(result.isFirst()).isTrue();
      assertThat(result.isLast()).isFalse();
      assertThat(result.isHasPrevious()).isFalse();
      assertThat(result.isHasNext()).isTrue();
      assertThat(result.getNumberOfElements()).isEqualTo(2);
    }

    @Test
    @DisplayName("calculatePaginationFlags - page du milieu")
    void testCalculatePaginationFlagsPageMilieu() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .currentPage(2)
          .totalPages(5)
          .membres(List.of(new MembreDTO(), new MembreDTO()))
          .build();

      result.calculatePaginationFlags();

      assertThat(result.isFirst()).isFalse();
      assertThat(result.isLast()).isFalse();
      assertThat(result.isHasPrevious()).isTrue();
      assertThat(result.isHasNext()).isTrue();
      assertThat(result.getNumberOfElements()).isEqualTo(2);
    }

    @Test
    @DisplayName("calculatePaginationFlags - dernière page")
    void testCalculatePaginationFlagsDernierePage() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .currentPage(4)
          .totalPages(5)
          .membres(List.of(new MembreDTO()))
          .build();

      result.calculatePaginationFlags();

      assertThat(result.isFirst()).isFalse();
      assertThat(result.isLast()).isTrue();
      assertThat(result.isHasPrevious()).isTrue();
      assertThat(result.isHasNext()).isFalse();
      assertThat(result.getNumberOfElements()).isEqualTo(1);
    }

    @Test
    @DisplayName("calculatePaginationFlags - membres null")
    void testCalculatePaginationFlagsMembresNull() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .currentPage(0)
          .totalPages(1)
          .membres(null)
          .build();

      result.calculatePaginationFlags();

      assertThat(result.getNumberOfElements()).isEqualTo(0);
    }
  }

  @Nested
  @DisplayName("Tests isEmpty")
  class IsEmptyTests {

    @Test
    @DisplayName("isEmpty - liste vide")
    void testIsEmptyListeVide() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .membres(List.of())
          .build();

      assertThat(result.isEmpty()).isTrue();
    }

    @Test
    @DisplayName("isEmpty - membres null")
    void testIsEmptyMembresNull() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .membres(null)
          .build();

      assertThat(result.isEmpty()).isTrue();
    }

    @Test
    @DisplayName("isEmpty - avec membres")
    void testIsEmptyAvecMembres() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .membres(List.of(new MembreDTO()))
          .build();

      assertThat(result.isEmpty()).isFalse();
    }
  }

  @Nested
  @DisplayName("Tests getNextPageNumber")
  class GetNextPageNumberTests {

    @Test
    @DisplayName("getNextPageNumber - avec page suivante")
    void testGetNextPageNumberAvecPageSuivante() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .currentPage(0)
          .totalPages(5)
          .build();
      result.calculatePaginationFlags();

      assertThat(result.getNextPageNumber()).isEqualTo(2);
    }

    @Test
    @DisplayName("getNextPageNumber - sans page suivante")
    void testGetNextPageNumberSansPageSuivante() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .currentPage(4)
          .totalPages(5)
          .build();
      result.calculatePaginationFlags();

      assertThat(result.getNextPageNumber()).isEqualTo(-1);
    }
  }

  @Nested
  @DisplayName("Tests getPreviousPageNumber")
  class GetPreviousPageNumberTests {

    @Test
    @DisplayName("getPreviousPageNumber - avec page précédente")
    void testGetPreviousPageNumberAvecPagePrecedente() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .currentPage(2)
          .totalPages(5)
          .build();
      result.calculatePaginationFlags();

      assertThat(result.getPreviousPageNumber()).isEqualTo(2);
    }

    @Test
    @DisplayName("getPreviousPageNumber - sans page précédente")
    void testGetPreviousPageNumberSansPagePrecedente() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .currentPage(0)
          .totalPages(5)
          .build();
      result.calculatePaginationFlags();

      assertThat(result.getPreviousPageNumber()).isEqualTo(-1);
    }
  }

  @Nested
  @DisplayName("Tests getResultDescription")
  class GetResultDescriptionTests {

    @Test
    @DisplayName("getResultDescription - aucun résultat")
    void testGetResultDescriptionAucunResultat() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .membres(List.of())
          .build();

      assertThat(result.getResultDescription()).isEqualTo("Aucun membre trouvé");
    }

    @Test
    @DisplayName("getResultDescription - un seul résultat")
    void testGetResultDescriptionUnSeulResultat() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .membres(List.of(new MembreDTO()))
          .totalElements(1L)
          .totalPages(1)
          .build();

      assertThat(result.getResultDescription()).isEqualTo("1 membre trouvé");
    }

    @Test
    @DisplayName("getResultDescription - une seule page")
    void testGetResultDescriptionUneSeulePage() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .membres(List.of(new MembreDTO(), new MembreDTO()))
          .totalElements(2L)
          .totalPages(1)
          .currentPage(0)
          .pageSize(20)
          .numberOfElements(2)
          .build();

      assertThat(result.getResultDescription()).isEqualTo("2 membres trouvés");
    }

    @Test
    @DisplayName("getResultDescription - plusieurs pages")
    void testGetResultDescriptionPlusieursPages() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .membres(List.of(new MembreDTO(), new MembreDTO()))
          .totalElements(100L)
          .totalPages(5)
          .currentPage(0)
          .pageSize(20)
          .numberOfElements(20)
          .build();

      String description = result.getResultDescription();
      assertThat(description).contains("Membres 1-20 sur 100");
      assertThat(description).contains("page 1/5");
    }

    @Test
    @DisplayName("getResultDescription - dernière page partielle")
    void testGetResultDescriptionDernierePagePartielle() {
      MembreSearchResultDTO result = MembreSearchResultDTO.builder()
          .membres(List.of(new MembreDTO(), new MembreDTO()))
          .totalElements(42L)
          .totalPages(3)
          .currentPage(2)
          .pageSize(20)
          .numberOfElements(2)
          .build();

      String description = result.getResultDescription();
      assertThat(description).contains("Membres 41-42 sur 42");
      assertThat(description).contains("page 3/3");
    }
  }

  @Nested
  @DisplayName("Tests empty factory method")
  class EmptyFactoryTests {

    @Test
    @DisplayName("empty - crée un résultat vide")
    void testEmpty() {
      MembreSearchCriteria criteria = MembreSearchCriteria.builder()
          .query("test")
          .build();

      MembreSearchResultDTO result = MembreSearchResultDTO.empty(criteria);

      assertThat(result.getMembres()).isEmpty();
      assertThat(result.getTotalElements()).isEqualTo(0L);
      assertThat(result.getTotalPages()).isEqualTo(0);
      assertThat(result.getCurrentPage()).isEqualTo(0);
      assertThat(result.getPageSize()).isEqualTo(20);
      assertThat(result.getNumberOfElements()).isEqualTo(0);
      assertThat(result.isHasNext()).isFalse();
      assertThat(result.isHasPrevious()).isFalse();
      assertThat(result.isFirst()).isTrue();
      assertThat(result.isLast()).isTrue();
      assertThat(result.getCriteria()).isEqualTo(criteria);
      assertThat(result.getExecutionTimeMs()).isEqualTo(0L);
    }
  }

  @Nested
  @DisplayName("Tests SearchStatistics")
  class SearchStatisticsTests {

    @Test
    @DisplayName("SearchStatistics - builder")
    void testSearchStatisticsBuilder() {
      MembreSearchResultDTO.SearchStatistics stats = MembreSearchResultDTO.SearchStatistics.builder()
          .membresActifs(50L)
          .membresInactifs(10L)
          .ageMoyen(35.5)
          .ageMin(18)
          .ageMax(65)
          .nombreOrganisations(5L)
          .nombreRegions(3L)
          .ancienneteMoyenne(5.2)
          .build();

      assertThat(stats.getMembresActifs()).isEqualTo(50L);
      assertThat(stats.getMembresInactifs()).isEqualTo(10L);
      assertThat(stats.getAgeMoyen()).isEqualTo(35.5);
      assertThat(stats.getAgeMin()).isEqualTo(18);
      assertThat(stats.getAgeMax()).isEqualTo(65);
      assertThat(stats.getNombreOrganisations()).isEqualTo(5L);
      assertThat(stats.getNombreRegions()).isEqualTo(3L);
      assertThat(stats.getAncienneteMoyenne()).isEqualTo(5.2);
    }
  }
}

