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.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
 * Donnée de référence paramétrable via le client.
 *
 * <p>
 * Remplace toutes les enums Java et valeurs hardcodées
 * par une table unique CRUD-able depuis l'interface
 * d'administration. Chaque ligne appartient à un
 * {@code domaine} (ex: STATUT_ORGANISATION, DEVISE)
 * et porte un {@code code} unique dans ce domaine.
 *
 * <p>
 * Le champ {@code organisation} permet une
 * personnalisation par organisation. Lorsqu'il est
 * {@code null}, la valeur est globale à la plateforme.
 *
 * <p>
 * Table : {@code types_reference}
 *
 * @author UnionFlow Team
 * @version 3.0
 * @since 2026-02-21
 */
@Entity
@Table(name = "types_reference", indexes = {
        @Index(name = "idx_typeref_domaine", columnList = "domaine"),
        @Index(name = "idx_typeref_domaine_actif", columnList = "domaine, actif, ordre_affichage"),
        @Index(name = "idx_typeref_org", columnList = "organisation_id")
}, uniqueConstraints = {
        @UniqueConstraint(name = "uk_typeref_domaine_code_org", columnNames = {
                "domaine", "code", "organisation_id"
        })
})
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class TypeReference extends BaseEntity {

    /**
     * Domaine fonctionnel de cette valeur de référence.
     *
     * <p>
     * Exemples : {@code STATUT_ORGANISATION},
     * {@code TYPE_ORGANISATION}, {@code DEVISE}.
     */
    @NotBlank
    @Size(max = 50)
    @Column(name = "domaine", nullable = false, length = 50)
    private String domaine;

    /**
     * Code technique unique au sein du domaine.
     *
     * <p>
     * Exemples : {@code ACTIVE}, {@code XOF},
     * {@code ASSOCIATION}.
     */
    @NotBlank
    @Size(max = 50)
    @Column(name = "code", nullable = false, length = 50)
    private String code;

    /**
     * Libellé affiché dans l'interface utilisateur.
     *
     * <p>
     * Exemple : {@code "Franc CFA (UEMOA)"}.
     */
    @NotBlank
    @Size(max = 200)
    @Column(name = "libelle", nullable = false, length = 200)
    private String libelle;

    /** Description longue optionnelle. */
    @Size(max = 1000)
    @Column(name = "description", length = 1000)
    private String description;

    /**
     * Classe d'icône pour le rendu UI.
     *
     * <p>
     * Exemple : {@code "pi-check-circle"}.
     */
    @Size(max = 100)
    @Column(name = "icone", length = 100)
    private String icone;

    /**
     * Code couleur hexadécimal pour le rendu UI.
     *
     * <p>
     * Exemple : {@code "#22C55E"}.
     */
    @Size(max = 50)
    @Column(name = "couleur", length = 50)
    private String couleur;

    /**
     * Niveau de sévérité pour les badges PrimeFaces.
     *
     * <p>
     * Valeurs typiques : {@code success},
     * {@code warning}, {@code danger}, {@code info}.
     */
    @Size(max = 20)
    @Column(name = "severity", length = 20)
    private String severity;

    /**
     * Ordre d'affichage dans les listes déroulantes.
     *
     * <p>
     * Les valeurs avec un ordre inférieur
     * apparaissent en premier.
     */
    @Builder.Default
    @Column(name = "ordre_affichage", nullable = false)
    private Integer ordreAffichage = 0;

    /**
     * Indique si cette valeur est la valeur par défaut
     * pour son domaine. Une seule valeur par défaut
     * est autorisée par domaine et organisation.
     */
    @Builder.Default
    @Column(name = "est_defaut", nullable = false)
    private Boolean estDefaut = false;

    /**
     * Indique si cette valeur est protégée par le
     * système. Les valeurs système ne peuvent être
     * ni supprimées ni désactivées par un
     * administrateur.
     */
    @Builder.Default
    @Column(name = "est_systeme", nullable = false)
    private Boolean estSysteme = false;

    /**
     * Catégorie fonctionnelle (ex: ASSOCIATIF, FINANCIER_SOLIDAIRE, RELIGIEUX…).
     * Utilisée pour les types d'organisation (domaine TYPE_ORGANISATION).
     */
    @Size(max = 50)
    @Column(name = "categorie", length = 50)
    private String categorie;

    /**
     * Liste CSV des modules activés pour ce type d'organisation.
     * Exemple : "MEMBRES,COTISATIONS,TONTINE,FINANCE"
     * Utilisée pour initialiser {@code Organisation.modulesActifs} à la création.
     */
    @Column(name = "modules_requis", columnDefinition = "TEXT")
    private String modulesRequis;

    /**
     * Organisation propriétaire de cette valeur.
     *
     * <p>
     * Lorsque {@code null}, la valeur est globale
     * à la plateforme et visible par toutes les
     * organisations.
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "organisation_id")
    private Organisation organisation;

    /**
     * Callback JPA exécuté avant la persistance.
     *
     * <p>
     * Normalise le code et le domaine en
     * majuscules pour garantir la cohérence.
     */
    @Override
    @PrePersist
    protected void onCreate() {
        super.onCreate();
        if (domaine != null) {
            domaine = domaine.toUpperCase();
        }
        if (code != null) {
            code = code.toUpperCase();
        }
    }
}
