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

import com.fasterxml.jackson.annotation.JsonProperty;
import dev.lions.unionflow.server.api.dto.membre.response.MembreSummaryResponse;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.eclipse.microprofile.openapi.annotations.media.Schema;

/**
 * DTO pour les résultats de recherche avancée des membres Contient les
 * résultats paginés et les
 * métadonnées de recherche
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-19
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema(description = "Résultats de recherche avancée des membres avec pagination")
public class MembreSearchResultDTO {

  /** Liste des membres trouvés */
  @Schema(description = "Liste des membres correspondant aux critères")
  private List<MembreSummaryResponse> membres;

  /** Nombre total de résultats (toutes pages confondues) */
  @Schema(description = "Nombre total de résultats trouvés", example = "247")
  private long totalElements;

  /** Nombre total de pages */
  @Schema(description = "Nombre total de pages", example = "13")
  private int totalPages;

  /** Numéro de la page actuelle (0-based) */
  @Schema(description = "Numéro de la page actuelle", example = "0")
  private int currentPage;

  /** Taille de la page */
  @Schema(description = "Nombre d'éléments par page", example = "20")
  private int pageSize;

  /** Nombre d'éléments sur la page actuelle */
  @Schema(description = "Nombre d'éléments sur cette page", example = "20")
  private int numberOfElements;

  /** Indique s'il y a une page suivante */
  @Schema(description = "Indique s'il y a une page suivante")
  private boolean hasNext;

  /** Indique s'il y a une page précédente */
  @Schema(description = "Indique s'il y a une page précédente")
  private boolean hasPrevious;

  /** Indique si c'est la première page */
  @Schema(description = "Indique si c'est la première page")
  @JsonProperty("isFirst")
  private boolean isFirst;

  /** Indique si c'est la dernière page */
  @Schema(description = "Indique si c'est la dernière page")
  @JsonProperty("isLast")
  private boolean isLast;

  /** Critères de recherche utilisés */
  @Schema(description = "Critères de recherche qui ont été appliqués")
  private MembreSearchCriteria criteria;

  /** Temps d'exécution de la recherche en millisecondes */
  @Schema(description = "Temps d'exécution de la recherche en ms", example = "45")
  private long executionTimeMs;

  /** Statistiques de recherche */
  @Schema(description = "Statistiques sur les résultats de recherche")
  private SearchStatistics statistics;

  /** Statistiques sur les résultats de recherche */
  @Data
  @NoArgsConstructor
  @AllArgsConstructor
  @Builder
  @Schema(description = "Statistiques sur les résultats de recherche")
  public static class SearchStatistics {

    /** Répartition par statut */
    @Schema(description = "Nombre de membres actifs dans les résultats")
    private long membresActifs;

    @Schema(description = "Nombre de membres inactifs dans les résultats")
    private long membresInactifs;

    /** Répartition par âge */
    @Schema(description = "Âge moyen des membres trouvés")
    private double ageMoyen;

    @Schema(description = "Âge minimum des membres trouvés")
    private int ageMin;

    @Schema(description = "Âge maximum des membres trouvés")
    private int ageMax;

    /** Répartition par organisation */
    @Schema(description = "Nombre d'organisations représentées")
    private long nombreOrganisations;

    /** Répartition par région */
    @Schema(description = "Nombre de régions représentées")
    private long nombreRegions;

    /** Ancienneté moyenne */
    @Schema(description = "Ancienneté moyenne en années")
    private double ancienneteMoyenne;
  }

  /** Calcule et met à jour les indicateurs de pagination */
  public void calculatePaginationFlags() {
    this.isFirst = currentPage == 0;
    this.isLast = currentPage >= totalPages - 1;
    this.hasPrevious = currentPage > 0;
    this.hasNext = currentPage < totalPages - 1;
    this.numberOfElements = membres != null ? membres.size() : 0;
  }

  /**
   * Vérifie si les résultats sont vides
   *
   * @return true si aucun résultat
   */
  public boolean isEmpty() {
    return membres == null || membres.isEmpty();
  }

  /**
   * Retourne le numéro de la page suivante (1-based pour affichage)
   *
   * @return Numéro de page suivante ou -1 si pas de page suivante
   */
  public int getNextPageNumber() {
    return hasNext ? currentPage + 2 : -1;
  }

  /**
   * Retourne le numéro de la page précédente (1-based pour affichage)
   *
   * @return Numéro de page précédente ou -1 si pas de page précédente
   */
  public int getPreviousPageNumber() {
    return hasPrevious ? currentPage : -1;
  }

  /**
   * Retourne une description textuelle des résultats
   *
   * @return Description des résultats
   */
  public String getResultDescription() {
    if (isEmpty()) {
      return "Aucun membre trouvé";
    }

    if (totalElements == 1) {
      return "1 membre trouvé";
    }

    if (totalPages == 1) {
      return String.format("%d membres trouvés", totalElements);
    }

    int startElement = currentPage * pageSize + 1;
    int endElement = Math.min(startElement + numberOfElements - 1, (int) totalElements);

    return String.format(
        "Membres %d-%d sur %d (page %d/%d)",
        startElement, endElement, totalElements, currentPage + 1, totalPages);
  }

  /**
   * Factory method pour créer un résultat vide
   *
   * @param criteria Critères de recherche
   * @return Résultat vide
   */
  public static MembreSearchResultDTO empty(MembreSearchCriteria criteria) {
    return empty(criteria, 20, 0);
  }

  /**
   * Factory method pour créer un résultat vide avec pageSize spécifique
   *
   * @param criteria    Critères de recherche
   * @param pageSize    Taille de la page
   * @param currentPage Page actuelle
   * @return Résultat vide
   */
  public static MembreSearchResultDTO empty(MembreSearchCriteria criteria, int pageSize, int currentPage) {
    MembreSearchResultDTO result = new MembreSearchResultDTO();
    result.setMembres(List.of());
    result.setTotalElements(0L);
    result.setTotalPages(0);
    result.setCurrentPage(currentPage);
    result.setPageSize(pageSize);
    result.setNumberOfElements(0);
    result.setHasNext(false);
    result.setHasPrevious(false);
    result.isFirst = true; // Assignation directe pour éviter les problèmes avec les setters Lombok
    result.isLast = true; // Assignation directe pour éviter les problèmes avec les setters Lombok
    result.setCriteria(criteria);
    result.setExecutionTimeMs(0L);
    // Initialiser statistics avec des valeurs vides
    result.setStatistics(SearchStatistics.builder()
        .membresActifs(0)
        .membresInactifs(0)
        .ageMoyen(0.0)
        .ageMin(0)
        .ageMax(0)
        .nombreOrganisations(0)
        .nombreRegions(0)
        .ancienneteMoyenne(0.0)
        .build());
    return result;
  }
}
