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

import com.fasterxml.jackson.annotation.JsonFormat;
import dev.lions.unionflow.server.api.dto.base.BaseDTO;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;

/**
 * DTO pour la gestion des abonnements UnionFlow Représente un abonnement d'une organisation à une
 * formule
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-10
 */
@Getter
@Setter
public class AbonnementDTO extends BaseDTO {

  private static final long serialVersionUID = 1L;

  /** Numéro de référence unique de l'abonnement */
  @NotBlank(message = "Le numéro de référence est obligatoire")
  @Pattern(
      regexp = "^ABO-\\d{4}-[A-Z0-9]{8}$",
      message = "Format de référence invalide (ABO-YYYY-XXXXXXXX)")
  private String numeroReference;

  /** Identifiant de l'organisation abonnée */
  @NotNull(message = "L'identifiant de l'organisation est obligatoire")
  private UUID organisationId;

  /** Nom de l'organisation abonnée */
  private String nomOrganisation;

  /** Identifiant de la formule d'abonnement */
  @NotNull(message = "L'identifiant de la formule est obligatoire")
  private UUID formulaireId;

  /** Code de la formule */
  private String codeFormule;

  /** Nom de la formule */
  private String nomFormule;

  /** Type de formule (BASIC, STANDARD, PREMIUM, ENTERPRISE) */
  private String typeFormule;

  /** Statut de l'abonnement ACTIF, SUSPENDU, EXPIRE, ANNULE, EN_ATTENTE_PAIEMENT */
  @NotBlank(message = "Le statut est obligatoire")
  @Pattern(
      regexp = "^(ACTIF|SUSPENDU|EXPIRE|ANNULE|EN_ATTENTE_PAIEMENT)$",
      message = "Statut invalide")
  private String statut;

  /** Type d'abonnement (MENSUEL, ANNUEL) */
  @NotBlank(message = "Le type d'abonnement est obligatoire")
  @Pattern(regexp = "^(MENSUEL|ANNUEL)$", message = "Le type doit être MENSUEL ou ANNUEL")
  private String typeAbonnement;

  /** Date de début de l'abonnement */
  @NotNull(message = "La date de début est obligatoire")
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateDebut;

  /** Date de fin de l'abonnement */
  @Future(message = "La date de fin doit être dans le futur")
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateFin;

  /** Date de la prochaine facturation */
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateProchainePeriode;

