package dev.lions.unionflow.server.entity;

import com.fasterxml.jackson.annotation.JsonIgnore;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * Entité Paiement centralisée pour tous les types de paiements.
 * Réutilisable pour cotisations, adhésions, événements, aides.
 *
 * @author UnionFlow Team
 * @version 3.0
 * @since 2025-01-29
 */
@Entity
@Table(name = "paiements", indexes = {
    @Index(name = "idx_paiement_numero_reference", columnList = "numero_reference", unique = true),
    @Index(name = "idx_paiement_membre", columnList = "membre_id"),
    @Index(name = "idx_paiement_statut", columnList = "statut_paiement"),
    @Index(name = "idx_paiement_methode", columnList = "methode_paiement"),
    @Index(name = "idx_paiement_date", columnList = "date_paiement")
})
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class Paiement extends BaseEntity {

  /** Numéro de référence unique */
  @NotBlank
  @Column(name = "numero_reference", unique = true, nullable = false, length = 50)
  private String numeroReference;

  /** Montant du paiement */
  @NotNull
  @DecimalMin(value = "0.0", message = "Le montant doit être positif")
  @Digits(integer = 12, fraction = 2)
  @Column(name = "montant", nullable = false, precision = 14, scale = 2)
  private BigDecimal montant;

  /** Code devise (ISO 3 lettres) */
  @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;

  /** Méthode de paiement */
  @NotNull
  @Column(name = "methode_paiement", nullable = false, length = 50)
  private String methodePaiement;

  /** Statut du paiement */
  @NotNull
  @Builder.Default
  @Column(name = "statut_paiement", nullable = false, length = 30)
  private String statutPaiement = "EN_ATTENTE";

  /** Date de paiement */
  @Column(name = "date_paiement")
  private LocalDateTime datePaiement;

  /** Date de validation */
  @Column(name = "date_validation")
  private LocalDateTime dateValidation;

  /** Validateur (email de l'administrateur) */
  @Column(name = "validateur", length = 255)
  private String validateur;

  /** Référence externe (numéro transaction, URL preuve, etc.) */
  @Column(name = "reference_externe", length = 500)
  private String referenceExterne;

  /** URL de preuve de paiement */
  @Column(name = "url_preuve", length = 1000)
  private String urlPreuve;

  /** Commentaires et notes */
  @Column(name = "commentaire", length = 1000)
  private String commentaire;

  /** Adresse IP de l'initiateur */
  @Column(name = "ip_address", length = 45)
  private String ipAddress;

  /** User-Agent de l'initiateur */
  @Column(name = "user_agent", length = 500)
  private String userAgent;

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

  /** Objets cibles de ce paiement (polymorphique) */
  @JsonIgnore
  @OneToMany(mappedBy = "paiement", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @Builder.Default
  private List<PaiementObjet> paiementsObjets = new ArrayList<>();

  /** Relation avec TransactionWave (optionnelle) */
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "transaction_wave_id")
  private TransactionWave transactionWave;

  /** Génère un numéro de référence unique */
  public static String genererNumeroReference() {
    return "PAY-"
        + LocalDateTime.now().getYear()
        + "-"
        + String.format("%012d", System.currentTimeMillis() % 1000000000000L);
  }

  /** Vérifie si le paiement est validé */
  public boolean isValide() {
    return "VALIDE".equals(statutPaiement);
  }

  /** Vérifie si le paiement peut être modifié */
  public boolean peutEtreModifie() {
    return !"VALIDE".equals(statutPaiement)
        && !"ANNULE".equals(statutPaiement);
  }

  @PrePersist
  protected void onCreate() {
    super.onCreate();
    if (numeroReference == null || numeroReference.isEmpty()) {
      numeroReference = genererNumeroReference();
    }
    if (statutPaiement == null) {
      statutPaiement = "EN_ATTENTE";
    }
    if (datePaiement == null) {
      datePaiement = LocalDateTime.now();
    }
  }
}
