package dev.lions.unionflow.server.entity;

import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.concurrent.atomic.AtomicLong;
import lombok.*;

/**
 * Demande d'adhésion d'un utilisateur à une organisation.
 *
 * <p>Flux :
 * <ol>
 *   <li>L'utilisateur crée son compte et choisit une organisation</li>
 *   <li>Une {@code DemandeAdhesion} est créée (statut EN_ATTENTE)</li>
 *   <li>Si frais d'adhésion : une {@link IntentionPaiement} est créée et liée</li>
 *   <li>Le manager valide → {@link MembreOrganisation} créé, quota souscription décrémenté</li>
 * </ol>
 *
 * <p>Remplace l'ancienne entité {@code Adhesion}.
 * Table : {@code demandes_adhesion}
 */
@Entity
@Table(
    name = "demandes_adhesion",
    indexes = {
      @Index(name = "idx_da_utilisateur",  columnList = "utilisateur_id"),
      @Index(name = "idx_da_organisation", columnList = "organisation_id"),
      @Index(name = "idx_da_statut",       columnList = "statut"),
      @Index(name = "idx_da_date",         columnList = "date_demande")
    })
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class DemandeAdhesion extends BaseEntity {

  @NotBlank
  @Column(name = "numero_reference", unique = true, nullable = false, length = 50)
  private String numeroReference;

  @NotNull
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "utilisateur_id", nullable = false)
  private Membre utilisateur;

  @NotNull
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "organisation_id", nullable = false)
  private Organisation organisation;

  @NotBlank
  @Pattern(regexp = "^(EN_ATTENTE|APPROUVEE|REJETEE|ANNULEE)$")
  @Builder.Default
  @Column(name = "statut", nullable = false, length = 20)
  private String statut = "EN_ATTENTE";

  @Builder.Default
  @DecimalMin("0.00")
  @Digits(integer = 10, fraction = 2)
  @Column(name = "frais_adhesion", nullable = false, precision = 12, scale = 2)
  private BigDecimal fraisAdhesion = BigDecimal.ZERO;

  @Builder.Default
  @DecimalMin("0.00")
  @Digits(integer = 10, fraction = 2)
  @Column(name = "montant_paye", nullable = false, precision = 12, scale = 2)
  private BigDecimal montantPaye = BigDecimal.ZERO;

  @Builder.Default
  @Pattern(regexp = "^[A-Z]{3}$")
  @Column(name = "code_devise", nullable = false, length = 3)
  private String codeDevise = "XOF";

  /** Intention de paiement Wave liée aux frais d'adhésion */
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "intention_paiement_id")
  private IntentionPaiement intentionPaiement;

  @Builder.Default
  @Column(name = "date_demande", nullable = false)
  private LocalDateTime dateDemande = LocalDateTime.now();

  @Column(name = "date_traitement")
  private LocalDateTime dateTraitement;

  /** Manager/Admin qui a approuvé ou rejeté */
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "traite_par_id")
  private Membre traitePar;

  @Column(name = "motif_rejet", length = 1000)
  private String motifRejet;

  @Column(name = "observations", length = 1000)
  private String observations;

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

  public boolean isEnAttente() { return "EN_ATTENTE".equals(statut); }
  public boolean isApprouvee() { return "APPROUVEE".equals(statut); }
  public boolean isRejetee()   { return "REJETEE".equals(statut); }

  public boolean isPayeeIntegralement() {
    return fraisAdhesion != null
        && montantPaye != null
        && montantPaye.compareTo(fraisAdhesion) >= 0;
  }

  private static final AtomicLong REFERENCE_COUNTER =
      new AtomicLong(System.currentTimeMillis() % 100000000L);

  public static String genererNumeroReference() {
    return "ADH-" + java.time.LocalDate.now().getYear()
        + "-" + String.format("%08d", REFERENCE_COUNTER.incrementAndGet() % 100000000L);
  }

  @PrePersist
  protected void onCreate() {
    super.onCreate();
    if (dateDemande == null) dateDemande = LocalDateTime.now();
    if (statut == null)      statut      = "EN_ATTENTE";
    if (codeDevise == null)  codeDevise  = "XOF";
    if (fraisAdhesion == null) fraisAdhesion = BigDecimal.ZERO;
    if (montantPaye == null)   montantPaye   = BigDecimal.ZERO;
    if (numeroReference == null || numeroReference.isEmpty()) {
      numeroReference = genererNumeroReference();
    }
  }
}
