package com.tiedup.remake.client.animation.tick;

import com.mojang.logging.LogUtils;
import com.tiedup.remake.client.animation.AnimationStateRegistry;
import com.tiedup.remake.client.animation.BondageAnimationManager;
import com.tiedup.remake.client.events.CellHighlightHandler;
import com.tiedup.remake.client.events.LeashProxyClientHandler;
import com.tiedup.remake.client.gltf.GltfAnimationApplier;
import com.tiedup.remake.client.state.ClothesClientCache;
import com.tiedup.remake.client.state.MovementStyleClientState;
import com.tiedup.remake.client.state.PetBedClientState;
import com.tiedup.remake.state.PlayerBindState;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.slf4j.Logger;

/**
 * Event handler for animation tick updates.
 *
 * <p><b>Phase 2.8 RIG cleanup</b> : le ticking <i>player</i> V2 (boucle
 * {@code mc.level.players()} + {@code updatePlayerAnimation}) est entièrement
 * désactivé. Les joueurs sont désormais pilotés par
 * {@link com.tiedup.remake.rig.tick.RigAnimationTickHandler} via le pipeline
 * RIG (capability {@code LivingEntityPatch} + {@code Animator} natif EF).
 * Les features V2 qui dépendaient du tick player sont trackées dans
 * {@code docs/plans/rig/V3_REWORK_BACKLOG.md} (V3-REW-01/02/03/07).</p>
 *
 * <p>Restent actifs ici :
 * <ul>
 *   <li>Nettoyage périodique de {@code ClothesClientCache} (cache remote
 *       players, hygiène mémoire indépendante du pipeline de rendu)</li>
 *   <li>Cleanup logout / world unload (caches V2 encore utilisés par les
 *       NPCs ticked par {@link NpcAnimationTickHandler})</li>
 * </ul>
 *
 * <p>Le ticking NPC est assuré par {@link NpcAnimationTickHandler}. Ce
 * handler ne tick plus les NPCs directement — il ne gère que les hooks
 * lifecycle globaux (logout + world unload).</p>
 *
 * <p>Registered on the FORGE event bus (not MOD bus).</p>
 */
@Mod.EventBusSubscriber(
    modid = "tiedup",
    value = Dist.CLIENT,
    bus = Mod.EventBusSubscriber.Bus.FORGE
)
@OnlyIn(Dist.CLIENT)
public class AnimationTickHandler {

    private static final Logger LOGGER = LogUtils.getLogger();

    /** Tick counter for periodic cleanup tasks */
    private static int cleanupTickCounter = 0;

    /**
     * Per-player retry counter for the cold-cache furniture animation loop.
     * A GLB without a {@code Player_*} armature (legacy V1-only furniture)
     * can never yield a seat animation, so
     * {@link BondageAnimationManager#hasFurnitureAnimation} stays false
     * forever and the retry would spam at 20 Hz until dismount. Cap at
     * {@link #MAX_FURNITURE_RETRIES}; reset on successful apply or on
     * dismount so the next mount starts fresh.
     */
    private static final Map<UUID, Integer> furnitureRetryCounters =
        new ConcurrentHashMap<>();
    private static final int MAX_FURNITURE_RETRIES = 60; // ~3 seconds at 20 Hz — covers slow-disk GLB load

    /**
     * Drain the retry counter for a specific entity leaving the level.
     * Called from {@code EntityCleanupHandler.onEntityLeaveLevel} so a
     * remote player getting unloaded (chunk unload, dimension change,
     * kicked) doesn't leak a counter until the next world unload.
     */
    public static void removeFurnitureRetry(UUID uuid) {
        furnitureRetryCounters.remove(uuid);
    }

