package dev.lions.unionflow.server.service;

import dev.lions.unionflow.server.entity.Configuration;
import dev.lions.unionflow.server.repository.ConfigurationRepository;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.math.BigDecimal;
import java.util.Optional;
import org.jboss.logging.Logger;

/**
 * Service centralisé pour la lecture des valeurs
 * par défaut depuis la table {@code configurations}.
 *
 * <p>
 * Élimine tous les littéraux magiques
 * ({@code "XOF"}, {@code "ACTIVE"},
 * {@code "ASSOCIATION"}, etc.) dispersés dans le
 * code métier. Chaque service injecte ce service
 * au lieu de coder en dur ses valeurs par défaut.
 *
 * <p>
 * Clés standardisées :
 * <ul>
 * <li>{@code defaut.devise} → {@code XOF}
 * <li>{@code defaut.statut.organisation}
 * → {@code ACTIVE}
 * <li>{@code defaut.type.organisation}
 * → {@code ASSOCIATION}
 * <li>{@code defaut.utilisateur.systeme}
 * → {@code system}
 * </ul>
 *
 * @author UnionFlow Team
 * @version 3.0
 * @since 2026-02-21
 */
@ApplicationScoped
public class DefaultsService {

    private static final Logger LOG = Logger.getLogger(DefaultsService.class);

    @Inject
    ConfigurationRepository configRepo;

    // ── Clés standardisées ─────────────────────

    /** Clé : devise par défaut. */
    public static final String CLE_DEVISE = "defaut.devise";

    /** Clé : statut organisation par défaut. */
    public static final String CLE_STATUT_ORG = "defaut.statut.organisation";

    /** Clé : type organisation par défaut. */
    public static final String CLE_TYPE_ORG = "defaut.type.organisation";

    /** Clé : utilisateur système (audit). */
    public static final String CLE_USER_SYSTEME = "defaut.utilisateur.systeme";

    /** Clé : montant cotisation par défaut. */
    public static final String CLE_MONTANT_COTISATION = "defaut.montant.cotisation";

    // ── Fallbacks compilés ─────────────────────

    private static final String FALLBACK_DEVISE = "XOF";
    private static final String FALLBACK_STATUT_ORG = "ACTIVE";
    private static final String FALLBACK_TYPE_ORG = "ASSOCIATION";
    private static final String FALLBACK_USER = "system";

    // ── API publique ───────────────────────────

    /**
     * Retourne la devise par défaut.
     *
     * @return code devise (ex: {@code "XOF"})
     */
    public String getDevise() {
        return getString(CLE_DEVISE, FALLBACK_DEVISE);
    }

    /**
     * Retourne le statut par défaut d'une
     * organisation.
     *
     * @return code statut (ex: {@code "ACTIVE"})
     */
    public String getStatutOrganisation() {
        return getString(
                CLE_STATUT_ORG, FALLBACK_STATUT_ORG);
    }

    /**
     * Retourne le type par défaut d'une
     * organisation.
     *
     * @return code type (ex: {@code "ASSOCIATION"})
     */
    public String getTypeOrganisation() {
        return getString(
                CLE_TYPE_ORG, FALLBACK_TYPE_ORG);
    }

    /**
     * Retourne le nom de l'utilisateur système.
     *
     * @return identifiant système
     */
    public String getUtilisateurSysteme() {
        return getString(CLE_USER_SYSTEME, FALLBACK_USER);
    }

    /**
     * Retourne le montant de cotisation par défaut.
     *
     * @return montant ou {@code BigDecimal.ZERO}
     */
    public BigDecimal getMontantCotisation() {
        return getDecimal(
                CLE_MONTANT_COTISATION, BigDecimal.ZERO);
    }

    // ── Méthodes utilitaires ───────────────────

    /**
     * Lit une valeur String depuis la table
     * {@code configurations}.
     *
     * @param cle      clé de configuration
     * @param fallback valeur de repli
     * @return la valeur en base ou le fallback
     */
    public String getString(
            String cle, String fallback) {
        try {
            Optional<Configuration> config = configRepo.findByCle(cle);
            if (config.isPresent()
                    && config.get().getValeur() != null
                    && !config.get().getValeur().isBlank()) {
                return config.get().getValeur();
            }
        } catch (Exception e) {
            LOG.debugf(
                    "Configuration '%s' indisponible: %s",
                    cle, e.getMessage());
        }
        return fallback;
    }

    /**
     * Lit une valeur BigDecimal depuis la table
     * {@code configurations}.
     *
     * @param cle      clé de configuration
     * @param fallback valeur de repli
     * @return la valeur en base ou le fallback
     */
    public BigDecimal getDecimal(
            String cle, BigDecimal fallback) {
        String val = getString(cle, null);
        if (val != null) {
            try {
                return new BigDecimal(val);
            } catch (NumberFormatException e) {
                LOG.warnf(
                        "Valeur non numérique pour '%s': %s",
                        cle, val);
            }
        }
        return fallback;
    }

    /**
     * Lit une valeur Boolean depuis la table
     * {@code configurations}.
     *
     * @param cle      clé de configuration
     * @param fallback valeur de repli
     * @return la valeur en base ou le fallback
     */
    public boolean getBoolean(
            String cle, boolean fallback) {
        String val = getString(cle, null);
        if (val != null) {
            return Boolean.parseBoolean(val);
        }
        return fallback;
    }

    /**
     * Lit une valeur int depuis la table
     * {@code configurations}.
     *
     * @param cle      clé de configuration
     * @param fallback valeur de repli
     * @return la valeur en base ou le fallback
     */
    public int getInt(String cle, int fallback) {
        String val = getString(cle, null);
        if (val != null) {
            try {
                return Integer.parseInt(val);
            } catch (NumberFormatException e) {
                LOG.warnf(
                        "Valeur non entière pour '%s': %s",
                        cle, val);
            }
        }
        return fallback;
    }
}
