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

import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.constraints.DecimalMax;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * DTO pour les données analytics UnionFlow
 *
 * <p>
 * Représente une donnée analytique avec sa valeur, sa métrique associée, sa
 * période d'analyse et
 * ses métadonnées.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AnalyticsDataResponse extends BaseDTO {

  private static final long serialVersionUID = 1L;

  /** Type de métrique analysée */
  @NotNull(message = "Le type de métrique est obligatoire")
  private TypeMetrique typeMetrique;

  /** Période d'analyse */
  @NotNull(message = "La période d'analyse est obligatoire")
  private PeriodeAnalyse periodeAnalyse;

  /** Valeur numérique de la métrique */
  @NotNull(message = "La valeur est obligatoire")
  @DecimalMin(value = "0.0", message = "La valeur doit être positive ou nulle")
  @Digits(integer = 15, fraction = 4, message = "Format de valeur invalide")
  private BigDecimal valeur;

  /** Valeur précédente pour comparaison */
  @DecimalMin(value = "0.0", message = "La valeur précédente doit être positive ou nulle")
  @Digits(integer = 15, fraction = 4, message = "Format de valeur précédente invalide")
  private BigDecimal valeurPrecedente;

  /** Pourcentage d'évolution par rapport à la période précédente */
  @Digits(integer = 6, fraction = 2, message = "Format de pourcentage d'évolution invalide")
  private BigDecimal pourcentageEvolution;

  /** Date de début de la période analysée */
  @NotNull(message = "La date de début est obligatoire")
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateDebut;

  /** Date de fin de la période analysée */
  @NotNull(message = "La date de fin est obligatoire")
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateFin;

  /** Date de calcul de la métrique */
  @NotNull(message = "La date de calcul est obligatoire")
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateCalcul;

  /** Identifiant de l'organisation (optionnel pour filtrage) */
  private UUID organisationId;

  /** Nom de l'organisation */
  @Size(max = 200, message = "Le nom de l'organisation ne peut pas dépasser 200 caractères")
  private String nomOrganisation;

  /** Identifiant de l'utilisateur qui a demandé le calcul */
  private UUID utilisateurId;

  /** Nom de l'utilisateur qui a demandé le calcul */
  @Size(max = 200, message = "Le nom de l'utilisateur ne peut pas dépasser 200 caractères")
  private String nomUtilisateur;

  /** Libellé personnalisé de la métrique */
  @Size(max = 300, message = "Le libellé personnalisé ne peut pas dépasser 300 caractères")
  private String libellePersonnalise;

  /** Description ou commentaire sur la métrique */
  @Size(max = 1000, message = "La description ne peut pas dépasser 1000 caractères")
  private String description;

  /** Données détaillées pour les graphiques (format JSON) */
  @Size(max = 10000, message = "Les données détaillées ne peuvent pas dépasser 10000 caractères")
  private String donneesDetaillees;

  /** Configuration du graphique (couleurs, type, etc.) */
  @Size(max = 2000, message = "La configuration graphique ne peut pas dépasser 2000 caractères")
  private String configurationGraphique;

  /** Métadonnées additionnelles */
  private Map<String, Object> metadonnees;

  /** Indicateur de fiabilité des données (0-100) */
  @DecimalMin(value = "0.0", message = "L'indicateur de fiabilité doit être positif")
  @DecimalMax(value = "100.0", message = "L'indicateur de fiabilité ne peut pas dépasser 100")
  @Digits(integer = 3, fraction = 1, message = "Format d'indicateur de fiabilité invalide")
  private BigDecimal indicateurFiabilite;

  /** Nombre d'éléments analysés pour calculer cette métrique */
  @DecimalMin(value = "0", message = "Le nombre d'éléments doit être positif")
  private Integer nombreElementsAnalyses;

  /** Temps de calcul en millisecondes */
  @DecimalMin(value = "0", message = "Le temps de calcul doit être positif")
  private Long tempsCalculMs;

  /** Indicateur si la métrique est en temps réel */
  @Builder.Default
  private Boolean tempsReel = false;

  /** Indicateur si la métrique nécessite une mise à jour */
  @Builder.Default
  private Boolean necessiteMiseAJour = false;

  /** Niveau de priorité de la métrique (1=faible, 5=critique) */
  @DecimalMin(value = "1", message = "Le niveau de priorité minimum est 1")
  @DecimalMax(value = "5", message = "Le niveau de priorité maximum est 5")
  private Integer niveauPriorite;

  /** Tags pour catégoriser la métrique */
  private List<String> tags;

  // === MÉTHODES UTILITAIRES ===

  /**
   * Retourne le libellé à afficher (personnalisé ou par défaut)
   *
   * @return Le libellé à afficher
   */
  public String getLibelleAffichage() {
    return libellePersonnalise != null && !libellePersonnalise.trim().isEmpty()
        ? libellePersonnalise
        : typeMetrique.getLibelle();
  }

  /**
   * Retourne l'unité de mesure de la métrique
   *
   * @return L'unité de mesure
   */
  public String getUnite() {
    return typeMetrique.getUnite();
  }

  /**
   * Retourne l'icône de la métrique
   *
   * @return L'icône Material Design
   */
  public String getIcone() {
    return typeMetrique.getIcone();
  }

  /**
   * Retourne la couleur de la métrique
   *
   * @return Le code couleur hexadécimal
   */
  public String getCouleur() {
    return typeMetrique.getCouleur();
  }

  /**
   * Vérifie si la métrique a évolué positivement
   *
   * @return true si l'évolution est positive
   */
  public boolean hasEvolutionPositive() {
    return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) > 0;
  }

  /**
   * Vérifie si la métrique a évolué négativement
   *
   * @return true si l'évolution est négative
   */
  public boolean hasEvolutionNegative() {
    return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) < 0;
  }

  /**
   * Vérifie si la métrique est stable (pas d'évolution)
   *
   * @return true si l'évolution est nulle
   */
  public boolean isStable() {
    return pourcentageEvolution != null && pourcentageEvolution.compareTo(BigDecimal.ZERO) == 0;
  }

  /**
   * Retourne la tendance sous forme de texte
   *
   * @return "hausse", "baisse" ou "stable"
   */
  public String getTendance() {
    if (hasEvolutionPositive())
      return "hausse";
    if (hasEvolutionNegative())
      return "baisse";
    return "stable";
  }

  /**
   * Vérifie si les données sont fiables (indicateur > 80)
   *
   * @return true si les données sont considérées comme fiables
   */
  public boolean isDonneesFiables() {
    return indicateurFiabilite != null
        && indicateurFiabilite.compareTo(new BigDecimal("80.0")) >= 0;
  }

  /**
   * Vérifie si la métrique est critique (priorité >= 4)
   *
   * @return true si la métrique est critique
   */
  public boolean isCritique() {
    return niveauPriorite != null && niveauPriorite >= 4;
  }

  /**
   * Constructeur avec les champs essentiels
   *
   * @param typeMetrique   Le type de métrique
   * @param periodeAnalyse La période d'analyse
   * @param valeur         La valeur de la métrique
   */
  public AnalyticsDataResponse(
      TypeMetrique typeMetrique, PeriodeAnalyse periodeAnalyse, BigDecimal valeur) {
    super();
    this.typeMetrique = typeMetrique;
    this.periodeAnalyse = periodeAnalyse;
    this.valeur = valeur;
    this.dateCalcul = LocalDateTime.now();
    this.dateDebut = periodeAnalyse.getDateDebut();
    this.dateFin = periodeAnalyse.getDateFin();
    this.tempsReel = false;
    this.necessiteMiseAJour = false;
    this.niveauPriorite = 3; // Priorité normale par défaut
    this.indicateurFiabilite = new BigDecimal("95.0"); // Fiabilité élevée par défaut
  }
}
