package com.tiedup.remake.client.gltf;

import com.mojang.blaze3d.platform.InputConstants;
import com.tiedup.remake.client.animation.context.ContextAnimationFactory;
import com.tiedup.remake.client.animation.context.ContextGlbRegistry;
import com.tiedup.remake.v2.bondage.client.V2BondageRenderLayer;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenItemReloadListener;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.SimplePreparableReloadListener;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.EntityRenderersEvent;
import net.minecraftforge.client.event.RegisterClientReloadListenersEvent;
import net.minecraftforge.client.event.RegisterKeyMappingsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/**
 * Forge event registration for the glTF pipeline.
 * Registers keybind (F9), render layers, and animation factory.
 */
public final class GltfClientSetup {

    private static final Logger LOGGER = LogManager.getLogger("GltfPipeline");

    private static final String KEY_CATEGORY = "key.categories.tiedup";
    static final KeyMapping TOGGLE_KEY = new KeyMapping(
        "key.tiedup.gltf_toggle",
        InputConstants.Type.KEYSYM,
        InputConstants.KEY_F9,
        KEY_CATEGORY
    );

    private GltfClientSetup() {}

    /**
     * MOD bus event subscribers (FMLClientSetupEvent, RegisterKeyMappings, AddLayers).
     */
    @Mod.EventBusSubscriber(
        modid = "tiedup",
        bus = Mod.EventBusSubscriber.Bus.MOD,
        value = Dist.CLIENT
    )
    public static class ModBusEvents {

        @SubscribeEvent
        public static void onClientSetup(FMLClientSetupEvent event) {
            event.enqueueWork(() -> {
                GltfCache.init();
                GltfAnimationApplier.init();
                LOGGER.info("[GltfPipeline] Client setup complete");
            });
        }

        @SubscribeEvent
        public static void onRegisterKeybindings(
            RegisterKeyMappingsEvent event
        ) {
            event.register(TOGGLE_KEY);
            LOGGER.info("[GltfPipeline] Keybind registered: F9");
        }

        @SuppressWarnings("unchecked")
        @SubscribeEvent
        public static void onAddLayers(EntityRenderersEvent.AddLayers event) {
            // Add GltfRenderLayer (prototype/debug with F9 toggle) to player renderers
            var defaultRenderer = event.getSkin("default");
            if (defaultRenderer instanceof PlayerRenderer playerRenderer) {
                playerRenderer.addLayer(new GltfRenderLayer(playerRenderer));
                playerRenderer.addLayer(
                    new V2BondageRenderLayer<>(playerRenderer)
                );
                LOGGER.info(
                    "[GltfPipeline] Render layers added to 'default' player renderer"
                );
            }

            // Add both layers to slim player renderer (Alex)
            var slimRenderer = event.getSkin("slim");
            if (slimRenderer instanceof PlayerRenderer playerRenderer) {
                playerRenderer.addLayer(new GltfRenderLayer(playerRenderer));
                playerRenderer.addLayer(
                    new V2BondageRenderLayer<>(playerRenderer)
                );
                LOGGER.info(
                    "[GltfPipeline] Render layers added to 'slim' player renderer"
                );
            }
        }

        /**
         * Register resource reload listener to clear GLB caches on resource pack reload.
         * This ensures re-exported GLB models are picked up without restarting the game.
         */
        @SubscribeEvent
        public static void onRegisterReloadListeners(
            RegisterClientReloadListenersEvent event
        ) {
            event.registerReloadListener(
                new SimplePreparableReloadListener<Void>() {
                    @Override
                    protected Void prepare(
                        ResourceManager resourceManager,
                        ProfilerFiller profiler
                    ) {
                        return null;
                    }

                    @Override
                    protected void apply(
                        Void nothing,
                        ResourceManager resourceManager,
                        ProfilerFiller profiler
                    ) {
                        GltfCache.clearCache();
                        GltfAnimationApplier.invalidateCache();
                        GltfMeshRenderer.clearRenderTypeCache();
                        // Reload context GLB animations from resource packs FIRST,
                        // then clear the factory cache so it rebuilds against the
                        // new GLB registry (prevents stale JSON fallback caching).
                        ContextGlbRegistry.reload(resourceManager);
                        ContextAnimationFactory.clearCache();
                        com.tiedup.remake.v2.furniture.client.FurnitureGltfCache.clear();
                        LOGGER.info(
                            "[GltfPipeline] Caches cleared on resource reload"
                        );
                    }
                }
            );
            LOGGER.info("[GltfPipeline] Resource reload listener registered");

            // Data-driven bondage item definitions (tiedup_items/*.json)
            event.registerReloadListener(new DataDrivenItemReloadListener());
            LOGGER.info(
                "[GltfPipeline] Data-driven item reload listener registered"
            );
        }
    }

    /**
     * FORGE bus event subscribers (ClientTickEvent for keybind toggle).
     */
    @Mod.EventBusSubscriber(
        modid = "tiedup",
        bus = Mod.EventBusSubscriber.Bus.FORGE,
        value = Dist.CLIENT
    )
    public static class ForgeBusEvents {

        @SubscribeEvent
        public static void onClientTick(TickEvent.ClientTickEvent event) {
            if (event.phase != TickEvent.Phase.END) return;

            while (TOGGLE_KEY.consumeClick()) {
                GltfAnimationApplier.toggle();
            }
        }
    }
}
