package dev.lions.unionflow.server.entity;

import dev.lions.unionflow.server.api.enums.abonnement.PlageMembres;
import dev.lions.unionflow.server.api.enums.abonnement.StatutSouscription;
import dev.lions.unionflow.server.api.enums.abonnement.StatutValidationSouscription;
import dev.lions.unionflow.server.api.enums.abonnement.TypePeriodeAbonnement;
import dev.lions.unionflow.server.api.enums.abonnement.TypeOrganisationFacturation;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.UUID;
import lombok.*;

/**
 * Abonnement actif d'une organisation racine à un forfait UnionFlow.
 *
 * <p>Règle clé : quand {@code quotaUtilise >= quotaMax}, toute nouvelle
 * validation d'adhésion est bloquée avec un message explicite.
 * Le manager peut upgrader son forfait à tout moment.
 *
 * <p>Table : {@code souscriptions_organisation}
 */
@Entity
@Table(
    name = "souscriptions_organisation",
    indexes = {
      @Index(name = "idx_souscription_org",    columnList = "organisation_id", unique = true),
      @Index(name = "idx_souscription_statut", columnList = "statut"),
      @Index(name = "idx_souscription_fin",    columnList = "date_fin")
    })
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class SouscriptionOrganisation extends BaseEntity {

  /** Organisation racine abonnée (une seule souscription active par org) */
  @NotNull
  @OneToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "organisation_id", nullable = false, unique = true)
  private Organisation organisation;

  @NotNull
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "formule_id", nullable = false)
  private FormuleAbonnement formule;

  @Enumerated(EnumType.STRING)
  @Builder.Default
  @Column(name = "type_periode", nullable = false, length = 10)
  private TypePeriodeAbonnement typePeriode = TypePeriodeAbonnement.MENSUEL;

  @NotNull
  @Column(name = "date_debut", nullable = false)
  private LocalDate dateDebut;

  @NotNull
  @Column(name = "date_fin", nullable = false)
  private LocalDate dateFin;

  /** Snapshot du quota max au moment de la souscription */
  @Column(name = "quota_max")
  private Integer quotaMax;

  /** Compteur incrémenté à chaque adhésion validée */
  @Builder.Default
  @Min(0)
  @Column(name = "quota_utilise", nullable = false)
  private Integer quotaUtilise = 0;

  @Enumerated(EnumType.STRING)
  @Builder.Default
  @Column(name = "statut", nullable = false, length = 30)
  private StatutSouscription statut = StatutSouscription.ACTIVE;

  @Column(name = "reference_paiement_wave", length = 100)
  private String referencePaiementWave;

  @Column(name = "wave_session_id", length = 255)
  private String waveSessionId;

  @Column(name = "wave_checkout_url", length = 1024)
  private String waveCheckoutUrl;

  @Column(name = "date_dernier_paiement")
  private LocalDate dateDernierPaiement;

  @Column(name = "date_prochain_paiement")
  private LocalDate dateProchainePaiement;

  // ── Champs workflow de validation (onboarding) ────────────────────────────

  /** Plage de membres choisie lors de la souscription. */
  @Enumerated(EnumType.STRING)
  @Column(name = "plage", length = 20)
  private PlageMembres plage;

  /** Type d'organisation déclaré, utilisé pour le coefficient tarifaire. */
  @Enumerated(EnumType.STRING)
  @Column(name = "type_organisation", length = 30)
  private TypeOrganisationFacturation typeOrganisationSouscription;

  /** Coefficient multiplicateur effectivement appliqué (org × période). */
  @Column(name = "coefficient_applique", precision = 4, scale = 2)
  private BigDecimal coefficientApplique;

  /** État du workflow de validation SuperAdmin. */
  @Enumerated(EnumType.STRING)
  @Builder.Default
  @Column(name = "statut_validation", nullable = false, length = 40)
  private StatutValidationSouscription statutValidation = StatutValidationSouscription.EN_ATTENTE_PAIEMENT;

  /** Montant total facturé pour la période choisie (en XOF). */
  @Column(name = "montant_total", precision = 12, scale = 2)
  private BigDecimal montantTotal;

  /** Date à laquelle le SuperAdmin a approuvé ou rejeté la souscription. */
  @Column(name = "date_validation")
  private LocalDate dateValidation;

  /** UUID du SuperAdmin ayant validé ou rejeté. */
  @Column(name = "validated_by_id")
  private UUID validatedById;

  /** Motif de rejet renseigné par le SuperAdmin. */
  @Column(name = "commentaire_rejet", length = 500)
  private String commentaireRejet;

  /** Mot de passe temporaire généré à l'activation du compte. */
  @Column(name = "mot_de_passe_temporaire", length = 100)
  private String motDePasseTemporaire;

  // ── Méthodes métier ────────────────────────────────────────────────────────

  public boolean isActive() {
    return StatutSouscription.ACTIVE.equals(statut)
        && LocalDate.now().isBefore(dateFin.plusDays(1));
  }

  public boolean isQuotaDepasse() {
    return quotaMax != null && quotaUtilise >= quotaMax;
  }

  public int getPlacesRestantes() {
    if (quotaMax == null) return Integer.MAX_VALUE;
    return Math.max(0, quotaMax - quotaUtilise);
  }

  /** Incrémente le quota lors de la validation d'une adhésion */
  public void incrementerQuota() {
    if (quotaUtilise == null) quotaUtilise = 0;
    quotaUtilise++;
  }

  /** Décrémente le quota lors de la radiation d'un membre */
  public void decrementerQuota() {
    if (quotaUtilise != null && quotaUtilise > 0) quotaUtilise--;
  }

  @PrePersist
  protected void onCreate() {
    super.onCreate();
    if (statut == null)            statut            = StatutSouscription.ACTIVE;
    if (typePeriode == null)       typePeriode       = TypePeriodeAbonnement.MENSUEL;
    if (quotaUtilise == null)      quotaUtilise      = 0;
    if (statutValidation == null)  statutValidation  = StatutValidationSouscription.EN_ATTENTE_PAIEMENT;
    if (formule != null && quotaMax == null) quotaMax = formule.getMaxMembres();
  }
}
