package top.r3944realms.lib39.core.event;

import com.mojang.authlib.GameProfile;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents;
import net.fabricmc.fabric.api.lookup.v1.entity.EntityApiLookup;
import net.minecraft.Util;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.Identifier;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.Services;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.SkullBlockEntity;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.Lib39;
import top.r3944realms.lib39.api.callback.ActionResult;
import top.r3944realms.lib39.api.callback.AnvilUpdateCallback;
import top.r3944realms.lib39.api.callback.MinecraftSetUpServiceCallback;
import top.r3944realms.lib39.api.callback.SyncManagerRegisterCallback;
import top.r3944realms.lib39.api.callback.client.ClientWorldCallback;
import top.r3944realms.lib39.base.command.Lib39HelpCommand;
import top.r3944realms.lib39.content.block.blockentity.DollBlockEntity;
import top.r3944realms.lib39.content.item.DollItem;
import top.r3944realms.lib39.core.register.Lib39Items;
import top.r3944realms.lib39.core.sync.ISyncData;
import top.r3944realms.lib39.core.sync.SyncData2LookupManager;
import top.r3944realms.lib39.example.content.data.AbstractedTestSyncData;
import top.r3944realms.lib39.example.content.data.FabricTestSyncData;
import top.r3944realms.lib39.example.content.data.FabricTestSyncLookupProvider;
import top.r3944realms.lib39.util.GameProfileHelper;
import top.r3944realms.lib39.util.IClientOnly;
import top.r3944realms.lib39.util.ILevelHelper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.function.Supplier;

/**
 * The type Fabric common event handler.
 */
public class FabricCommonEventHandler {
    /**
     * The Sync data 2 manager.
     */
    static volatile SyncData2LookupManager syncData2Manager;
    private static boolean isSync2MInitialized = false;
    private static ServerLevel sl;

    /**
     * Gets server level.
     *
     * @return the server level
     */
    public static ServerLevel getServerLevel() {
        return sl;
    }

    /**
     * Gets sync data 2 manager.
     *
     * @return the sync data 2 manager
     */
    public static SyncData2LookupManager getSyncData2Manager() {
        return syncData2Manager;
    }

    /**
     * On server world load.
     *
     * @param server      the server
     * @param serverLevel the server level
     */
    public static void onServerWorldLoad(MinecraftServer server, ServerLevel serverLevel) {
        if (!serverLevel.dimension().equals(Level.OVERWORLD)) return;
        synchronized (FabricCommonEventHandler.class) {
            if (!isSync2MInitialized) {
                syncData2Manager = new SyncData2LookupManager();
                isSync2MInitialized = true;
                sl = serverLevel;
                ILevelHelper.LevelHelper.SERVER.setLevel(serverLevel);
                SyncManagerRegisterCallback.EVENT.invoker().registerSyncManagers(new SyncManagerRegisterCallback.SyncManagerRegistrar((syncData2Manager)));
                Lib39.LOGGER.info("SyncData2Manager initialized on Sever load");
            }
        }
    }


    /**
     * On server world un load.
     *
     * @param server      the server
     * @param serverLevel the server level
     */
    public static void onServerWorldUnLoad(MinecraftServer server, @NotNull ServerLevel serverLevel) {
        if (!serverLevel.dimension().equals(Level.OVERWORLD)) return;
        sl = null;
        ILevelHelper.LevelHelper.SERVER.setLevel(null);
        isSync2MInitialized = false;
    }

    /**
     * On server tick.
     *
     * @param server the server
     */
    public static void onServerTick(MinecraftServer server) {
        if (syncData2Manager == null) return;
        if (server.getTickCount() % 10 == 0)
            syncData2Manager.forEach(((Identifier, iSyncManager) -> iSyncManager.foreach(ISyncData::markDirty)));
        syncData2Manager.forEach(((Identifier, iSyncManager) -> iSyncManager.foreach(ISyncData::checkIfDirtyThenUpdate)));
    }

    /**
     * On entity join world.
     *
     * @param entity the entity
     * @param level  the level
     */
    public static void onEntityJoinWorld(@NotNull Entity entity, ServerLevel level) {
        if (entity.level().isClientSide) return;

        for (Identifier id : syncData2Manager.getRegisteredKeys()) {
            if (syncData2Manager.isEntityClassAllowed(id, entity.getClass())) {
                syncData2Manager.trackEntityForManager(entity, id);
            }
        }
    }

    /**
     * On entity leave world.
     *
     * @param entity the entity
     * @param level  the level
     */
    public static void onEntityLeaveWorld(@NotNull Entity entity, ServerLevel level) {
        if (entity.level().isClientSide) return;

        for (Identifier id : syncData2Manager.getRegisteredKeys()) {
            if (syncData2Manager.isEntityClassAllowed(id, entity.getClass())) {
                syncData2Manager.untrackEntityForManager(entity, id);
            }
        }
    }
    private static final Map<Supplier<Block>, ResourceKey<CreativeModeTab>[]> itemAddMap = new ConcurrentHashMap<>();
    private static final Map<ResourceKey<CreativeModeTab>, List<Supplier<Block>>> tabToItemsMap = new ConcurrentHashMap<>();