    /**
     * Client tick event — called every tick on the client.
     *
     * <p>Phase 2.8 : la boucle {@code mc.level.players()} qui appelait
     * {@code updatePlayerAnimation}, {@code tickFurnitureSafety} et le
     * cold-cache retry furniture a été entièrement supprimée. Les joueurs
     * sont désormais ticked par {@link com.tiedup.remake.rig.tick.RigAnimationTickHandler}
     * via le pipeline RIG (capability {@code LivingEntityPatch} +
     * {@code Animator}). Les régressions visuelles (V2 bondage layer cassé,
     * furniture seat pose sur joueur cassée, pet bed pose cassée) sont
     * listées dans {@code docs/plans/rig/V3_REWORK_BACKLOG.md}.</p>
     *
     * <p>Seul le nettoyage périodique de {@link ClothesClientCache} reste
     * — c'est de l'hygiène mémoire sur un cache indexé UUID joueur,
     * indépendant du pipeline de rendu.</p>
     */
    @SubscribeEvent
    public static void onClientTick(TickEvent.ClientTickEvent event) {
        if (event.phase != TickEvent.Phase.END) {
            return;
        }

        Minecraft mc = Minecraft.getInstance();
        if (mc.level == null || mc.isPaused()) {
            return;
        }

        // Periodic cleanup of stale clothes cache entries (every 60 seconds = 1200 ticks).
        // Indépendant du rendu V2/RIG — c'est juste un cache UUID→ClothesData qui
        // doit libérer la mémoire des joueurs déconnectés depuis >5min.
        if (++cleanupTickCounter >= 1200) {
            cleanupTickCounter = 0;
            ClothesClientCache.cleanupStale();
        }

        // Le tick per-player V2 (updatePlayerAnimation, tickFurnitureSafety,
        // cold-cache furniture retry) est délégué à RigAnimationTickHandler
        // Phase 2.7+. Rien à faire ici.
    }

    /**
     * Player logout event - cleanup animation data.
     */
    @SubscribeEvent
    public static void onPlayerLogout(PlayerEvent.PlayerLoggedOutEvent event) {
        if (event.getEntity().level().isClientSide()) {
            UUID uuid = event.getEntity().getUUID();
            AnimationStateRegistry.getLastTiedState().remove(uuid);
            BondageAnimationManager.cleanup(uuid);
            GltfAnimationApplier.removeTracking(uuid);
            furnitureRetryCounters.remove(uuid);
        }
    }

    /**
     * World unload event - clear all animation and cache data.
     * FIX: Now also clears client-side caches to prevent memory leaks and stale data.
     */
    @SubscribeEvent
    public static void onWorldUnload(
        net.minecraftforge.event.level.LevelEvent.Unload event
    ) {
        if (event.getLevel().isClientSide()) {
            // Animation state (includes BondageAnimationManager, PendingAnimationManager,
            // DogPoseRenderHandler, MCAAnimationTickCache)
            // AnimationStateRegistry.clearAll() handles GltfAnimationApplier.clearAll() transitively
            AnimationStateRegistry.clearAll();
            furnitureRetryCounters.clear();

            // Non-animation client-side caches
            PetBedClientState.clearAll();
            MovementStyleClientState.clearAll();
            com.tiedup.remake.client.state.CollarRegistryClient.clear();
            CellHighlightHandler.clearCache();
            LeashProxyClientHandler.clearAll();
            com.tiedup.remake.client.state.ClientLaborState.clearTask();
            com.tiedup.remake.client.state.ClothesClientCache.clearAll();
            com.tiedup.remake.client.texture.DynamicTextureManager.getInstance().clearAll();

            // C1: Player bind state client instances (prevents stale Player references)
            PlayerBindState.clearClientInstances();

            // C2: Armor stand bondage data (entity IDs are not stable across worlds)
            com.tiedup.remake.entities.armorstand.ArmorStandBondageClientCache.clear();

            // C3: Furniture GLB model cache (resource-backed, also cleared on F3+T reload)
            com.tiedup.remake.v2.furniture.client.FurnitureGltfCache.clear();

            LOGGER.debug(
                "Cleared all animation and cache data due to world unload"
            );
        }
    }
}
