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.FormatExport;
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
import jakarta.validation.Valid;
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.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 la configuration des rapports analytics UnionFlow
 *
 * <p>Représente la configuration d'un rapport personnalisé avec ses métriques, sa mise en forme et
 * ses paramètres d'export.
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-16
 */
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReportConfigDTO extends BaseDTO {

  private static final long serialVersionUID = 1L;

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

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

  /** Type de rapport (executif, analytique, technique, operationnel) */
  @NotBlank(message = "Le type de rapport est obligatoire")
  @Size(max = 50, message = "Le type de rapport ne peut pas dépasser 50 caractères")
  private String typeRapport;

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

  /** Date de début personnalisée (si période personnalisée) */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateDebutPersonnalisee;

  /** Date de fin personnalisée (si période personnalisée) */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateFinPersonnalisee;

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

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

  /** Métriques incluses dans le rapport */
  @NotNull(message = "Les métriques sont obligatoires")
  @Valid
  private List<MetriqueConfigDTO> metriques;

  /** Sections du rapport */
  @Valid private List<SectionRapportDTO> sections;

  /** Format d'export par défaut */
  @NotNull(message = "Le format d'export est obligatoire")
  private FormatExport formatExport;

  /** Formats d'export autorisés */
  private List<FormatExport> formatsExportAutorises;

  /** Modèle de rapport à utiliser */
  @Size(max = 100, message = "Le modèle de rapport ne peut pas dépasser 100 caractères")
  private String modeleRapport;

  /** Configuration de la mise en page */
  @Size(
      max = 2000,
      message = "La configuration de mise en page ne peut pas dépasser 2000 caractères")
  private String configurationMiseEnPage;

  /** Logo personnalisé (URL ou base64) */
  @Size(max = 5000, message = "Le logo personnalisé ne peut pas dépasser 5000 caractères")
  private String logoPersonnalise;

  /** Couleurs personnalisées du rapport */
  private Map<String, String> couleursPersonnalisees;

  /** Indicateur si le rapport est public */
  @Builder.Default private Boolean rapportPublic = false;

  /** Indicateur si le rapport est automatique */
  @Builder.Default private Boolean rapportAutomatique = false;

  /** Fréquence de génération automatique (en heures) */
  @DecimalMin(value = "1", message = "La fréquence minimum est 1 heure")
  private Integer frequenceGenerationHeures;

  /** Prochaine génération automatique */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime prochaineGeneration;

  /** Liste des destinataires pour l'envoi automatique */
  private List<String> destinatairesEmail;

  /** Objet de l'email pour l'envoi automatique */
  @Size(max = 200, message = "L'objet de l'email ne peut pas dépasser 200 caractères")
  private String objetEmail;

  /** Corps de l'email pour l'envoi automatique */
  @Size(max = 2000, message = "Le corps de l'email ne peut pas dépasser 2000 caractères")
  private String corpsEmail;

  /** Paramètres de filtrage avancé */
  private Map<String, Object> parametresFiltrage;

  /** Tags pour catégoriser le rapport */
  private List<String> tags;

  /** Niveau de confidentialité (1=public, 5=confidentiel) */
  @DecimalMin(value = "1", message = "Le niveau de confidentialité minimum est 1")
  @DecimalMax(value = "5", message = "Le niveau de confidentialité maximum est 5")
  @Builder.Default
  private Integer niveauConfidentialite = 1;

  /** Date de dernière génération */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateDerniereGeneration;

  /** Nombre de générations effectuées */
  @DecimalMin(value = "0", message = "Le nombre de générations doit être positif")
  @Builder.Default
  private Integer nombreGenerations = 0;

  /** Taille moyenne des rapports générés (en KB) */
  @DecimalMin(value = "0", message = "La taille moyenne doit être positive")
  private Long tailleMoyenneKB;

  /** Temps moyen de génération (en secondes) */
  @DecimalMin(value = "0", message = "Le temps moyen de génération doit être positif")
  private Integer tempsMoyenGenerationSecondes;

  // === CLASSES INTERNES ===

  /** Configuration d'une métrique dans le rapport */
  @Getter
  @Setter
  @Builder
  @NoArgsConstructor
  @AllArgsConstructor
  public static class MetriqueConfigDTO {

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

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

    /** Position dans le rapport (ordre d'affichage) */
    @DecimalMin(value = "1", message = "La position minimum est 1")
    private Integer position;

    /** Taille d'affichage (1=petit, 2=moyen, 3=grand) */
    @DecimalMin(value = "1", message = "La taille minimum est 1")
    @DecimalMax(value = "3", message = "La taille maximum est 3")
    @Builder.Default
    private Integer tailleAffichage = 2;

    /** Couleur personnalisée */
    @Size(max = 7, message = "La couleur doit être au format #RRGGBB")
    private String couleurPersonnalisee;

    /** Indicateur si la métrique inclut un graphique */
    @Builder.Default private Boolean inclureGraphique = true;

    /** Type de graphique (line, bar, pie, area) */
    @Size(max = 20, message = "Le type de graphique ne peut pas dépasser 20 caractères")
    @Builder.Default
    private String typeGraphique = "line";

    /** Indicateur si la métrique inclut la tendance */
    @Builder.Default private Boolean inclureTendance = true;

    /** Indicateur si la métrique inclut la comparaison */
    @Builder.Default private Boolean inclureComparaison = true;

    /** Seuils d'alerte personnalisés */
    private Map<String, Object> seuilsAlerte;

    /** Filtres spécifiques à cette métrique */
    private Map<String, Object> filtresSpecifiques;
  }

  /** Configuration d'une section du rapport */
  @Getter
  @Setter
  @Builder
  @NoArgsConstructor
  @AllArgsConstructor
  public static class SectionRapportDTO {

    /** Nom de la section */
    @NotBlank(message = "Le nom de la section est obligatoire")
    @Size(max = 200, message = "Le nom de la section ne peut pas dépasser 200 caractères")
    private String nom;

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

    /** Position de la section dans le rapport */
    @DecimalMin(value = "1", message = "La position minimum est 1")
    private Integer position;

    /** Type de section (resume, metriques, graphiques, tableaux, analyse) */
    @NotBlank(message = "Le type de section est obligatoire")
    @Size(max = 50, message = "Le type de section ne peut pas dépasser 50 caractères")
    private String typeSection;

    /** Métriques incluses dans cette section */
    private List<TypeMetrique> metriquesIncluses;

    /** Configuration spécifique de la section */
    private Map<String, Object> configurationSection;

    /** Indicateur si la section est visible */
    @Builder.Default private Boolean visible = true;

    /** Indicateur si la section peut être réduite */
    @Builder.Default private Boolean pliable = false;
  }

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

  /**
   * Retourne le nombre de métriques configurées
   *
   * @return Le nombre de métriques
   */
  public int getNombreMetriques() {
    return metriques != null ? metriques.size() : 0;
  }

  /**
   * Retourne le nombre de sections configurées
   *
   * @return Le nombre de sections
   */
  public int getNombreSections() {
    return sections != null ? sections.size() : 0;
  }

  /**
   * Vérifie si le rapport utilise une période personnalisée
   *
   * @return true si la période est personnalisée
   */
  public boolean isPeriodePersonnalisee() {
    return periodeAnalyse == PeriodeAnalyse.PERIODE_PERSONNALISEE;
  }

  /**
   * Vérifie si le rapport est confidentiel (niveau >= 4)
   *
   * @return true si le rapport est confidentiel
   */
  /** Indique si le niveau de confidentialité est élevé (pour couverture branches). */
  private boolean isNiveauConfidentiel() {
    if (niveauConfidentialite == null) return false;
    return niveauConfidentialite >= 4;
  }

  public boolean isConfidentiel() {
    return isNiveauConfidentiel();
  }

  /**
   * Vérifie si le rapport nécessite une génération
   *
   * @return true si la prochaine génération est due
   */
  public boolean necessiteGeneration() {
    return rapportAutomatique
        && prochaineGeneration != null
        && prochaineGeneration.isBefore(LocalDateTime.now());
  }

  /**
   * Retourne la fréquence de génération en texte
   *
   * @return La fréquence sous forme de texte
   */
  public String getFrequenceTexte() {
    if (frequenceGenerationHeures == null) return "Manuelle";

    return switch (frequenceGenerationHeures) {
      case 1 -> "Toutes les heures";
      case 24 -> "Quotidienne";
      case 168 -> "Hebdomadaire"; // 24 * 7
      case 720 -> "Mensuelle"; // 24 * 30
      default -> "Toutes les " + frequenceGenerationHeures + " heures";
    };
  }
}