    /**
     * Add item to tabs.
     *
     * @param item the item
     * @param tabs the tabs
     */
    @SafeVarargs
    public static void addItemToTabs(Supplier<Block> item, ResourceKey<CreativeModeTab>... tabs) {
        itemAddMap.put(item, tabs);

        // 更新反向映射
        for (ResourceKey<CreativeModeTab> tab : tabs) {
            tabToItemsMap.computeIfAbsent(tab, k -> new ArrayList<>()).add(item);
        }
    }

    /**
     * Init common.
     */
    public static void initCommon() {
        try {
            Class.forName("top.r3944realms.lib39.base.command.Lib39CommandHelpManager");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        for (Map.Entry<ResourceKey<CreativeModeTab>, List<Supplier<Block>>> resourceKeyListEntry : tabToItemsMap.entrySet()) {
            ItemGroupEvents.modifyEntriesEvent(resourceKeyListEntry.getKey()).register(content -> resourceKeyListEntry.getValue().forEach(i -> content.accept(i.get())));
        }
        MinecraftSetUpServiceCallback.EVENT.register((services, mainThreadExecutor) -> {
            DollBlockEntity.setup(services, mainThreadExecutor);
            return ActionResult.SUCCESS;
        });
        AnvilUpdateCallback.EVENT.register((left, right, outputSlot, name, baseCost, player) -> {
            if (left.getItem() instanceof DollItem && name != null && !name.isEmpty() && name.length() <= 16) {
                ItemStack output = Lib39Items.DOLL.get().getDefaultInstance();
                output.setCount(left.getCount());

                // 创建 ResolvableProfile
                GameProfile profile = new GameProfile(Util.NIL_UUID, name);
                ResolvableProfile resolvableProfile = new ResolvableProfile(profile);
                output.set(DataComponents.PROFILE, resolvableProfile);

                // 使用 DollBlockEntity 的缓存异步获取完整档案
                DollBlockEntity.fetchGameProfile(name).thenAcceptAsync(optional -> {
                    optional.ifPresent(fullProfile -> {
                        output.set(DataComponents.PROFILE, new ResolvableProfile(fullProfile));
                    });
                }, SkullBlockEntity.CHECKED_MAIN_THREAD_EXECUTOR);

                return AnvilUpdateCallback.AnvilUpdateResult.withOutput(output, 1, 1);
            } else if (name == null || name.isEmpty()) {
                return AnvilUpdateCallback.AnvilUpdateResult.withOutput(ItemStack.EMPTY, 0, 0);
            } else {
                ItemStack errorOutput = Items.BARRIER.getDefaultInstance();
                errorOutput.set(DataComponents.CUSTOM_NAME,
                        Component.translatable("invalid.player_name.too_long"));
                return AnvilUpdateCallback.AnvilUpdateResult.withOutput(errorOutput, 0, 0);
            }
        });
        EntityApiLookup.get(FabricTestSyncData.ID, AbstractedTestSyncData.class, Void.class)
                .registerFallback(FabricTestSyncLookupProvider.INSTANCE);
        ServerTickEvents.END_SERVER_TICK.register(FabricCommonEventHandler::onServerTick);
        ServerWorldEvents.LOAD.register(FabricCommonEventHandler::onServerWorldLoad);
        ServerWorldEvents.UNLOAD.register(FabricCommonEventHandler::onServerWorldUnLoad);
        ServerEntityEvents.ENTITY_LOAD.register(FabricCommonEventHandler::onEntityJoinWorld);
        ServerEntityEvents.ENTITY_UNLOAD.register(FabricCommonEventHandler::onEntityLeaveWorld);
        CommandRegistrationCallback.EVENT.register((commandDispatcher, commandBuildContext, commandSelection) -> {
            new Lib39HelpCommand(commandDispatcher, commandBuildContext);
        });
    }

    /**
     * The type Client opt.
     */
    public static class ClientOpt implements IClientOnly {
        /**
         * On client world load.
         *
         * @param clientLevel the client level
         */
        public static void onClientWorldLoad(@NotNull ClientLevel clientLevel) {
            synchronized (FabricCommonEventHandler.ClientOpt.class) {
                IClientOnly.check(() -> {
                    if (!clientLevel.dimension().equals(Level.OVERWORLD)) return;
                    if (!isSync2MInitialized) {
                        syncData2Manager = new SyncData2LookupManager();
                        SyncManagerRegisterCallback.EVENT.invoker().registerSyncManagers(new SyncManagerRegisterCallback.SyncManagerRegistrar(syncData2Manager));
                        Lib39.LOGGER.info("SyncData2Manager initialized on Client load");
                        ILevelHelper.LevelHelper.CLIENT.setLevel(clientLevel);
                    }
                });
            }
        }

        /**
         * On client world un load.
         *
         * @param clientLevel the client level
         */
        public static void onClientWorldUnLoad(@NotNull ClientLevel clientLevel) {
            synchronized (FabricCommonEventHandler.ClientOpt.class) {
                IClientOnly.check(() -> ILevelHelper.LevelHelper.CLIENT.setLevel(null));
            }
        }
    }

    /**
     * Init client.
     */
    public static void initClient() {
        ClientWorldCallback.LOAD.register(ClientOpt::onClientWorldLoad);
        ClientWorldCallback.LOAD.register(ClientOpt::onClientWorldUnLoad);
    }

    /**
     * Gets item add map.
     *
     * @return the item add map
     */
    public static Map<Supplier<Block>, ResourceKey<CreativeModeTab>[]> getItemAddMap() {
        return itemAddMap;
    }

}
