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

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.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 lombok.Getter;
import lombok.Setter;

/**
 * DTO pour la gestion des formules d'abonnement UnionFlow Représente les différents plans/formules
 * d'abonnement disponibles dans le catalogue
 *
 * <p>Distinction importante : - FormuleAbonnementDTO = Plans disponibles (BASIC, PREMIUM, etc.) -
 * CATALOGUE - AbonnementDTO = Souscription effective d'une organisation - INSTANCE
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-10
 */
@Getter
@Setter
public class FormuleAbonnementDTO extends BaseDTO {

  private static final long serialVersionUID = 1L;

  /** Types de formules disponibles */
  public enum TypeFormule {
    BASIC("Formule Basique"),
    STANDARD("Formule Standard"),
    PREMIUM("Formule Premium"),
    ENTERPRISE("Formule Entreprise");

    private final String libelle;

    TypeFormule(String libelle) {
      this.libelle = libelle;
    }

    public String getLibelle() {
      return libelle;
    }
  }

  /** Statuts des formules */
  public enum StatutFormule {
    ACTIVE("Active"),
    INACTIVE("Inactive"),
    ARCHIVEE("Archivée"),
    BIENTOT_DISPONIBLE("Bientôt Disponible");

    private final String libelle;

    StatutFormule(String libelle) {
      this.libelle = libelle;
    }

    public String getLibelle() {
      return libelle;
    }
  }

  /** Nom de la formule */
  @NotBlank(message = "Le nom de la formule est obligatoire")
  @Size(
      min = 2,
      max = 100,
      message = "Le nom de la formule doit contenir entre 2 et 100 caractères")
  private String nom;

  /** Code unique de la formule */
  @NotBlank(message = "Le code de la formule est obligatoire")
  @Pattern(
      regexp = "^[A-Z_]{2,20}$",
      message = "Le code doit contenir uniquement des lettres majuscules et underscores")
  private String code;

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

  /** Type de formule (enum) */
  @NotNull(message = "Le type de formule est obligatoire")
  private TypeFormule type;

  /** Statut de la formule (enum) */
  @NotNull(message = "Le statut est obligatoire")
  private StatutFormule statut;

  /** Prix mensuel de la formule */
  @NotNull(message = "Le prix mensuel est obligatoire")
  @DecimalMin(value = "0.0", inclusive = false, message = "Le prix mensuel doit être positif")
  @Digits(
      integer = 10,
      fraction = 2,
      message = "Le prix mensuel ne peut avoir plus de 10 chiffres entiers et 2 décimales")
  private BigDecimal prixMensuel;

  /** Prix annuel de la formule (avec remise éventuelle) */
  @DecimalMin(value = "0.0", inclusive = false, message = "Le prix annuel doit être positif")
  @Digits(
      integer = 10,
      fraction = 2,
      message = "Le prix annuel ne peut avoir plus de 10 chiffres entiers et 2 décimales")
  private BigDecimal prixAnnuel;

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

  /** Nombre maximum de membres autorisés */
  @NotNull(message = "Le nombre maximum de membres est obligatoire")
  private Integer maxMembres;

  /** Nombre maximum d'administrateurs autorisés */
  @NotNull(message = "Le nombre maximum d'administrateurs est obligatoire")
  private Integer maxAdministrateurs;

  /** Espace de stockage alloué en GB */
  @NotNull(message = "L'espace de stockage est obligatoire")
  @DecimalMin(value = "0.1", message = "L'espace de stockage doit être d'au moins 0.1 GB")
  private BigDecimal espaceStockageGB;

  /** Support technique inclus */
  @NotNull(message = "Le support technique doit être spécifié")
  private Boolean supportTechnique;

  /** Niveau de support */
  @Pattern(
      regexp = "^(EMAIL|CHAT|TELEPHONE|PREMIUM)$",
      message = "Le niveau de support doit être EMAIL, CHAT, TELEPHONE ou PREMIUM")
  private String niveauSupport;

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

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

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

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

  /** Sauvegarde automatique incluse */
  private Boolean sauvegardeAutomatique;

