/*
 * Derived from Epic Fight (https://github.com/Epic-Fight/epicfight)
 * by the Epic Fight Team, licensed under GPLv3.
 * Modifications © 2026 TiedUp! Remake Contributors, distributed under GPLv3.
 */

package com.tiedup.remake.rig;

import com.mojang.logging.LogUtils;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.rig.anim.Animator;
import com.tiedup.remake.rig.anim.ServerAnimator;
import com.tiedup.remake.rig.patch.LivingEntityPatch;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.loading.FMLEnvironment;
import org.slf4j.Logger;

import java.util.function.Function;

/**
 * Remplace {@code yesman.epicfight.main.EpicFightMod} + {@code EpicFightSharedConstants}
 * du fork upstream. Expose les singletons nécessaires au runtime RIG :
 *
 * <ul>
 *   <li>{@link #MODID} — ID du mod TiedUp (tiedup)</li>
 *   <li>{@link #LOGGER} — logger commun RIG</li>
 *   <li>{@link #identifier(String)} — helper ResourceLocation</li>
 *   <li>{@link #ANIMATOR_PROVIDER} — factory client/server split pour instancier l'Animator</li>
 *   <li>{@link #isPhysicalClient()} — détection side runtime</li>
 * </ul>
 *
 * <p>Pattern lazy method-ref : {@code ClientAnimator::getAnimator} n'est chargé
 * que si {@link #isPhysicalClient()} est true. Sur serveur dedié, la classe
 * client n'est jamais référencée, donc jamais chargée → pas de
 * {@code NoClassDefFoundError}.</p>
 */
public final class TiedUpRigConstants {

    public static final String MODID = TiedUpMod.MOD_ID;
    public static final Logger LOGGER = LogUtils.getLogger();

    /** Détection dev env (Gradle runClient) — utilisé pour les logs debug EF. */
    public static final boolean IS_DEV_ENV = !FMLEnvironment.production;

    /** Durée d'un tick MC en secondes (20 TPS). */
    public static final float A_TICK = 1.0F / 20.0F;

    /** Durée de transition inter-animation par défaut (en secondes — 0.15s = 3 ticks). */
    public static final float GENERAL_ANIMATION_TRANSITION_TIME = 0.15F;

    /** Nombre max de joints supportés par une armature (limite matrice pool). */
    public static final int MAX_JOINTS = 128;

    /**
     * Factory lazy : crée un Animator approprié au side runtime courant.
     * Client → {@link com.tiedup.remake.rig.anim.client.ClientAnimator#getAnimator}
     * Server → {@link ServerAnimator#getAnimator} (forké verbatim EF)
     *
     * <p>Pattern lazy method-ref : {@code ClientAnimator::getAnimator} n'est
     * chargé que si {@link #isPhysicalClient()} est true. Sur serveur dédié,
     * la classe client n'est jamais référencée, donc jamais chargée → pas de
     * {@code NoClassDefFoundError}.</p>
     */
    public static final Function<LivingEntityPatch<?>, Animator> ANIMATOR_PROVIDER =
        isPhysicalClient()
            ? com.tiedup.remake.rig.anim.client.ClientAnimator::getAnimator
            : ServerAnimator::getAnimator;

    private TiedUpRigConstants() {}

    public static ResourceLocation identifier(String path) {
        return ResourceLocation.fromNamespaceAndPath(MODID, path);
    }

    /** Alias d'{@link #identifier(String)} — équivalent TiedUpRigConstants.prefix upstream. */
    public static ResourceLocation prefix(String path) {
        return identifier(path);
    }

    public static boolean isPhysicalClient() {
        return FMLEnvironment.dist == Dist.CLIENT;
    }

    /**
     * En dev env : log un message + throw l'exception fournie.
     * En prod : log WARN seulement. Équivalent EF {@code TiedUpRigConstants.stacktraceIfDevSide}.
     */
    public static <E extends RuntimeException> void stacktraceIfDevSide(
        String message,
        java.util.function.Function<String, E> exceptionFactory
    ) {
        if (IS_DEV_ENV) {
            throw exceptionFactory.apply(message);
        } else {
            LOGGER.warn(message);
        }
    }

    /**
     * En dev env : log via le consumer + throw l'exception.
     * En prod : log seulement. Équivalent EF {@code EpicFightMod.logAndStacktraceIfDevSide}.
     */
    public static void logAndStacktraceIfDevSide(
        java.util.function.BiConsumer<Logger, String> logAction,
        String message,
        java.util.function.Function<String, ? extends Throwable> exceptionFactory
    ) {
        logAndStacktraceIfDevSide(logAction, message, exceptionFactory, message);
    }

    public static void logAndStacktraceIfDevSide(
        java.util.function.BiConsumer<Logger, String> logAction,
        String message,
        java.util.function.Function<String, ? extends Throwable> exceptionFactory,
        String stackTraceMessage
    ) {
        logAction.accept(LOGGER, message);
        if (IS_DEV_ENV) {
            Throwable t = exceptionFactory.apply(stackTraceMessage);
            if (t instanceof RuntimeException re) throw re;
            if (t instanceof Error err) throw err;
            throw new RuntimeException(t);
        }
    }
}
