/*
 *  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.content.menu;

import net.minecraft.core.NonNullList;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.Container;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.inventory.*;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.Level;
import net.minecraftforge.common.ForgeHooks;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.eroticdungeongame.content.recipe.DungeonRecipe;
import top.r3944realms.eroticdungeongame.content.recipe.EDGRecipeBookTypes;
import top.r3944realms.eroticdungeongame.core.register.EDGBlocks;
import top.r3944realms.eroticdungeongame.core.register.EDGMenuTypes;
import top.r3944realms.eroticdungeongame.core.register.EDGRecipeTypes;

import java.util.Objects;
import java.util.Optional;

public class DungeonCraftingMenu extends RecipeBookMenu<CraftingContainer> {
    public static final int RESULT_SLOT = 0;
    private static final int CRAFT_SLOT_START = 1;
    private static final int CRAFT_SLOT_END = 10;
    private static final int INV_SLOT_START = 10;
    private static final int INV_SLOT_END = 37;
    private static final int USE_ROW_SLOT_START = 37;
    private static final int USE_ROW_SLOT_END = 46;

    private final CraftingContainer craftSlots;
    private final ResultContainer resultSlots;
    private final ContainerLevelAccess access;
    private final Player player;
    public DungeonCraftingMenu(int containerId, Inventory playerInventory) {
        this(containerId, playerInventory, ContainerLevelAccess.NULL);
    }

    public DungeonCraftingMenu(int containerId, Inventory playerInventory, ContainerLevelAccess access) {
        super(EDGMenuTypes.DUNGEON_CRAFTING_MENU.get(), containerId);
        this.craftSlots = new TransientCraftingContainer(this, 3, 3);
        this.resultSlots = new ResultContainer();
        this.access = access;
        this.player = playerInventory.player;

        // 结果槽位
        this.addSlot(new ResultSlot(playerInventory.player, this.craftSlots, this.resultSlots, RESULT_SLOT, 124, 35) {
            @Override
            public boolean mayPlace(@NotNull ItemStack stack) {
                return false;
            }

            @Override
            public void onTake(@NotNull Player player, @NotNull ItemStack stack) {
                this.checkTakeAchievements(stack);
                ForgeHooks.setCraftingPlayer(player);
                NonNullList<ItemStack> nonnulllist = player.level().getRecipeManager().getRemainingItemsFor(EDGRecipeTypes.DUNGEON_CRAFTING.get(), DungeonCraftingMenu.this.craftSlots, player.level());
                ForgeHooks.setCraftingPlayer(null);

                for(int i = 0; i < nonnulllist.size(); ++i) {
                    ItemStack itemstack = DungeonCraftingMenu.this.craftSlots.getItem(i);
                    ItemStack itemstack1 = nonnulllist.get(i);
                    if (!itemstack.isEmpty()) {
                        DungeonCraftingMenu.this.craftSlots.removeItem(i, 1);
                        itemstack = DungeonCraftingMenu.this.craftSlots.getItem(i);
                    }

                    if (!itemstack1.isEmpty()) {
                        if (itemstack.isEmpty()) {
                            DungeonCraftingMenu.this.craftSlots.setItem(i, itemstack1);
                        } else if (ItemStack.isSameItemSameTags(itemstack, itemstack1)) {
                            itemstack1.grow(itemstack.getCount());
                            DungeonCraftingMenu.this.craftSlots.setItem(i, itemstack1);
                        } else if (!DungeonCraftingMenu.this.player.getInventory().add(itemstack1)) {
                            DungeonCraftingMenu.this.player.drop(itemstack1, false);
                        }
                    }
                }

            }
        });

        // 合成网格槽位
        for(int i = 0; i < 3; ++i) {
            for(int j = 0; j < 3; ++j) {
                this.addSlot(new Slot(this.craftSlots, j + i * 3, 30 + j * 18, 17 + i * 18));
            }
        }

        // 玩家物品栏槽位
        for(int i = 0; i < 3; ++i) {
            for(int j = 0; j < 9; ++j) {
                this.addSlot(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
            }
        }

        // 玩家快捷栏槽位
        for(int i = 0; i < 9; ++i) {
            this.addSlot(new Slot(playerInventory, i, 8 + i * 18, 142));
        }
    }
    protected static void slotChangedCraftingGrid(AbstractContainerMenu menu, @NotNull Level level, Player player,
                                                  CraftingContainer container, ResultContainer result) {
        if (!level.isClientSide && player instanceof ServerPlayer serverPlayer) {
            ItemStack resultStack = ItemStack.EMPTY;

            Optional<DungeonRecipe> optional = Objects.requireNonNull(level.getServer()).getRecipeManager()
                    .getRecipeFor(EDGRecipeTypes.DUNGEON_CRAFTING.get(), container, level);

            if (optional.isPresent()) {
                DungeonRecipe recipe = optional.get();
                if (result.setRecipeUsed(level, serverPlayer, recipe)) {
                    ItemStack craftedStack = recipe.assemble(container, level.registryAccess());
                    if (craftedStack.isItemEnabled(level.enabledFeatures())) {
                        resultStack = craftedStack;
                    }
                }
            }

            result.setItem(RESULT_SLOT, resultStack);
            menu.setRemoteSlot(RESULT_SLOT, resultStack);
            serverPlayer.connection.send(new ClientboundContainerSetSlotPacket(
                    menu.containerId, menu.incrementStateId(), RESULT_SLOT, resultStack));
        }
    }

    @Override
    public void slotsChanged(@NotNull Container container) {
        this.access.execute((level, serverPlayer) -> {
            slotChangedCraftingGrid(this, level, player, craftSlots, resultSlots);
        });
    }

    @Override
    public void fillCraftSlotsStackedContents(@NotNull StackedContents stackedContents) {
        this.craftSlots.fillStackedContents(stackedContents);
    }

    @Override
    public void clearCraftingContent() {
        this.craftSlots.clearContent();
        this.resultSlots.clearContent();
    }

    @Override
    public boolean recipeMatches(@NotNull Recipe<? super CraftingContainer> recipe) {
        return recipe.matches(this.craftSlots, this.player.level());
    }

    @Override
    public int getResultSlotIndex() {
        return RESULT_SLOT;
    }

    @Override
    public int getGridWidth() {
        return this.craftSlots.getWidth();
    }

    @Override
    public int getGridHeight() {
        return this.craftSlots.getHeight();
    }

    @Override
    public int getSize() {
        return CRAFT_SLOT_END;
    }

    @Override
    public @NotNull RecipeBookType getRecipeBookType() {
        return EDGRecipeBookTypes.DUNGEON_CRAFTING;
    }

    @Override
    public boolean shouldMoveToInventory(int slotIndex) {
        return slotIndex != this.getResultSlotIndex();
    }

    @Override
    public @NotNull ItemStack quickMoveStack(@NotNull Player player, int index) {
        ItemStack itemstack = ItemStack.EMPTY;
        Slot slot = this.slots.get(index);
        if (slot.hasItem()) {
            ItemStack itemstack1 = slot.getItem();
            itemstack = itemstack1.copy();
            if (index == RESULT_SLOT) {
                this.access.execute((level, blockPos) -> itemstack1.getItem().onCraftedBy(itemstack1, level, player));
                if (!this.moveItemStackTo(itemstack1, INV_SLOT_START, USE_ROW_SLOT_END, true)) {
                    return ItemStack.EMPTY;
                }

                slot.onQuickCraft(itemstack1, itemstack);
            } else if (index >= INV_SLOT_START && index < USE_ROW_SLOT_END) {
                if (!this.moveItemStackTo(itemstack1, CRAFT_SLOT_START, CRAFT_SLOT_END, false)) {
                    if (index < INV_SLOT_END) {
                        if (!this.moveItemStackTo(itemstack1, USE_ROW_SLOT_START, USE_ROW_SLOT_END, false)) {
                            return ItemStack.EMPTY;
                        }
                    } else if (!this.moveItemStackTo(itemstack1, INV_SLOT_START, USE_ROW_SLOT_START, false)) {
                        return ItemStack.EMPTY;
                    }
                }
            } else if (!this.moveItemStackTo(itemstack1, INV_SLOT_START, USE_ROW_SLOT_END, false)) {
                return ItemStack.EMPTY;
            }

            if (itemstack1.isEmpty()) {
                slot.setByPlayer(ItemStack.EMPTY);
            } else {
                slot.setChanged();
            }

            if (itemstack1.getCount() == itemstack.getCount()) {
                return ItemStack.EMPTY;
            }

            slot.onTake(player, itemstack1);
            if (index == RESULT_SLOT) {
                player.drop(itemstack1, false);
            }
        }

        return itemstack;
    }

    @Override
    public void removed(Player player) {
        super.removed(player);
        this.access.execute((level, serverPlayer) -> this.clearContainer(player, this.craftSlots));
    }

    @Override
    public boolean stillValid(@NotNull Player player) {
        return stillValid(this.access, player, EDGBlocks.DUNGEON_CRAFTING_TABLE.get());
    }
}
