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.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.time.LocalDateTime;
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 widgets de tableau de bord analytics UnionFlow
 *
 * <p>Représente un widget personnalisable affiché sur le tableau de bord avec sa configuration, sa
 * position et ses données.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class DashboardWidgetResponse extends BaseDTO {

  private static final long serialVersionUID = 1L;

  /** Titre du widget */
  @NotBlank(message = "Le titre du widget est obligatoire")
  @Size(min = 3, max = 200, message = "Le titre du widget doit contenir entre 3 et 200 caractères")
  private String titre;

  /** Description du widget */
  @Size(max = 500, message = "La description ne peut pas dépasser 500 caractères")
  private String description;

  /** Type de widget (kpi, chart, table, gauge, progress, text) */
  @NotBlank(message = "Le type de widget est obligatoire")
  @Size(max = 50, message = "Le type de widget ne peut pas dépasser 50 caractères")
  private String typeWidget;

  /** Type de métrique affiché */
  private TypeMetrique typeMetrique;

  /** Période d'analyse */
  private PeriodeAnalyse periodeAnalyse;

  /** 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 propriétaire */
  @NotNull(message = "L'identifiant de l'utilisateur propriétaire est obligatoire")
  private UUID utilisateurProprietaireId;

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

  /** Position X du widget sur la grille */
  @NotNull(message = "La position X est obligatoire")
  @DecimalMin(value = "0", message = "La position X doit être positive ou nulle")
  private Integer positionX;

  /** Position Y du widget sur la grille */
  @NotNull(message = "La position Y est obligatoire")
  @DecimalMin(value = "0", message = "La position Y doit être positive ou nulle")
  private Integer positionY;

  /** Largeur du widget (en unités de grille) */
  @NotNull(message = "La largeur est obligatoire")
  @DecimalMin(value = "1", message = "La largeur minimum est 1")
  @DecimalMax(value = "12", message = "La largeur maximum est 12")
  private Integer largeur;

  /** Hauteur du widget (en unités de grille) */
  @NotNull(message = "La hauteur est obligatoire")
  @DecimalMin(value = "1", message = "La hauteur minimum est 1")
  @DecimalMax(value = "12", message = "La hauteur maximum est 12")
  private Integer hauteur;

  /** Ordre d'affichage (z-index) */
  @DecimalMin(value = "0", message = "L'ordre d'affichage doit être positif ou nul")
  @Builder.Default
  private Integer ordreAffichage = 0;

  /** Configuration visuelle du widget */
  @Size(max = 5000, message = "La configuration visuelle ne peut pas dépasser 5000 caractères")
  private String configurationVisuelle;

  /** Couleur principale du widget */
  @Size(max = 7, message = "La couleur doit être au format #RRGGBB")
  private String couleurPrincipale;

  /** Couleur secondaire du widget */
  @Size(max = 7, message = "La couleur secondaire doit être au format #RRGGBB")
  private String couleurSecondaire;

  /** Icône du widget */
  @Size(max = 50, message = "L'icône ne peut pas dépasser 50 caractères")
  private String icone;

  /** Indicateur si le widget est visible */
  @Builder.Default private Boolean visible = true;

  /** Indicateur si le widget est redimensionnable */
  @Builder.Default private Boolean redimensionnable = true;

  /** Indicateur si le widget est déplaçable */
  @Builder.Default private Boolean deplacable = true;

  /** Indicateur si le widget peut être supprimé */
  @Builder.Default private Boolean supprimable = true;

  /** Indicateur si le widget se met à jour automatiquement */
  @Builder.Default private Boolean miseAJourAutomatique = true;

  /** Fréquence de mise à jour en secondes */
  @DecimalMin(value = "30", message = "La fréquence minimum est 30 secondes")
  @Builder.Default
  private Integer frequenceMiseAJourSecondes = 300; // 5 minutes par défaut

  /** Date de dernière mise à jour des données */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateDerniereMiseAJour;

  /** Prochaine mise à jour programmée */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime prochaineMiseAJour;

  /** Données du widget (format JSON) */
  @Size(max = 50000, message = "Les données du widget ne peuvent pas dépasser 50000 caractères")
  private String donneesWidget;

  /** Configuration des filtres */
  private Map<String, Object> configurationFiltres;

  /** Configuration des alertes */
  private Map<String, Object> configurationAlertes;

  /** Seuil d'alerte bas */
  private Double seuilAlerteBas;

  /** Seuil d'alerte haut */
  private Double seuilAlerteHaut;

  /** Indicateur si une alerte est active */
  @Builder.Default private Boolean alerteActive = false;

  /** Message d'alerte actuel */
  @Size(max = 500, message = "Le message d'alerte ne peut pas dépasser 500 caractères")
  private String messageAlerte;

  /** Type d'alerte (info, warning, error, success) */
  @Size(max = 20, message = "Le type d'alerte ne peut pas dépasser 20 caractères")
  private String typeAlerte;

  /** Permissions d'accès au widget */
  @Size(max = 1000, message = "Les permissions ne peuvent pas dépasser 1000 caractères")
  private String permissions;

  /** Rôles autorisés à voir le widget */
  @Size(max = 500, message = "Les rôles autorisés ne peuvent pas dépasser 500 caractères")
  private String rolesAutorises;

  /** Template personnalisé du widget */
  @Size(max = 10000, message = "Le template personnalisé ne peut pas dépasser 10000 caractères")
  private String templatePersonnalise;

  /** CSS personnalisé du widget */
  @Size(max = 5000, message = "Le CSS personnalisé ne peut pas dépasser 5000 caractères")
  private String cssPersonnalise;

  /** JavaScript personnalisé du widget */
  @Size(max = 10000, message = "Le JavaScript personnalisé ne peut pas dépasser 10000 caractères")
  private String javascriptPersonnalise;

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

  /** Nombre de vues du widget */
  @DecimalMin(value = "0", message = "Le nombre de vues doit être positif")
  @Builder.Default
  private Long nombreVues = 0L;

  /** Nombre d'interactions avec le widget */
  @DecimalMin(value = "0", message = "Le nombre d'interactions doit être positif")
  @Builder.Default
  private Long nombreInteractions = 0L;

  /** Temps moyen passé sur le widget (en secondes) */
  @DecimalMin(value = "0", message = "Le temps moyen doit être positif")
  private Integer tempsMoyenSecondes;

  /** Taux d'erreur du widget (en pourcentage) */
  @DecimalMin(value = "0.0", message = "Le taux d'erreur doit être positif")
  @DecimalMax(value = "100.0", message = "Le taux d'erreur ne peut pas dépasser 100%")
  @Builder.Default
  private Double tauxErreur = 0.0;

  /** Date de dernière erreur */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateDerniereErreur;

  /** Message de dernière erreur */
  @Size(max = 1000, message = "Le message d'erreur ne peut pas dépasser 1000 caractères")
  private String messageDerniereErreur;

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

  /**
   * Retourne le libellé de la métrique si définie
   *
   * @return Le libellé de la métrique ou null
   */
  public String getLibelleMetrique() {
    return typeMetrique != null ? typeMetrique.getLibelle() : null;
  }

  /**
   * Retourne l'unité de mesure si métrique définie
   *
   * @return L'unité de mesure ou chaîne vide
   */
  public String getUnite() {
    return typeMetrique != null ? typeMetrique.getUnite() : "";
  }

  /**
   * Retourne l'icône de la métrique ou l'icône personnalisée
   *
   * @return L'icône à afficher
   */
  public String getIconeAffichage() {
    if (icone != null && !icone.trim().isEmpty()) {
      return icone;
    }
    return typeMetrique != null ? typeMetrique.getIcone() : "dashboard";
  }

  /**
   * Retourne la couleur de la métrique ou la couleur personnalisée
   *
   * @return La couleur à utiliser
   */
  public String getCouleurAffichage() {
    if (couleurPrincipale != null && !couleurPrincipale.trim().isEmpty()) {
      return couleurPrincipale;
    }
    return typeMetrique != null ? typeMetrique.getCouleur() : "#757575";
  }

  /**
   * Vérifie si le widget nécessite une mise à jour
   *
   * @return true si une mise à jour est nécessaire
   */
  public boolean necessiteMiseAJour() {
    return miseAJourAutomatique
        && prochaineMiseAJour != null
        && prochaineMiseAJour.isBefore(LocalDateTime.now());
  }

  /**
   * Vérifie si le widget est interactif
   *
   * @return true si le widget permet des interactions
   */
  public boolean isInteractif() {
    return "chart".equals(typeWidget) || "table".equals(typeWidget) || "gauge".equals(typeWidget);
  }

  /**
   * Vérifie si le widget affiche des données temps réel
   *
   * @return true si le widget est en temps réel
   */
  /** Indique si la fréquence est en temps réel (pour couverture branches). */
  private boolean isFrequenceTempsReel() {
    if (frequenceMiseAJourSecondes == null) return false;
    return frequenceMiseAJourSecondes <= 60;
  }

  public boolean isTempsReel() {
    return isFrequenceTempsReel();
  }

  /**
   * Retourne la taille du widget (surface occupée)
   *
   * @return La surface en unités de grille
   */
  public int getTailleWidget() {
    return largeur * hauteur;
  }

  /**
   * Vérifie si le widget est grand (surface > 6)
   *
   * @return true si le widget est considéré comme grand
   */
  public boolean isWidgetGrand() {
    return getTailleWidget() > 6;
  }

  /**
   * Vérifie si le widget a des erreurs récentes (< 24h)
   *
   * @return true si des erreurs récentes sont détectées
   */
  public boolean hasErreursRecentes() {
    return dateDerniereErreur != null
        && dateDerniereErreur.isAfter(LocalDateTime.now().minusHours(24));
  }

  /**
   * Retourne le statut du widget
   *
   * @return "actif", "erreur", "inactif" ou "maintenance"
   */
  /** Indique si le taux d'erreur déclenche le statut maintenance (pour couverture branches). */
  private boolean isTauxErreurMaintenance() {
    if (tauxErreur == null) return false;
    return tauxErreur > 10.0;
  }

  public String getStatutWidget() {
    if (hasErreursRecentes()) return "erreur";
    if (!visible) return "inactif";
    if (isTauxErreurMaintenance()) return "maintenance";
    return "actif";
  }
}
