package dev.lions.unionflow.server.entity;

import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * Entité Adhesion avec UUID
 * Représente une demande d'adhésion d'un membre à une organisation
 *
 * @author UnionFlow Team
 * @version 1.0
 * @since 2025-01-17
 */
@Entity
@Table(
    name = "adhesions",
    indexes = {
      @Index(name = "idx_adhesion_membre", columnList = "membre_id"),
      @Index(name = "idx_adhesion_organisation", columnList = "organisation_id"),
      @Index(name = "idx_adhesion_reference", columnList = "numero_reference", unique = true),
      @Index(name = "idx_adhesion_statut", columnList = "statut"),
      @Index(name = "idx_adhesion_date_demande", columnList = "date_demande")
    })
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class Adhesion extends BaseEntity {

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

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

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

  @NotNull
  @Column(name = "date_demande", nullable = false)
  private LocalDate dateDemande;

  @NotNull
  @DecimalMin(value = "0.0", message = "Le montant des frais d'adhésion doit être positif")
  @Digits(integer = 10, fraction = 2)
  @Column(name = "frais_adhesion", nullable = false, precision = 12, scale = 2)
  private BigDecimal fraisAdhesion;

  @Builder.Default
  @DecimalMin(value = "0.0", message = "Le montant payé doit être positif")
  @Digits(integer = 10, fraction = 2)
  @Column(name = "montant_paye", nullable = false, precision = 12, scale = 2)
  private BigDecimal montantPaye = BigDecimal.ZERO;

  @NotBlank
  @Pattern(regexp = "^[A-Z]{3}$", message = "Le code devise doit être un code ISO à 3 lettres")
  @Column(name = "code_devise", nullable = false, length = 3)
  private String codeDevise;

  @NotBlank
  @Pattern(
      regexp = "^(EN_ATTENTE|APPROUVEE|REJETEE|ANNULEE|EN_PAIEMENT|PAYEE)$",
      message = "Statut invalide")
  @Column(name = "statut", nullable = false, length = 30)
  private String statut;

  @Column(name = "date_approbation")
  private LocalDate dateApprobation;

  @Column(name = "date_paiement")
  private LocalDateTime datePaiement;

  @Size(max = 20)
  @Column(name = "methode_paiement", length = 20)
  private String methodePaiement;

  @Size(max = 100)
  @Column(name = "reference_paiement", length = 100)
  private String referencePaiement;

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

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

  @Column(name = "approuve_par", length = 255)
  private String approuvePar;

  @Column(name = "date_validation")
  private LocalDate dateValidation;

  /** Méthode métier pour vérifier si l'adhésion est payée intégralement */
  public boolean isPayeeIntegralement() {
    return montantPaye != null
        && fraisAdhesion != null
        && montantPaye.compareTo(fraisAdhesion) >= 0;
  }

  /** Méthode métier pour vérifier si l'adhésion est en attente de paiement */
  public boolean isEnAttentePaiement() {
    return "APPROUVEE".equals(statut) && !isPayeeIntegralement();
  }

  /** Méthode métier pour calculer le montant restant à payer */
  public BigDecimal getMontantRestant() {
    if (fraisAdhesion == null) return BigDecimal.ZERO;
    if (montantPaye == null) return fraisAdhesion;
    BigDecimal restant = fraisAdhesion.subtract(montantPaye);
    return restant.compareTo(BigDecimal.ZERO) > 0 ? restant : BigDecimal.ZERO;
  }
}