  /** Support multi-langues */
  private Boolean multiLangues;

  /** Personnalisation de l'interface */
  private Boolean personnalisationInterface;

  /** Formation incluse */
  private Boolean formationIncluse;

  /** Nombre d'heures de formation incluses */
  private Integer heuresFormation;

  /** Formule populaire (mise en avant) */
  private Boolean populaire;

  /** Formule recommandée */
  private Boolean recommandee;

  /** Période d'essai gratuite en jours */
  private Integer periodeEssaiJours;

  /** Date de début de validité de la formule */
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateDebutValidite;

  /** Date de fin de validité de la formule */
  @JsonFormat(pattern = "yyyy-MM-dd")
  private LocalDate dateFinValidite;

  /** Ordre d'affichage dans le catalogue */
  private Integer ordreAffichage;

  /** Couleur associée à la formule (code hexadécimal) */
  @Pattern(
      regexp = "^#[0-9A-Fa-f]{6}$",
      message = "La couleur doit être un code hexadécimal valide")
  private String couleur;

  /** Icône associée à la formule */
  @Size(max = 50, message = "Le nom de l'icône ne peut pas dépasser 50 caractères")
  private String icone;

  /** Notes administratives */
  @Size(max = 500, message = "Les notes ne peuvent pas dépasser 500 caractères")
  private String notes;

  // Constructeurs
  public FormuleAbonnementDTO() {
    super();
    this.devise = "XOF";
    this.statut = StatutFormule.ACTIVE;
    this.type = TypeFormule.BASIC;
    this.supportTechnique = true;
    this.fonctionnalitesAvancees = false;
    this.apiAccess = false;
    this.rapportsPersonnalises = false;
    this.integrationsTierces = false;
    this.sauvegardeAutomatique = true;
    this.multiLangues = false;
    this.personnalisationInterface = false;
    this.formationIncluse = false;
    this.populaire = false;
    this.recommandee = false;
    this.periodeEssaiJours = 0;
    this.heuresFormation = 0;
    this.ordreAffichage = 1;
  }

  public FormuleAbonnementDTO(String nom, String code, TypeFormule type, BigDecimal prixMensuel) {
    this();
    this.nom = nom;
    this.code = code;
    this.type = type;
    this.prixMensuel = prixMensuel;
  }

  // Getters et Setters
  public String getNom() {
    return nom;
  }

  public void setNom(String nom) {
    this.nom = nom;
  }

  public String getCode() {
    return code;
  }

  public void setCode(String code) {
    this.code = code;
  }

