package dev.lions.unionflow.server.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.PrePersist;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.Digits;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * Table de liaison polymorphique entre un paiement
 * et son objet cible.
 *
 * <p>
 * Remplace les 4 tables dupliquées
 * {@code paiements_cotisations},
 * {@code paiements_adhesions},
 * {@code paiements_evenements} et
 * {@code paiements_aides} par une table unique
 * utilisant le pattern
 * {@code (type_objet_cible, objet_cible_id)}.
 *
 * <p>
 * Les types d'objet cible sont définis dans le
 * domaine {@code OBJET_PAIEMENT} de la table
 * {@code types_reference} (ex: COTISATION,
 * ADHESION, EVENEMENT, AIDE).
 *
 * @author UnionFlow Team
 * @version 3.0
 * @since 2026-02-21
 */
@Entity
@Table(name = "paiements_objets", indexes = {
        @Index(name = "idx_po_paiement", columnList = "paiement_id"),
        @Index(name = "idx_po_objet", columnList = "type_objet_cible,"
                + " objet_cible_id"),
        @Index(name = "idx_po_type", columnList = "type_objet_cible")
}, uniqueConstraints = {
        @UniqueConstraint(name = "uk_paiement_objet", columnNames = {
                "paiement_id",
                "type_objet_cible",
                "objet_cible_id"
        })
})
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class PaiementObjet extends BaseEntity {

    /** Paiement parent. */
    @NotNull
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "paiement_id", nullable = false)
    private Paiement paiement;

    /**
     * Type de l'objet cible (code du domaine
     * {@code OBJET_PAIEMENT} dans
     * {@code types_reference}).
     *
     * <p>
     * Valeurs attendues : {@code COTISATION},
     * {@code ADHESION}, {@code EVENEMENT},
     * {@code AIDE}.
     */
    @NotBlank
    @Size(max = 50)
    @Column(name = "type_objet_cible", nullable = false, length = 50)
    private String typeObjetCible;

    /**
     * UUID de l'objet cible (cotisation, demande
     * d'adhésion, inscription événement, ou demande
     * d'aide).
     */
    @NotNull
    @Column(name = "objet_cible_id", nullable = false)
    private UUID objetCibleId;

    /** Montant appliqué à cet objet cible. */
    @NotNull
    @DecimalMin(value = "0.0", message = "Le montant doit être positif")
    @Digits(integer = 12, fraction = 2)
    @Column(name = "montant_applique", nullable = false, precision = 14, scale = 2)
    private BigDecimal montantApplique;

    /** Date d'application du paiement. */
    @Column(name = "date_application")
    private LocalDateTime dateApplication;

    /** Commentaire sur l'application. */
    @Size(max = 500)
    @Column(name = "commentaire", length = 500)
    private String commentaire;

    /**
     * Callback JPA avant la persistance.
     *
     * <p>
     * Initialise {@code dateApplication} si non
     * renseignée.
     */
    @Override
    @PrePersist
    protected void onCreate() {
        super.onCreate();
        if (dateApplication == null) {
            dateApplication = LocalDateTime.now();
        }
    }
}
