package dev.lions.unionflow.server.entity;

import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
import dev.lions.unionflow.server.api.enums.wave.TypeTransactionWave;
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é TransactionWave pour le suivi des transactions Wave Mobile Money
 *
 * @author UnionFlow Team
 * @version 3.0
 * @since 2025-01-29
 */
@Entity
@Table(
    name = "transactions_wave",
    indexes = {
      @Index(name = "idx_transaction_wave_id", columnList = "wave_transaction_id", unique = true),
      @Index(name = "idx_transaction_wave_request_id", columnList = "wave_request_id"),
      @Index(name = "idx_transaction_wave_reference", columnList = "wave_reference"),
      @Index(name = "idx_transaction_wave_statut", columnList = "statut_transaction"),
      @Index(name = "idx_transaction_wave_compte", columnList = "compte_wave_id")
    })
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class TransactionWave extends BaseEntity {

  /** Identifiant Wave de la transaction (unique) */
  @NotBlank
  @Column(name = "wave_transaction_id", unique = true, nullable = false, length = 100)
  private String waveTransactionId;

  /** Identifiant de requête Wave */
  @Column(name = "wave_request_id", length = 100)
  private String waveRequestId;

  /** Référence Wave */
  @Column(name = "wave_reference", length = 100)
  private String waveReference;

  /** Type de transaction */
  @NotNull
  @Enumerated(EnumType.STRING)
  @Column(name = "type_transaction", nullable = false, length = 50)
  private TypeTransactionWave typeTransaction;

  /** Statut de la transaction */
  @NotNull
  @Enumerated(EnumType.STRING)
  @Builder.Default
  @Column(name = "statut_transaction", nullable = false, length = 30)
  private StatutTransactionWave statutTransaction = StatutTransactionWave.INITIALISE;

  /** Montant de la transaction */
  @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;

  /** Frais de transaction */
  @DecimalMin(value = "0.0")
  @Digits(integer = 10, fraction = 2)
  @Column(name = "frais", precision = 12, scale = 2)
  private BigDecimal frais;

  /** Montant net (montant - frais) */
  @DecimalMin(value = "0.0")
  @Digits(integer = 12, fraction = 2)
  @Column(name = "montant_net", precision = 14, scale = 2)
  private BigDecimal montantNet;

  /** Code devise */
  @NotBlank
  @Pattern(regexp = "^[A-Z]{3}$")
  @Column(name = "code_devise", nullable = false, length = 3)
  private String codeDevise;

  /** Numéro téléphone payeur */
  @Column(name = "telephone_payeur", length = 13)
  private String telephonePayeur;

  /** Numéro téléphone bénéficiaire */
  @Column(name = "telephone_beneficiaire", length = 13)
  private String telephoneBeneficiaire;

  /** Métadonnées JSON (réponse complète de Wave API) */
  @Column(name = "metadonnees", columnDefinition = "TEXT")
  private String metadonnees;

  /** Réponse complète de Wave API (JSON) */
  @Column(name = "reponse_wave_api", columnDefinition = "TEXT")
  private String reponseWaveApi;

  /** Nombre de tentatives */
  @Builder.Default
  @Column(name = "nombre_tentatives", nullable = false)
  private Integer nombreTentatives = 0;

  /** Date de dernière tentative */
  @Column(name = "date_derniere_tentative")
  private LocalDateTime dateDerniereTentative;

  /** Message d'erreur (si échec) */
  @Column(name = "message_erreur", length = 1000)
  private String messageErreur;

  // Relations
  @NotNull
  @ManyToOne(fetch = FetchType.LAZY)
  @JoinColumn(name = "compte_wave_id", nullable = false)
  private CompteWave compteWave;

  @JsonIgnore

  @OneToMany(mappedBy = "transactionWave", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @Builder.Default
  private List<WebhookWave> webhooks = new ArrayList<>();

  /** Méthode métier pour vérifier si la transaction est réussie */
  public boolean isReussie() {
    return StatutTransactionWave.REUSSIE.equals(statutTransaction);
  }

  /** Méthode métier pour vérifier si la transaction peut être retentée */
  public boolean peutEtreRetentee() {
    return (statutTransaction == StatutTransactionWave.ECHOUE
            || statutTransaction == StatutTransactionWave.EXPIRED)
        && (nombreTentatives == null || nombreTentatives < 5);
  }

  /** Callback JPA avant la persistance */
  @PrePersist
  protected void onCreate() {
    super.onCreate();
    if (statutTransaction == null) {
      statutTransaction = StatutTransactionWave.INITIALISE;
    }
    if (codeDevise == null || codeDevise.isEmpty()) {
      codeDevise = "XOF";
    }
    if (nombreTentatives == null) {
      nombreTentatives = 0;
    }
    if (montantNet == null && montant != null && frais != null) {
      montantNet = montant.subtract(frais);
    }
  }
}

