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 java.util.concurrent.atomic.AtomicLong;
import lombok.*;

/**
 * Versement — acte de régler une cotisation ou de déposer des fonds.
 *
 * <p>Un versement peut être effectué :
 * <ul>
 *   <li>Via <b>Wave Mobile Money</b> : deep link natif, app Wave sur le même téléphone</li>
 *   <li>Manuellement : espèces, virement, chèque → statut EN_ATTENTE_VALIDATION</li>
 * </ul>
 *
 * <p>Table DB : {@code paiements} (nom hérité, conservé pour compatibilité Flyway).
 *
 * @author UnionFlow Team
 * @version 4.0
 * @since 2026-04-13
 */
@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 Versement extends BaseEntity {

    private static final AtomicLong REFERENCE_COUNTER =
        new AtomicLong(System.currentTimeMillis() % 1_000_000_000_000L);

    // ── Identité ──────────────────────────────────────────────────────────────

    /** Référence unique (ex: VRS-2026-XXXXXXXXXXXX) */
    @NotBlank
    @Column(name = "numero_reference", unique = true, nullable = false, length = 50)
    private String numeroReference;

    // ── Montant ───────────────────────────────────────────────────────────────

    @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;

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

    // ── Méthode & Statut ──────────────────────────────────────────────────────

    /** WAVE | ESPECES | VIREMENT | CHEQUE | AUTRE */
    @NotNull
    @Column(name = "methode_paiement", nullable = false, length = 50)
    private String methodePaiement;

    /** EN_ATTENTE | EN_COURS | CONFIRME | ECHEC | EN_ATTENTE_VALIDATION | ANNULE */
    @NotNull
    @Builder.Default
    @Column(name = "statut_paiement", nullable = false, length = 30)
    private String statutPaiement = "EN_ATTENTE";

    // ── Dates ─────────────────────────────────────────────────────────────────

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

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

    // ── Validation ────────────────────────────────────────────────────────────

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

    // ── Traçabilité ───────────────────────────────────────────────────────────

    /** ID transaction Wave (TCN...) ou référence chèque / bordereau */
    @Column(name = "reference_externe", length = 500)
    private String referenceExterne;

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

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

    @Column(name = "ip_address", length = 45)
    private String ipAddress;

    @Column(name = "user_agent", length = 500)
    private String userAgent;

    // ── Téléphone Wave ────────────────────────────────────────────────────────

    /**
     * Numéro de téléphone Wave utilisé pour ce versement.
     * Pré-rempli depuis le profil du membre (même téléphone qu'UnionFlow),
     * modifiable à l'étape "Récapitulatif" avant de tapper "Payer".
     */
    @Column(name = "numero_telephone", length = 20)
    private String numeroTelephone;

    // ── Relations ─────────────────────────────────────────────────────────────

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

    @JsonIgnore
    @OneToMany(mappedBy = "versement", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @Builder.Default
    private List<VersementObjet> versementsObjets = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "transaction_wave_id")
    private TransactionWave transactionWave;

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

    /** Génère une référence unique : VRS-YYYY-XXXXXXXXXXXX */
    public static String genererNumeroReference() {
        return "VRS-"
            + LocalDateTime.now().getYear()
            + "-"
            + String.format("%012d", REFERENCE_COUNTER.incrementAndGet() % 1_000_000_000_000L);
    }

    /** Vrai si le versement est confirmé (Wave) ou validé (manuel) */
    public boolean isConfirme() {
        return "CONFIRME".equals(statutPaiement) || "VALIDE".equals(statutPaiement);
    }

    /** Vrai si le versement peut encore être modifié ou annulé */
    public boolean peutEtreModifie() {
        return !"CONFIRME".equals(statutPaiement)
            && !"VALIDE".equals(statutPaiement)
            && !"ANNULE".equals(statutPaiement);
    }

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