  public String getDescription() {
    return description;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public TypeFormule getType() {
    return type;
  }

  public void setType(TypeFormule type) {
    this.type = type;
  }

  public StatutFormule getStatut() {
    return statut;
  }

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

  public BigDecimal getPrixMensuel() {
    return prixMensuel;
  }

  public void setPrixMensuel(BigDecimal prixMensuel) {
    this.prixMensuel = prixMensuel;
  }

  public BigDecimal getPrixAnnuel() {
    return prixAnnuel;
  }

  public void setPrixAnnuel(BigDecimal prixAnnuel) {
    this.prixAnnuel = prixAnnuel;
  }

  public String getDevise() {
    return devise;
  }

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

  public Integer getMaxMembres() {
    return maxMembres;
  }

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

  public Integer getMaxAdministrateurs() {
    return maxAdministrateurs;
  }

  public void setMaxAdministrateurs(Integer maxAdministrateurs) {
    this.maxAdministrateurs = maxAdministrateurs;
  }

  public BigDecimal getEspaceStockageGB() {
    return espaceStockageGB;
  }

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

  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 Boolean getSauvegardeAutomatique() {
    return sauvegardeAutomatique;
  }

  public void setSauvegardeAutomatique(Boolean sauvegardeAutomatique) {
    this.sauvegardeAutomatique = sauvegardeAutomatique;
  }

  public Boolean getMultiLangues() {
    return multiLangues;
  }

  public void setMultiLangues(Boolean multiLangues) {
    this.multiLangues = multiLangues;
  }

  public Boolean getPersonnalisationInterface() {
    return personnalisationInterface;
  }

  public void setPersonnalisationInterface(Boolean personnalisationInterface) {
    this.personnalisationInterface = personnalisationInterface;
  }

  public Boolean getFormationIncluse() {
    return formationIncluse;
  }

  public void setFormationIncluse(Boolean formationIncluse) {
    this.formationIncluse = formationIncluse;
  }

  public Integer getHeuresFormation() {
    return heuresFormation;
  }

  public void setHeuresFormation(Integer heuresFormation) {
    this.heuresFormation = heuresFormation;
  }

  public Boolean getPopulaire() {
    return populaire;
  }

  public void setPopulaire(Boolean populaire) {
    this.populaire = populaire;
  }

  public Boolean getRecommandee() {
    return recommandee;
  }

  public void setRecommandee(Boolean recommandee) {
    this.recommandee = recommandee;
  }

  public Integer getPeriodeEssaiJours() {
    return periodeEssaiJours;
  }

  public void setPeriodeEssaiJours(Integer periodeEssaiJours) {
    this.periodeEssaiJours = periodeEssaiJours;
  }

  public LocalDate getDateDebutValidite() {
    return dateDebutValidite;
  }

  public void setDateDebutValidite(LocalDate dateDebutValidite) {
    this.dateDebutValidite = dateDebutValidite;
  }

  public LocalDate getDateFinValidite() {
    return dateFinValidite;
  }

  public void setDateFinValidite(LocalDate dateFinValidite) {
    this.dateFinValidite = dateFinValidite;
  }

  public Integer getOrdreAffichage() {
    return ordreAffichage;
  }

  public void setOrdreAffichage(Integer ordreAffichage) {
    this.ordreAffichage = ordreAffichage;
  }

  public String getCouleur() {
    return couleur;
  }

  public void setCouleur(String couleur) {
    this.couleur = couleur;
  }

  public String getIcone() {
    return icone;
  }

  public void setIcone(String icone) {
    this.icone = icone;
  }

  public String getNotes() {
    return notes;
  }

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

  // Méthodes utilitaires

  /**
   * Vérifie si la formule est active
   *
   * @return true si la formule est active
   */
  public boolean isActive() {
    return StatutFormule.ACTIVE.equals(statut);
  }

  /**
   * Vérifie si la formule est inactive
   *
   * @return true si la formule est inactive
   */
  public boolean isInactive() {
    return StatutFormule.INACTIVE.equals(statut);
  }

  /**
   * Vérifie si la formule est archivée
   *
   * @return true si la formule est archivée
   */
  public boolean isArchivee() {
    return StatutFormule.ARCHIVEE.equals(statut);
  }

  /**
   * Vérifie si la formule est actuellement valide
   *
   * @return true si la formule est dans sa période de validité
   */
  public boolean isValide() {
    if (!isActive()) return false;

    LocalDate aujourd = LocalDate.now();

    if (dateDebutValidite != null && aujourd.isBefore(dateDebutValidite)) {
      return false;
    }

    if (dateFinValidite != null && aujourd.isAfter(dateFinValidite)) {
      return false;
    }

    return true;
  }

  /**
   * Calcule l'économie annuelle par rapport au paiement mensuel
   *
   * @return L'économie réalisée en payant annuellement
   */
  public BigDecimal getEconomieAnnuelle() {
    if (prixMensuel == null || prixAnnuel == null) {
      return BigDecimal.ZERO;
    }

    BigDecimal coutMensuelAnnuel = prixMensuel.multiply(BigDecimal.valueOf(12));
    return coutMensuelAnnuel.subtract(prixAnnuel);
  }

  /**
   * Calcule le pourcentage d'économie annuelle
   *
   * @return Le pourcentage d'économie (0-100)
   */
  public int getPourcentageEconomieAnnuelle() {
    if (prixMensuel == null || prixAnnuel == null) return 0;

    BigDecimal coutMensuelAnnuel = prixMensuel.multiply(BigDecimal.valueOf(12));
    if (coutMensuelAnnuel.compareTo(BigDecimal.ZERO) > 0) {
      BigDecimal economie = getEconomieAnnuelle();
      return economie
          .multiply(BigDecimal.valueOf(100))
          .divide(coutMensuelAnnuel, 0, java.math.RoundingMode.HALF_UP)
          .intValue();
    }
    return 0;
  }

  /**
   * Vérifie si la formule a une période d'essai
   *
   * @return true si une période d'essai est disponible
   */
  public boolean hasPeriodeEssai() {
    return periodeEssaiJours != null && periodeEssaiJours > 0;
  }

  /**
   * Vérifie si la formule inclut la formation
   *
   * @return true si la formation est incluse
   */
  public boolean hasFormation() {
    return Boolean.TRUE.equals(formationIncluse) && heuresFormation != null && heuresFormation > 0;
  }

  /**
   * Vérifie si la formule est recommandée ou populaire
   *
   * @return true si la formule est mise en avant
   */
  public boolean isMiseEnAvant() {
    return Boolean.TRUE.equals(populaire) || Boolean.TRUE.equals(recommandee);
  }

  /**
   * Retourne le badge à afficher pour la formule
   *
   * @return Le texte du badge ou null
   */
  public String getBadge() {
    if (Boolean.TRUE.equals(populaire)) return "POPULAIRE";
    if (Boolean.TRUE.equals(recommandee)) return "RECOMMANDÉE";
    if (hasPeriodeEssai()) return "ESSAI GRATUIT";
    return null;
  }

  /**
   * Calcule le score de fonctionnalités (0-100)
   *
   * @return Le score basé sur les fonctionnalités incluses
   */
  public int getScoreFonctionnalites() {
    int score = 0;
    int total = 0;

    // Fonctionnalités de base (poids 1)
    if (Boolean.TRUE.equals(supportTechnique)) score += 10;
    total += 10;

    if (Boolean.TRUE.equals(sauvegardeAutomatique)) score += 10;
    total += 10;

    // Fonctionnalités avancées (poids 2)
    if (Boolean.TRUE.equals(fonctionnalitesAvancees)) score += 15;
    total += 15;

    if (Boolean.TRUE.equals(apiAccess)) score += 15;
    total += 15;

    if (Boolean.TRUE.equals(rapportsPersonnalises)) score += 15;
    total += 15;

    if (Boolean.TRUE.equals(integrationsTierces)) score += 15;
    total += 15;

    // Fonctionnalités premium (poids 3)
    if (Boolean.TRUE.equals(multiLangues)) score += 10;
    total += 10;

    if (Boolean.TRUE.equals(personnalisationInterface)) score += 10;
    total += 10;

    return total > 0 ? (score * 100) / total : 0;
  }

  /**
   * Détermine la classe CSS pour la couleur de la formule
   *
   * @return La classe CSS appropriée
   */
  public String getCssClass() {
    if (type == null) return "formule-default";

    return switch (type) {
      case BASIC -> "formule-basic";
      case STANDARD -> "formule-standard";
      case PREMIUM -> "formule-premium";
      case ENTERPRISE -> "formule-enterprise";
    };
  }

  /** Active la formule */
  public void activer() {
    this.statut = StatutFormule.ACTIVE;
    marquerCommeModifie("SYSTEM");
  }

  /** Désactive la formule */
  public void desactiver() {
    this.statut = StatutFormule.INACTIVE;
    marquerCommeModifie("SYSTEM");
  }

  /** Archive la formule */
  public void archiver() {
    this.statut = StatutFormule.ARCHIVEE;
    marquerCommeModifie("SYSTEM");
  }

  @Override
  public String toString() {
    return "FormuleAbonnementDTO{"
        + "nom='"
        + nom
        + '\''
        + ", code='"
        + code
        + '\''
        + ", type="
        + type
        + ", prixMensuel="
        + prixMensuel
        + ", prixAnnuel="
        + prixAnnuel
        + ", devise='"
        + devise
        + '\''
        + ", statut="
        + statut
        + ", populaire="
        + populaire
        + ", recommandee="
        + recommandee
        + "} "
        + super.toString();
  }
}
