package net.wesjd.anvilgui.version;

import net.minecraft.core.BlockPos;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.game.ClientboundContainerClosePacket;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.inventory.AnvilMenu;
import net.minecraft.world.inventory.ContainerLevelAccess;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.craftbukkit.util.CraftChatMessage;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.Inventory;

public final class VersionWrapper {
    public static final VersionWrapper INSTANCE = new VersionWrapper();

    /**
     * Interface implemented by the custom NMS AnvilContainer used to interact with it directly
     */
    public interface AnvilContainerWrapper {

        /**
         * Retrieves the raw text that has been entered into the Anvil at the moment
         * <br><br>
         * This field is marked as public in the Minecraft AnvilContainer only from Minecraft 1.11 and upwards
         *
         * @return The raw text in the rename field
         */
        default String getRenameText() {
            return null;
        }

        /**
         * Sets the provided text as the literal hovername of the item in the left input slot
         *
         * @param text The text to set
         */
        default void setRenameText(String text) {
        }

        /**
         * Gets the {@link Inventory} wrapper of the NMS container
         *
         * @return The inventory of the NMS container
         */
        Inventory getBukkitInventory();
    }

    private static class AnvilContainer extends AnvilMenu implements AnvilContainerWrapper {
        public AnvilContainer(Player player, int containerId, Component guiTitle) {
            super(
                    containerId,
                    ((CraftPlayer) player).getHandle().getInventory(),
                    ContainerLevelAccess.create(((CraftWorld) player.getWorld()).getHandle(), new BlockPos(0, 0, 0))
            );
            this.checkReachable = false;
            setTitle(guiTitle);
        }

        public int getContainerId() {
            return this.containerId;
        }

        public String getRenameText() {
            return this.itemName;
        }

        public void setRenameText(String text) {
            // If an item is present in the left input slot change its hover name to the literal text.
            Slot inputLeft = getSlot(0);
            if (inputLeft.hasItem()) {
                inputLeft.getItem()
                        .set(DataComponents.CUSTOM_NAME, Component.literal(text));
            }
        }

        public Inventory getBukkitInventory() {
            return this.getBukkitView().getTopInventory();
        }
    }

    private int getRealNextContainerId(Player player) {
        return toNMS(player).nextContainerCounter();
    }

    /**
     * Turns a {@link Player} into an NMS one
     *
     * @param player The player to be converted
     * @return the NMS EntityPlayer
     */
    private ServerPlayer toNMS(Player player) {
        return ((CraftPlayer) player).getHandle();
    }

    public int getNextContainerId(Player player, AnvilContainerWrapper container) {
        return ((AnvilContainer) container).getContainerId();
    }

    public void handleInventoryCloseEvent(Player player) {
        CraftEventFactory.handleInventoryCloseEvent(toNMS(player), InventoryCloseEvent.Reason.UNKNOWN);
        toNMS(player).doCloseContainer();
    }

    public void sendPacketOpenWindow(Player player, int containerId, Object inventoryTitle) {
        toNMS(player).connection.send(new ClientboundOpenScreenPacket(containerId, MenuType.ANVIL, (Component) inventoryTitle));
    }

    public void sendPacketCloseWindow(Player player, int containerId) {
        toNMS(player).connection.send(new ClientboundContainerClosePacket(containerId));
    }

    public void sendPacketExperienceChange(Player player, int experienceLevel) {
        toNMS(player).connection.send(new ClientboundSetExperiencePacket(0f, 0, experienceLevel));
    }

    public void setActiveContainerDefault(Player player) {
        toNMS(player).containerMenu = toNMS(player).inventoryMenu;
    }

    public void setActiveContainer(Player player, AnvilContainerWrapper container) {
        toNMS(player).containerMenu = (AnvilMenu) container;
    }

    public void setActiveContainerId(AnvilContainerWrapper container, int containerId) {
    }

    public void addActiveContainerSlotListener(AnvilContainerWrapper container, Player player) {
        toNMS(player).initMenu((AnvilMenu) container);
    }

    public AnvilContainerWrapper newContainerAnvil(Player player, Object title) {
        return new AnvilContainer(player, getRealNextContainerId(player), (Component) title);
    }

    public Object literalChatComponent(String content) {
        return Component.literal(content);
    }

    public Object jsonChatComponent(String json) {
        return CraftChatMessage.fromJSON(json);
    }
}