  /** Montant de l'abonnement */
  @NotNull(message = "Le montant est obligatoire")
  @DecimalMin(value = "0.0", inclusive = false, message = "Le montant doit être positif")
  @Digits(
      integer = 10,
      fraction = 2,
      message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales")
  private BigDecimal montant;

  /** Devise */
  @NotBlank(message = "La devise est obligatoire")
  @Pattern(regexp = "^[A-Z]{3}$", message = "La devise doit être un code ISO à 3 lettres")
  private String devise;

  /** Remise appliquée (pourcentage) */
  @DecimalMin(value = "0.0", message = "La remise doit être positive")
  @DecimalMin(value = "100.0", message = "La remise ne peut pas dépasser 100%")
  private BigDecimal remise;

  /** Montant après remise */
  @DecimalMin(value = "0.0", message = "Le montant final doit être positif")
  @Digits(
      integer = 10,
      fraction = 2,
      message = "Le montant ne peut avoir plus de 10 chiffres entiers et 2 décimales")
  private BigDecimal montantFinal;

  /** Renouvellement automatique */
  private Boolean renouvellementAutomatique;

  /** Période d'essai utilisée */
  private Boolean periodeEssaiUtilisee;

  /** Date de fin de la période d'essai */
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateFinEssai;

  /** Nombre de membres autorisés */
  private Integer maxMembres;

  /** Nombre de membres actuels */
  private Integer nombreMembresActuels;

  /** Espace de stockage alloué (GB) */
  private BigDecimal espaceStockageGB;

  /** Espace de stockage utilisé (GB) */
  private BigDecimal espaceStockageUtilise;

  /** Support technique inclus */
  private Boolean supportTechnique;

  /** Niveau de support */
  private String niveauSupport;

  /** Fonctionnalités avancées activées */
  private Boolean fonctionnalitesAvancees;

  /** Accès API activé */
  private Boolean apiAccess;

  /** Rapports personnalisés activés */
  private Boolean rapportsPersonnalises;

  /** Intégrations tierces activées */
  private Boolean integrationsTierces;

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

  /** Nombre de connexions ce mois */
  private Integer connexionsCeMois;

  /** Identifiant du responsable de l'abonnement */
  private UUID responsableId;

  /** Nom du responsable */
  private String nomResponsable;

  /** Email du responsable */
  private String emailResponsable;

  /** Téléphone du responsable */
  private String telephoneResponsable;

  /** Mode de paiement préféré */
  @Pattern(
      regexp = "^(WAVE_MONEY|ORANGE_MONEY|FREE_MONEY|VIREMENT|CHEQUE|AUTRE)$",
      message = "Mode de paiement invalide")
  private String modePaiementPrefere;

  /** Numéro de téléphone pour paiement mobile */
  @Pattern(regexp = "^\\+?[0-9]{8,15}$", message = "Format de numéro de téléphone invalide")
  private String numeroPaiementMobile;

  /** Historique des paiements (JSON) */
  @Size(max = 5000, message = "L'historique ne peut pas dépasser 5000 caractères")
  private String historiquePaiements;

  /** Notes sur l'abonnement */
  @Size(max = 1000, message = "Les notes ne peuvent pas dépasser 1000 caractères")
  private String notes;

  /** Alertes activées */
  private Boolean alertesActivees;

  /** Notifications par email */
  private Boolean notificationsEmail;

  /** Notifications par SMS */
  private Boolean notificationsSMS;

  /** Date de suspension (si applicable) */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateSuspension;

  /** Raison de la suspension */
  @Size(max = 500, message = "La raison ne peut pas dépasser 500 caractères")
  private String raisonSuspension;

  /** Date d'annulation (si applicable) */
  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
  private LocalDateTime dateAnnulation;

  /** Raison de l'annulation */
  @Size(max = 500, message = "La raison ne peut pas dépasser 500 caractères")
  private String raisonAnnulation;

  // Constructeurs
  public AbonnementDTO() {
    super();
    this.statut = "EN_ATTENTE_PAIEMENT";
    this.devise = "XOF";
    this.renouvellementAutomatique = true;
    this.periodeEssaiUtilisee = false;
    this.supportTechnique = true;
    this.fonctionnalitesAvancees = false;
    this.apiAccess = false;
    this.rapportsPersonnalises = false;
    this.integrationsTierces = false;
    this.connexionsCeMois = 0;
    this.alertesActivees = true;
    this.notificationsEmail = true;
    this.notificationsSMS = false;
    this.numeroReference = genererNumeroReference();
  }

  public AbonnementDTO(UUID organisationId, String nomOrganisation, String typeFormule) {
    this();
    this.organisationId = organisationId;
    this.nomOrganisation = nomOrganisation;
    this.typeFormule = typeFormule;
  }

  // Getters et Setters
  public String getNumeroReference() {
    return numeroReference;
  }

  public void setNumeroReference(String numeroReference) {
    this.numeroReference = numeroReference;
  }

  public UUID getOrganisationId() {
    return organisationId;
  }

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

  public String getNomOrganisation() {
    return nomOrganisation;
  }

  public void setNomOrganisation(String nomOrganisation) {
    this.nomOrganisation = nomOrganisation;
  }

  public UUID getFormulaireId() {
    return formulaireId;
  }

  public void setFormulaireId(UUID formulaireId) {
    this.formulaireId = formulaireId;
  }

  public String getCodeFormule() {
    return codeFormule;
  }

  public void setCodeFormule(String codeFormule) {
    this.codeFormule = codeFormule;
  }

  public String getNomFormule() {
    return nomFormule;
  }

  public void setNomFormule(String nomFormule) {
    this.nomFormule = nomFormule;
  }

  public String getTypeFormule() {
    return typeFormule;
  }

  public void setTypeFormule(String typeFormule) {
    this.typeFormule = typeFormule;
  }

  public String getStatut() {
    return statut;
  }

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

  public String getTypeAbonnement() {
    return typeAbonnement;
  }

  public void setTypeAbonnement(String typeAbonnement) {
    this.typeAbonnement = typeAbonnement;
  }

  public LocalDate getDateDebut() {
    return dateDebut;
  }

  public void setDateDebut(LocalDate dateDebut) {
    this.dateDebut = dateDebut;
  }

  public LocalDate getDateFin() {
    return dateFin;
  }

  public void setDateFin(LocalDate dateFin) {
    this.dateFin = dateFin;
  }

  public LocalDate getDateProchainePeriode() {
    return dateProchainePeriode;
  }

  public void setDateProchainePeriode(LocalDate dateProchainePeriode) {
    this.dateProchainePeriode = dateProchainePeriode;
  }

  public BigDecimal getMontant() {
    return montant;
  }

  public void setMontant(BigDecimal montant) {
    this.montant = montant;
  }

  public String getDevise() {
    return devise;
  }

  public void setDevise(String devise) {
    this.devise = devise;
  }

  // Getters et setters restants (suite)
  public BigDecimal getRemise() {
    return remise;
  }

  public void setRemise(BigDecimal remise) {
    this.remise = remise;
  }

  public BigDecimal getMontantFinal() {
    return montantFinal;
  }

  public void setMontantFinal(BigDecimal montantFinal) {
    this.montantFinal = montantFinal;
  }

  public Boolean getRenouvellementAutomatique() {
    return renouvellementAutomatique;
  }

  public void setRenouvellementAutomatique(Boolean renouvellementAutomatique) {
    this.renouvellementAutomatique = renouvellementAutomatique;
  }

  public Boolean getPeriodeEssaiUtilisee() {
    return periodeEssaiUtilisee;
  }

  public void setPeriodeEssaiUtilisee(Boolean periodeEssaiUtilisee) {
    this.periodeEssaiUtilisee = periodeEssaiUtilisee;
  }

  public LocalDate getDateFinEssai() {
    return dateFinEssai;
  }

  public void setDateFinEssai(LocalDate dateFinEssai) {
    this.dateFinEssai = dateFinEssai;
  }

  public Integer getMaxMembres() {
    return maxMembres;
  }

  public void setMaxMembres(Integer maxMembres) {
    this.maxMembres = maxMembres;
  }

  public Integer getNombreMembresActuels() {
    return nombreMembresActuels;
  }

  public void setNombreMembresActuels(Integer nombreMembresActuels) {
    this.nombreMembresActuels = nombreMembresActuels;
  }

  public BigDecimal getEspaceStockageGB() {
    return espaceStockageGB;
  }

  public void setEspaceStockageGB(BigDecimal espaceStockageGB) {
    this.espaceStockageGB = espaceStockageGB;
  }

  public BigDecimal getEspaceStockageUtilise() {
    return espaceStockageUtilise;
  }

  public void setEspaceStockageUtilise(BigDecimal espaceStockageUtilise) {
    this.espaceStockageUtilise = espaceStockageUtilise;
  }

  public Boolean getSupportTechnique() {
    return supportTechnique;
  }

  public void setSupportTechnique(Boolean supportTechnique) {
    this.supportTechnique = supportTechnique;
  }

  public String getNiveauSupport() {
    return niveauSupport;
  }

  public void setNiveauSupport(String niveauSupport) {
    this.niveauSupport = niveauSupport;
  }

  public Boolean getFonctionnalitesAvancees() {
    return fonctionnalitesAvancees;
  }

  public void setFonctionnalitesAvancees(Boolean fonctionnalitesAvancees) {
    this.fonctionnalitesAvancees = fonctionnalitesAvancees;
  }

  public Boolean getApiAccess() {
    return apiAccess;
  }

  public void setApiAccess(Boolean apiAccess) {
    this.apiAccess = apiAccess;
  }

  public Boolean getRapportsPersonnalises() {
    return rapportsPersonnalises;
  }

  public void setRapportsPersonnalises(Boolean rapportsPersonnalises) {
    this.rapportsPersonnalises = rapportsPersonnalises;
  }

  public Boolean getIntegrationsTierces() {
    return integrationsTierces;
  }

  public void setIntegrationsTierces(Boolean integrationsTierces) {
    this.integrationsTierces = integrationsTierces;
  }

  public LocalDateTime getDateDerniereUtilisation() {
    return dateDerniereUtilisation;
  }

  public void setDateDerniereUtilisation(LocalDateTime dateDerniereUtilisation) {
    this.dateDerniereUtilisation = dateDerniereUtilisation;
  }

  public Integer getConnexionsCeMois() {
    return connexionsCeMois;
  }

  public void setConnexionsCeMois(Integer connexionsCeMois) {
    this.connexionsCeMois = connexionsCeMois;
  }

  public UUID getResponsableId() {
    return responsableId;
  }

  public void setResponsableId(UUID responsableId) {
    this.responsableId = responsableId;
  }

  public String getNomResponsable() {
    return nomResponsable;
  }

  public void setNomResponsable(String nomResponsable) {
    this.nomResponsable = nomResponsable;
  }

  public String getEmailResponsable() {
    return emailResponsable;
  }

  public void setEmailResponsable(String emailResponsable) {
    this.emailResponsable = emailResponsable;
  }

  public String getTelephoneResponsable() {
    return telephoneResponsable;
  }

  public void setTelephoneResponsable(String telephoneResponsable) {
    this.telephoneResponsable = telephoneResponsable;
  }

  public String getModePaiementPrefere() {
    return modePaiementPrefere;
  }

  public void setModePaiementPrefere(String modePaiementPrefere) {
    this.modePaiementPrefere = modePaiementPrefere;
  }

  public String getNumeroPaiementMobile() {
    return numeroPaiementMobile;
  }

  public void setNumeroPaiementMobile(String numeroPaiementMobile) {
    this.numeroPaiementMobile = numeroPaiementMobile;
  }

  public String getHistoriquePaiements() {
    return historiquePaiements;
  }

  public void setHistoriquePaiements(String historiquePaiements) {
    this.historiquePaiements = historiquePaiements;
  }

  public String getNotes() {
    return notes;
  }

  public void setNotes(String notes) {
    this.notes = notes;
  }

  public Boolean getAlertesActivees() {
    return alertesActivees;
  }

  public void setAlertesActivees(Boolean alertesActivees) {
    this.alertesActivees = alertesActivees;
  }

  public Boolean getNotificationsEmail() {
    return notificationsEmail;
  }

  public void setNotificationsEmail(Boolean notificationsEmail) {
    this.notificationsEmail = notificationsEmail;
  }

  public Boolean getNotificationsSMS() {
    return notificationsSMS;
  }

  public void setNotificationsSMS(Boolean notificationsSMS) {
    this.notificationsSMS = notificationsSMS;
  }

  public LocalDateTime getDateSuspension() {
    return dateSuspension;
  }

  public void setDateSuspension(LocalDateTime dateSuspension) {
    this.dateSuspension = dateSuspension;
  }

  public String getRaisonSuspension() {
    return raisonSuspension;
  }

  public void setRaisonSuspension(String raisonSuspension) {
    this.raisonSuspension = raisonSuspension;
  }

  public LocalDateTime getDateAnnulation() {
    return dateAnnulation;
  }

  public void setDateAnnulation(LocalDateTime dateAnnulation) {
    this.dateAnnulation = dateAnnulation;
  }

  public String getRaisonAnnulation() {
    return raisonAnnulation;
  }

  public void setRaisonAnnulation(String raisonAnnulation) {
    this.raisonAnnulation = raisonAnnulation;
  }

  /**
   * Génère un numéro de référence unique
   *
   * @return Le numéro de référence généré
   */
  private String genererNumeroReference() {
    return "ABO-"
        + LocalDate.now().getYear()
        + "-"
        + String.format("%08d", (int) (Math.random() * 100000000));
  }
}
