/*
 *  Copyright 2025-2026 R3944Realms
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package top.r3944realms.eroticdungeongame.core.event;

import com.google.common.collect.ImmutableList;
import net.minecraft.client.RecipeBookCategories;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.SlotAccess;
import net.minecraft.world.entity.npc.VillagerTrades;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.*;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.RegisterRecipeBookCategoriesEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
import net.minecraftforge.data.event.GatherDataEvent;
import net.minecraftforge.event.AttachCapabilitiesEvent;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.EntityAttributeCreationEvent;
import net.minecraftforge.event.entity.living.LivingEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.event.server.ServerStoppingEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.LogicalSide;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLConstructModEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.eroticdungeongame.EroticDungeon;
import top.r3944realms.eroticdungeongame.client.gui.screens.SignArmorEditScreen;
import top.r3944realms.eroticdungeongame.content.EDGVillagerTrades;
import top.r3944realms.eroticdungeongame.content.command.EDGCommand;
import top.r3944realms.eroticdungeongame.content.entity.IAnchorableEntity;
import top.r3944realms.eroticdungeongame.content.entity.IronBallEntity;
import top.r3944realms.eroticdungeongame.content.entity.npc.INPCPlayer;
import top.r3944realms.eroticdungeongame.content.item.SignArmorItem;
import top.r3944realms.eroticdungeongame.content.recipe.DungeonCraftingBookCategory;
import top.r3944realms.eroticdungeongame.content.recipe.DungeonRecipe;
import top.r3944realms.eroticdungeongame.content.recipe.EDGRecipeBookTypes;
import top.r3944realms.eroticdungeongame.content.recipe.EDGRecipeTypeCategories;
import top.r3944realms.eroticdungeongame.core.capability.DungeonDataSyncManager;
import top.r3944realms.eroticdungeongame.core.capability.PlayerDungeonDataProvider;
import top.r3944realms.eroticdungeongame.core.compat.*;
import top.r3944realms.eroticdungeongame.core.network.EDGNetworkHandler;
import top.r3944realms.eroticdungeongame.core.network.toClient.NPCInfoUpdatePacket;
import top.r3944realms.eroticdungeongame.core.register.EDGCapabilities;
import top.r3944realms.eroticdungeongame.core.register.EDGEntities;
import top.r3944realms.eroticdungeongame.core.register.EDGRecipeTypes;
import top.r3944realms.eroticdungeongame.datagen.EDGDataGenEvent;
import top.r3944realms.eroticdungeongame.util.IEDGMinecraftServer;
import top.r3944realms.lib39.api.event.SyncManagerRegisterEvent;
import top.r3944realms.lib39.core.compat.ForgeCompatManager;
import top.r3944realms.lib39.core.event.CommonEventHandler;

import java.util.List;

public class CommonHandler {
    @net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = EroticDungeon.MOD_ID, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.FORGE)
    public static class Game extends CommonHandler {
        public static DungeonDataSyncManager dungeonDataSyncManager;
        public static ResourceLocation DUNGEON_SYNC = EroticDungeon.rl("dungeon_sync");
        @SubscribeEvent
        public static void attachCapability(AttachCapabilitiesEvent<?> event) {
            EDGCapabilities.attachCapability(event);
        }

        @SubscribeEvent
        public static void onServerStarted(@NotNull ServerStartedEvent event) {
            if (event.getServer() instanceof IEDGMinecraftServer iedgMinecraftServer) {
                iedgMinecraftServer.getNPCPlayerList().loadAllNPCs();
            }
        }
        @SubscribeEvent
        public static void onServerStopping(@NotNull ServerStoppingEvent event) {

        }

        @SubscribeEvent
        public static void syncCapabilities(@NotNull SyncManagerRegisterEvent event) {
            dungeonDataSyncManager = new DungeonDataSyncManager();
            event.registerSyncManager(DUNGEON_SYNC, dungeonDataSyncManager, PlayerDungeonDataProvider.PLAYER_DUNGEON_DATA_CAP);
            event.addAllowEntityClass(DUNGEON_SYNC, Player.class);
        }

        private static char ticks = 0;
        @SubscribeEvent
        public static void onPlayerTick(TickEvent.@NotNull PlayerTickEvent event) {
            Player player = event.player;
            ticks++;

            if (ticks % 2 == 0) {
                DungeonDataSyncManager.tick(player);
            }
        }

        @SubscribeEvent
        public static void onLivingTick(LivingEvent.@NotNull LivingTickEvent event) {
            LivingEntity livingEntity = event.getEntity();
            SlotAccess slotAccess = SlotAccess.forEquipmentSlot(livingEntity, EquipmentSlot.CHEST);
            if (!slotAccess.get().isEmpty() && slotAccess.get().getItem() instanceof SignArmorItem signArmorItem) {
                signArmorItem.tick(slotAccess.get().getCraftingRemainingItem(), livingEntity.level(), livingEntity);
            }
        }


        @SubscribeEvent
        public static void onPlayerLogin (PlayerEvent.@NotNull PlayerLoggedInEvent event) {
            Player player = event.getEntity();
            if (player == null || player.level().isClientSide()) return;
            DungeonDataSyncManager.login(player);
            if (player.getServer() instanceof IEDGMinecraftServer iedgMinecraftServer && player instanceof ServerPlayer serverPlayer && !(player instanceof INPCPlayer)) {
                EDGNetworkHandler.sendToPlayer(NPCInfoUpdatePacket.createNPCPlayerInitializing(iedgMinecraftServer.getNPCPlayerList().getPlayers()), serverPlayer);
            }

        }

        @SubscribeEvent
        public static void onPlayerLogout (PlayerEvent.@NotNull PlayerLoggedOutEvent event) {
            Player player = event.getEntity();
            if (player == null || player.level().isClientSide()) return;
            DungeonDataSyncManager.logout(player);
        }

        @SubscribeEvent
        public static void onCommandRegister(@NotNull RegisterCommandsEvent event) {
            EDGCommand command = new EDGCommand(event.getDispatcher(), event.getBuildContext());
        }

        @SubscribeEvent(priority = EventPriority.HIGHEST)
        public static void playerInteractEntity(PlayerInteractEvent.@NotNull EntityInteract event) {
            if (event.getSide() == LogicalSide.CLIENT)
                return;
            if (event.getHand() == InteractionHand.MAIN_HAND) {
                ServerPlayer player = (ServerPlayer) event.getEntity();
                Entity target = event.getTarget();
                ItemStack funcItemStack = player.getItemInHand(event.getHand());
                if (target instanceof Player) {
                    IAnchorableEntity anchorableEntity = (IAnchorableEntity) target;
                    if (anchorableEntity.edg$isAnchored()) {
                        if (funcItemStack.is(Items.AIR)){
                            anchorableEntity.edg$setAnchoredTo(null);

                            player.level().playSound(null, event.getPos(), SoundEvents.CHAIN_BREAK, SoundSource.PLAYERS,
                                    0.7f, 1);

                            event.setCancellationResult(InteractionResult.SUCCESS);
                            event.setCanceled(true);
                        }
                    } else if (funcItemStack.is(Items.CHAIN)) {
                        anchorableEntity.edg$setAnchoredTo(player);
                        player.getItemInHand(InteractionHand.MAIN_HAND).shrink(1);

                        player.level().playSound(null, event.getPos(), SoundEvents.CHAIN_PLACE, SoundSource.PLAYERS,
                                0.7f, 1);

                        event.setCancellationResult(InteractionResult.SUCCESS);
                        event.setCanceled(true);
                    }

                }
                if (target instanceof LivingEntity living) {
                    ItemStack bySlot = living.getItemBySlot(EquipmentSlot.CHEST);
                    Item item = funcItemStack.getItem();
                    if (SignArmorItem.handleSignArmor(item, bySlot, player.level(), target) && !player.isCreative()) {
                        funcItemStack.shrink(1);
                    }
                }
            }
        }
        @SubscribeEvent
        public static void playerRightUse(PlayerInteractEvent.@NotNull RightClickItem event) {
            Player player = event.getEntity();
            ItemStack main = player.getItemInHand(InteractionHand.MAIN_HAND);
            ItemStack off = player.getItemInHand(InteractionHand.OFF_HAND);
            boolean flagM = main.getItem() instanceof SignArmorItem;
            boolean flagO = off.getItem() instanceof SignArmorItem;
            if ((flagM || flagO) && !(flagM && flagO)) {
                if (flagM && SignArmorItem.handleSignArmor(off.getItem(), main, player.level(), player)) {
                    if (!player.isCreative())
                        off.shrink(1);
                    event.setCanceled(true);
                } else if(SignArmorItem.handleSignArmor(main.getItem(), off, player.level(), player) && !player.isCreative()) {
                    if (!player.isCreative())
                        main.shrink(1);
                    event.setCanceled(true);
                }
            }
        }

    }

    @net.minecraftforge.fml.common.Mod.EventBusSubscriber(modid = EroticDungeon.MOD_ID, bus = net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus.MOD)
    public static class Mod extends CommonHandler {
        public static final IEventBus EVENT_BUS = FMLJavaModLoadingContext.get().getModEventBus();
        /**
         * Gets compat manager.
         *
         * @return the compat manager
         */
        public static ForgeCompatManager getOrCreateCompatManager() {
            if (compatManager == null) {
                synchronized (CommonEventHandler.Mod.class) {
                    if (compatManager == null) {
                        compatManager = new EDGCompatManager("compat", EVENT_BUS, MinecraftForge.EVENT_BUS);
                    }
                }
            }
            return compatManager;
        }

        /**
         * The Compat manager.
         */
        static volatile ForgeCompatManager compatManager;


        /**
         * On construct mod.
         *
         * @param event the event
         */
        @SubscribeEvent
        public static void onConstructMod(@NotNull FMLConstructModEvent event) {
            event.enqueueWork(() -> {
                ForgeCompatManager orCreateCompatManager = Mod.getOrCreateCompatManager();
                orCreateCompatManager
                        .registerCompat(CarryOnCompat.ID, CarryOnCompat.INSTANCE);
                orCreateCompatManager
                        .registerCompat(SuperLeadRopeCompat.ID, SuperLeadRopeCompat.INSTANCE);
                orCreateCompatManager
                        .registerCompat(EmoteCraftCompat.ID, EmoteCraftCompat.INSTANCE);
                orCreateCompatManager
                        .registerCompat(TouhouLittleMaidCompat.ID, TouhouLittleMaidCompat.INSTANCE);
                DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
                       orCreateCompatManager
                               .registerCompat(FreecamCompat.ID, FreecamCompat.INSTANCE);
                       orCreateCompatManager
                               .registerCompat(RealCameraCompat.ID, RealCameraCompat.INSTANCE);
                       orCreateCompatManager
                               .registerCompat(FirstPersonModelAndRealCameraCompat.ID, FirstPersonModelAndRealCameraCompat.INSTANCE);
                    }
                );
                orCreateCompatManager.initialize();
            });
        }
        @SubscribeEvent
        public static void onSetUp (@NotNull FMLCommonSetupEvent event) {
            event.enqueueWork(() -> EDGVillagerTrades.init(VillagerTrades.TRADES));
        }
        @SubscribeEvent
        public static void registerAttributes(@NotNull EntityAttributeCreationEvent event) {
            event.put(EDGEntities.IRON_BALL.get(), IronBallEntity.createAttributes().build());
        }

        @SubscribeEvent
        public static void registerCapability(RegisterCapabilitiesEvent event) {
            EDGCapabilities.registerCapability(event);
        }
        @SubscribeEvent
        public static void gatherData(GatherDataEvent event) {
            EDGDataGenEvent.gatherData(event);
        }
        @SubscribeEvent
        public static void RegisterRecipeBookCategories (@NotNull RegisterRecipeBookCategoriesEvent event) {
            EDGRecipeTypeCategories.reload();
            event.registerBookCategories(EDGRecipeBookTypes.DUNGEON_CRAFTING, List.of(
                    EDGRecipeTypeCategories.EDG_CRAFTING_SEARCH ,EDGRecipeTypeCategories.WOODEN_DEVICE,
                    EDGRecipeTypeCategories.METAL_DEVICE, EDGRecipeTypeCategories.MISC_THING, EDGRecipeTypeCategories.EQUIPMENT
            ));
            event.registerAggregateCategory(EDGRecipeTypeCategories.EDG_CRAFTING_SEARCH,
                    ImmutableList.of(
                            EDGRecipeTypeCategories.WOODEN_DEVICE,
                            EDGRecipeTypeCategories.METAL_DEVICE,
                            EDGRecipeTypeCategories.EQUIPMENT,
                            EDGRecipeTypeCategories.MISC_THING
                    )
            );
            event.registerRecipeCategoryFinder(EDGRecipeTypes.DUNGEON_CRAFTING.get(), recipe -> {
                if (recipe instanceof DungeonRecipe dungeonRecipe){
                    DungeonCraftingBookCategory category = dungeonRecipe.category();
                    return switch (category) {
                        case WOODEN_DEVICE -> EDGRecipeTypeCategories.WOODEN_DEVICE;
                        case METAL_DEVICE -> EDGRecipeTypeCategories.METAL_DEVICE;
                        case OTHER_DEVICE -> EDGRecipeTypeCategories.OTHER_DEVICE;
                        case EQUIPMENT -> EDGRecipeTypeCategories.EDG_CRAFTING_SEARCH;
                        case MISC -> EDGRecipeTypeCategories.MISC_THING;
                    };
                }
                return RecipeBookCategories.UNKNOWN;
            });
        }

    }
}
