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

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import dev.lions.unionflow.server.api.enums.notification.CanalNotification;
import dev.lions.unionflow.server.api.enums.notification.StatutNotification;
import dev.lions.unionflow.server.api.enums.notification.TypeNotification;
import jakarta.validation.constraints.*;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * DTO pour les notifications UnionFlow
 *
 * <p>Ce DTO représente une notification complète avec toutes ses propriétés, métadonnées et
 * informations de suivi.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class NotificationDTO {

  /** Identifiant unique de la notification */
  private String id;

  /** Type de notification */
  @NotNull(message = "Le type de notification est obligatoire")
  private TypeNotification typeNotification;

  /** Statut actuel de la notification */
  @NotNull(message = "Le statut de notification est obligatoire")
  private StatutNotification statut;

  /** Canal de notification utilisé */
  @NotNull(message = "Le canal de notification est obligatoire")
  private CanalNotification canal;

  /** Titre de la notification */
  @NotBlank(message = "Le titre ne peut pas être vide")
  @Size(max = 100, message = "Le titre ne peut pas dépasser 100 caractères")
  private String titre;

  /** Corps du message de la notification */
  @NotBlank(message = "Le message ne peut pas être vide")
  @Size(max = 500, message = "Le message ne peut pas dépasser 500 caractères")
  private String message;

  /** Message court pour l'affichage dans la barre de notification */
  @Size(max = 150, message = "Le message court ne peut pas dépasser 150 caractères")
  private String messageCourt;

  /** Identifiant de l'expéditeur */
  private String expediteurId;

  /** Nom de l'expéditeur */
  private String expediteurNom;

  /** Liste des identifiants des destinataires */
  @NotEmpty(message = "Au moins un destinataire est requis")
  private List<String> destinatairesIds;

  /** Identifiant de l'organisation concernée */
  private String organisationId;

  /** Données personnalisées de la notification */
  private Map<String, Object> donneesPersonnalisees;

  /** URL de l'image à afficher (optionnel) */
  private String imageUrl;

  /** URL de l'icône personnalisée (optionnel) */
  private String iconeUrl;

  /** Action à exécuter lors du clic */
  private String actionClic;

  /** Paramètres de l'action */
  private Map<String, String> parametresAction;

  /** Boutons d'action rapide */
  private List<ActionNotificationDTO> actionsRapides;

  /** Date et heure de création */
  @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
  private LocalDateTime dateCreation;

  /** Date et heure d'envoi programmé */
  @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
  private LocalDateTime dateEnvoiProgramme;

  /** Date et heure d'envoi effectif */
  @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
  private LocalDateTime dateEnvoi;

  /** Date et heure d'expiration */
  @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
  private LocalDateTime dateExpiration;

  /** Date et heure de dernière lecture */
  @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")
  private LocalDateTime dateDerniereLecture;

  /** Priorité de la notification (1=basse, 5=haute) */
  @Min(value = 1, message = "La priorité doit être comprise entre 1 et 5")
  @Max(value = 5, message = "La priorité doit être comprise entre 1 et 5")
  private Integer priorite;

  /** Nombre de tentatives d'envoi */
  private Integer nombreTentatives;

  /** Nombre maximum de tentatives autorisées */
  private Integer maxTentatives;

  /** Délai entre les tentatives en minutes */
  private Integer delaiTentativesMinutes;

  /** Indique si la notification doit vibrer */
  private Boolean doitVibrer;

  /** Indique si la notification doit émettre un son */
  private Boolean doitEmettreSon;

  /** Indique si la notification doit allumer la LED */
  private Boolean doitAllumerLED;

  /** Pattern de vibration personnalisé */
  private long[] patternVibration;

  /** Son personnalisé à jouer */
  private String sonPersonnalise;

  /** Couleur de la LED */
  private String couleurLED;

  /** Indique si la notification est lue */
  private Boolean estLue;

  /** Indique si la notification est marquée comme importante */
  private Boolean estImportante;

  /** Indique si la notification est archivée */
  private Boolean estArchivee;

  /** Nombre de fois que la notification a été affichée */
  private Integer nombreAffichages;

  /** Nombre de clics sur la notification */
  private Integer nombreClics;

  /** Taux de livraison (pourcentage) */
  private Double tauxLivraison;

  /** Taux d'ouverture (pourcentage) */
  private Double tauxOuverture;

  /** Temps moyen de lecture en secondes */
  private Integer tempsMoyenLectureSecondes;

  /** Message d'erreur en cas d'échec */
  private String messageErreur;

  /** Code d'erreur technique */
  private String codeErreur;

  /** Trace de la pile d'erreur (pour debug) */
  private String traceErreur;

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

  /** Tags pour catégorisation */
  private List<String> tags;

  /** Identifiant de la campagne (si applicable) */
  private String campagneId;

  /** Version de l'application qui a créé la notification */
  private String versionApp;

  /** Plateforme cible (android, ios, web) */
  private String plateforme;

  /** Token FCM du destinataire (usage interne) */
  private String tokenFCM;

  /** Identifiant de suivi externe */
  private String idSuiviExterne;

  // === CONSTRUCTEURS ===

  /** Constructeur par défaut */
  public NotificationDTO() {
    this.dateCreation = LocalDateTime.now();
    this.statut = StatutNotification.BROUILLON;
    this.nombreTentatives = 0;
    this.maxTentatives = 3;
    this.delaiTentativesMinutes = 5;
    this.estLue = false;
    this.estImportante = false;
    this.estArchivee = false;
    this.nombreAffichages = 0;
    this.nombreClics = 0;
  }

  /** Constructeur avec paramètres essentiels */
  public NotificationDTO(
      TypeNotification typeNotification,
      String titre,
      String message,
      List<String> destinatairesIds) {
    this();
    this.typeNotification = typeNotification;
    this.titre = titre;
    this.message = message;
    this.destinatairesIds = destinatairesIds;
    this.canal = CanalNotification.valueOf(typeNotification.getCanalNotification());
    this.priorite = typeNotification.getNiveauPriorite();
    this.doitVibrer = typeNotification.doitVibrer();
    this.doitEmettreSon = typeNotification.doitEmettreSon();
  }

  // === GETTERS ET SETTERS ===

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }

  public TypeNotification getTypeNotification() {
    return typeNotification;
  }

  public void setTypeNotification(TypeNotification typeNotification) {
    this.typeNotification = typeNotification;
  }

  public StatutNotification getStatut() {
    return statut;
  }

  public void setStatut(StatutNotification statut) {
    this.statut = statut;
  }

  public CanalNotification getCanal() {
    return canal;
  }

  public void setCanal(CanalNotification canal) {
    this.canal = canal;
  }

  public String getTitre() {
    return titre;
  }

  public void setTitre(String titre) {
    this.titre = titre;
  }

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }

  public String getMessageCourt() {
    return messageCourt;
  }

  public void setMessageCourt(String messageCourt) {
    this.messageCourt = messageCourt;
  }

  public String getExpediteurId() {
    return expediteurId;
  }

  public void setExpediteurId(String expediteurId) {
    this.expediteurId = expediteurId;
  }

  public String getExpediteurNom() {
    return expediteurNom;
  }

  public void setExpediteurNom(String expediteurNom) {
    this.expediteurNom = expediteurNom;
  }

  public List<String> getDestinatairesIds() {
    return destinatairesIds;
  }

  public void setDestinatairesIds(List<String> destinatairesIds) {
    this.destinatairesIds = destinatairesIds;
  }

  public String getOrganisationId() {
    return organisationId;
  }

  public void setOrganisationId(String organisationId) {
    this.organisationId = organisationId;
  }

  public Map<String, Object> getDonneesPersonnalisees() {
    return donneesPersonnalisees;
  }

  public void setDonneesPersonnalisees(Map<String, Object> donneesPersonnalisees) {
    this.donneesPersonnalisees = donneesPersonnalisees;
  }

  public String getImageUrl() {
    return imageUrl;
  }

  public void setImageUrl(String imageUrl) {
    this.imageUrl = imageUrl;
  }

  public String getIconeUrl() {
    return iconeUrl;
  }

  public void setIconeUrl(String iconeUrl) {
    this.iconeUrl = iconeUrl;
  }

  public String getActionClic() {
    return actionClic;
  }

  public void setActionClic(String actionClic) {
    this.actionClic = actionClic;
  }

  public Map<String, String> getParametresAction() {
    return parametresAction;
  }

  public void setParametresAction(Map<String, String> parametresAction) {
    this.parametresAction = parametresAction;
  }

  public List<ActionNotificationDTO> getActionsRapides() {
    return actionsRapides;
  }

  public void setActionsRapides(List<ActionNotificationDTO> actionsRapides) {
    this.actionsRapides = actionsRapides;
  }

  // Getters/Setters pour les dates
  public LocalDateTime getDateCreation() {
    return dateCreation;
  }

  public void setDateCreation(LocalDateTime dateCreation) {
    this.dateCreation = dateCreation;
  }

  public LocalDateTime getDateEnvoiProgramme() {
    return dateEnvoiProgramme;
  }

  public void setDateEnvoiProgramme(LocalDateTime dateEnvoiProgramme) {
    this.dateEnvoiProgramme = dateEnvoiProgramme;
  }

  public LocalDateTime getDateEnvoi() {
    return dateEnvoi;
  }

  public void setDateEnvoi(LocalDateTime dateEnvoi) {
    this.dateEnvoi = dateEnvoi;
  }

  public LocalDateTime getDateExpiration() {
    return dateExpiration;
  }

  public void setDateExpiration(LocalDateTime dateExpiration) {
    this.dateExpiration = dateExpiration;
  }

  public LocalDateTime getDateDerniereLecture() {
    return dateDerniereLecture;
  }

  public void setDateDerniereLecture(LocalDateTime dateDerniereLecture) {
    this.dateDerniereLecture = dateDerniereLecture;
  }

  // Getters/Setters pour les propriétés numériques
  public Integer getPriorite() {
    return priorite;
  }

  public void setPriorite(Integer priorite) {
    this.priorite = priorite;
  }

  public Integer getNombreTentatives() {
    return nombreTentatives;
  }

  public void setNombreTentatives(Integer nombreTentatives) {
    this.nombreTentatives = nombreTentatives;
  }

  public Integer getMaxTentatives() {
    return maxTentatives;
  }

  public void setMaxTentatives(Integer maxTentatives) {
    this.maxTentatives = maxTentatives;
  }

  public Integer getDelaiTentativesMinutes() {
    return delaiTentativesMinutes;
  }

  public void setDelaiTentativesMinutes(Integer delaiTentativesMinutes) {
    this.delaiTentativesMinutes = delaiTentativesMinutes;
  }

  // Getters/Setters pour les propriétés booléennes
  public Boolean getDoitVibrer() {
    return doitVibrer;
  }

  public void setDoitVibrer(Boolean doitVibrer) {
    this.doitVibrer = doitVibrer;
  }

  public Boolean getDoitEmettreSon() {
    return doitEmettreSon;
  }

  public void setDoitEmettreSon(Boolean doitEmettreSon) {
    this.doitEmettreSon = doitEmettreSon;
  }

  public Boolean getDoitAllumerLED() {
    return doitAllumerLED;
  }

  public void setDoitAllumerLED(Boolean doitAllumerLED) {
    this.doitAllumerLED = doitAllumerLED;
  }

  public Boolean getEstLue() {
    return estLue;
  }

  public void setEstLue(Boolean estLue) {
    this.estLue = estLue;
  }

  public Boolean getEstImportante() {
    return estImportante;
  }

  public void setEstImportante(Boolean estImportante) {
    this.estImportante = estImportante;
  }

  public Boolean getEstArchivee() {
    return estArchivee;
  }

  public void setEstArchivee(Boolean estArchivee) {
    this.estArchivee = estArchivee;
  }

  // Getters/Setters pour les propriétés de personnalisation
  public long[] getPatternVibration() {
    return patternVibration;
  }

  public void setPatternVibration(long[] patternVibration) {
    this.patternVibration = patternVibration;
  }

  public String getSonPersonnalise() {
    return sonPersonnalise;
  }

  public void setSonPersonnalise(String sonPersonnalise) {
    this.sonPersonnalise = sonPersonnalise;
  }

  public String getCouleurLED() {
    return couleurLED;
  }

  public void setCouleurLED(String couleurLED) {
    this.couleurLED = couleurLED;
  }

  // Getters/Setters pour les métriques
  public Integer getNombreAffichages() {
    return nombreAffichages;
  }

  public void setNombreAffichages(Integer nombreAffichages) {
    this.nombreAffichages = nombreAffichages;
  }

  public Integer getNombreClics() {
    return nombreClics;
  }

  public void setNombreClics(Integer nombreClics) {
    this.nombreClics = nombreClics;
  }

  public Double getTauxLivraison() {
    return tauxLivraison;
  }

  public void setTauxLivraison(Double tauxLivraison) {
    this.tauxLivraison = tauxLivraison;
  }

  public Double getTauxOuverture() {
    return tauxOuverture;
  }

  public void setTauxOuverture(Double tauxOuverture) {
    this.tauxOuverture = tauxOuverture;
  }

  public Integer getTempsMoyenLectureSecondes() {
    return tempsMoyenLectureSecondes;
  }

  public void setTempsMoyenLectureSecondes(Integer tempsMoyenLectureSecondes) {
    this.tempsMoyenLectureSecondes = tempsMoyenLectureSecondes;
  }

  // Getters/Setters pour la gestion d'erreurs
  public String getMessageErreur() {
    return messageErreur;
  }

  public void setMessageErreur(String messageErreur) {
    this.messageErreur = messageErreur;
  }

  public String getCodeErreur() {
    return codeErreur;
  }

  public void setCodeErreur(String codeErreur) {
    this.codeErreur = codeErreur;
  }

  public String getTraceErreur() {
    return traceErreur;
  }

  public void setTraceErreur(String traceErreur) {
    this.traceErreur = traceErreur;
  }

  // Getters/Setters pour les métadonnées
  public Map<String, Object> getMetadonnees() {
    return metadonnees;
  }

  public void setMetadonnees(Map<String, Object> metadonnees) {
    this.metadonnees = metadonnees;
  }

  public List<String> getTags() {
    return tags;
  }

  public void setTags(List<String> tags) {
    this.tags = tags;
  }

  public String getCampagneId() {
    return campagneId;
  }

  public void setCampagneId(String campagneId) {
    this.campagneId = campagneId;
  }

  public String getVersionApp() {
    return versionApp;
  }

  public void setVersionApp(String versionApp) {
    this.versionApp = versionApp;
  }

  public String getPlateforme() {
    return plateforme;
  }

  public void setPlateforme(String plateforme) {
    this.plateforme = plateforme;
  }

  public String getTokenFCM() {
    return tokenFCM;
  }

  public void setTokenFCM(String tokenFCM) {
    this.tokenFCM = tokenFCM;
  }

  public String getIdSuiviExterne() {
    return idSuiviExterne;
  }

  public void setIdSuiviExterne(String idSuiviExterne) {
    this.idSuiviExterne = idSuiviExterne;
  }

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

  /** Vérifie si la notification est expirée */
  public boolean isExpiree() {
    return dateExpiration != null && LocalDateTime.now().isAfter(dateExpiration);
  }

  /** Vérifie si la notification peut être renvoyée */
  public boolean peutEtreRenvoyee() {
    return nombreTentatives < maxTentatives && !statut.isFinal();
  }

  /** Calcule le taux d'engagement */
  public double getTauxEngagement() {
    if (nombreAffichages == 0) return 0.0;
    return (double) nombreClics / nombreAffichages * 100;
  }

  /** Retourne une représentation courte de la notification */
  @Override
  public String toString() {
    return String.format(
        "NotificationDTO{id='%s', type=%s, statut=%s, titre='%s'}",
        id, typeNotification, statut, titre);
  }
}
