package com.trunksbomb.minetriad.menu;

import java.util.List;

import com.trunksbomb.minetriad.card.CardDefinition;
import com.trunksbomb.minetriad.card.CardRegistry;
import com.trunksbomb.minetriad.card.CardStackData;
import com.trunksbomb.minetriad.registry.TriadDataComponents;
import com.trunksbomb.minetriad.registry.TriadItems;
import com.trunksbomb.minetriad.registry.TriadMenus;

import net.minecraft.core.NonNullList;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ClickType;
import net.minecraft.world.inventory.DataSlot;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.ItemContainerContents;

public class CardBinderMenu extends AbstractContainerMenu {
    public static final int CARDS_PER_LEVEL = 11;
    public static final int PAGE_COUNT = 10;
    public static final int BINDER_SLOT_COUNT = PAGE_COUNT * CARDS_PER_LEVEL;
    public static final int BUTTON_PREVIOUS_PAGE = 0;
    public static final int BUTTON_NEXT_PAGE = 1;
    public static final int BUTTON_HANDS = 2;
    public static final int NO_SELECTION = -1;

    private static final int SLOT_X_START = 8;
    private static final int SLOT_Y_START = 22;
    private static final int SLOT_SPACING = 18;
    private final InteractionHand hand;
    private final SimpleContainer binderContents;
    private final int lockedHotbarSlot;
    private int currentPage;
    private int selectedBinderSlot = NO_SELECTION;

    public CardBinderMenu(int containerId, Inventory inventory, FriendlyByteBuf extraData) {
        this(containerId, inventory, extraData.readEnum(InteractionHand.class));
    }

    public CardBinderMenu(int containerId, Inventory inventory, InteractionHand hand) {
        super(TriadMenus.CARD_BINDER.get(), containerId);
        this.hand = hand;
        this.lockedHotbarSlot = hand == InteractionHand.MAIN_HAND ? inventory.selected : -1;
        this.binderContents = new SimpleContainer(BINDER_SLOT_COUNT) {
            @Override
            public void setChanged() {
                super.setChanged();
                saveBinderContents(inventory.player);
            }
        };

        loadBinderContents(inventory.player);
        addDataSlot(new DataSlot() {
            @Override
            public int get() {
                return currentPage;
            }

            @Override
            public void set(int value) {
                currentPage = Math.clamp(value, 0, PAGE_COUNT - 1);
            }
        });
        addDataSlot(new DataSlot() {
            @Override
            public int get() {
                return selectedBinderSlot;
            }

            @Override
            public void set(int value) {
                selectedBinderSlot = value >= 0 && value < BINDER_SLOT_COUNT ? value : NO_SELECTION;
            }
        });

        addBinderSlots();
        addPlayerInventory(inventory);
    }

    public int currentPage() {
        return currentPage;
    }

    public int currentLevel() {
        return currentPage + 1;
    }

    public int selectedBinderSlot() {
        return selectedBinderSlot;
    }

    public ItemStack previewStack() {
        if (selectedBinderSlot < 0 || selectedBinderSlot >= BINDER_SLOT_COUNT) {
            return ItemStack.EMPTY;
        }
        ItemStack stack = binderContents.getItem(selectedBinderSlot);
        return stack.isEmpty() ? ItemStack.EMPTY : stack;
    }

    @Override
    public boolean stillValid(Player player) {
        return player.getItemInHand(hand).is(TriadItems.CARD_BINDER.get());
    }

    @Override
    public ItemStack quickMoveStack(Player player, int index) {
        Slot slot = slots.get(index);
        if (!slot.hasItem()) {
            return ItemStack.EMPTY;
        }

        ItemStack source = slot.getItem();
        ItemStack copy = source.copy();

        if (index < BINDER_SLOT_COUNT) {
            if (!moveItemStackTo(source, BINDER_SLOT_COUNT, slots.size(), false)) {
                return ItemStack.EMPTY;
            }
        } else {
            int binderStart = binderRangeStartFor(source);
            if (binderStart < 0 || !moveItemStackTo(source, binderStart, binderStart + CARDS_PER_LEVEL, false)) {
                return ItemStack.EMPTY;
            }
        }

        if (source.isEmpty()) {
            slot.set(ItemStack.EMPTY);
        } else {
            slot.setChanged();
        }

        normalizeSelection();

        return copy;
    }

    @Override
    public boolean clickMenuButton(Player player, int id) {
        if (id == BUTTON_PREVIOUS_PAGE && currentPage > 0) {
            currentPage--;
            broadcastChanges();
            return true;
        }
        if (id == BUTTON_NEXT_PAGE && currentPage < PAGE_COUNT - 1) {
            currentPage++;
            broadcastChanges();
            return true;
        }
        if (id == BUTTON_HANDS) {
            return true;
        }
        return false;
    }

    @Override
    public void removed(Player player) {
        clearSelection();
        saveBinderContents(player);
        super.removed(player);
    }

    @Override
    public void clicked(int slotId, int button, ClickType clickType, Player player) {
        if (slotId >= 0 && slotId < BINDER_SLOT_COUNT && clickType == ClickType.PICKUP && getCarried().isEmpty()) {
            Slot slot = slots.get(slotId);
            if (slot.hasItem()) {
                if (selectedBinderSlot == slotId) {
                    super.clicked(slotId, 1, ClickType.PICKUP, player);
                    normalizeSelection();
                } else {
                    selectedBinderSlot = slotId;
                    broadcastChanges();
                }
                saveBinderContents(player);
                return;
            }
        }

        super.clicked(slotId, button, clickType, player);
        normalizeSelection();
        saveBinderContents(player);
    }

    private void addBinderSlots() {
        for (int page = 0; page < PAGE_COUNT; page++) {
            for (int slotOnPage = 0; slotOnPage < CARDS_PER_LEVEL; slotOnPage++) {
                int slotIndex = page * CARDS_PER_LEVEL + slotOnPage;
                int row = slotOnPage / 2;
                int column = slotOnPage % 2;
                int x = SLOT_X_START + column * SLOT_SPACING;
                int y = SLOT_Y_START + row * SLOT_SPACING;
                addSlot(new BinderSlot(binderContents, slotIndex, x, y, page, page + 1));
            }
        }
    }

    private void addPlayerInventory(Inventory inventory) {
        for (int row = 0; row < 3; row++) {
            for (int column = 0; column < 9; column++) {
                addSlot(new Slot(inventory, column + row * 9 + 9, 8 + column * 18, 140 + row * 18));
            }
        }

        for (int slot = 0; slot < 9; slot++) {
            int inventoryIndex = slot;
            int x = 8 + slot * 18;
            int y = 198;
            if (slot == lockedHotbarSlot) {
                addSlot(new LockedSlot(inventory, inventoryIndex, x, y));
            } else {
                addSlot(new Slot(inventory, inventoryIndex, x, y));
            }
        }
    }

    private void loadBinderContents(Player player) {
        ItemStack binderStack = player.getItemInHand(hand);
        ItemContainerContents contents = binderStack.getOrDefault(DataComponents.CONTAINER, ItemContainerContents.EMPTY);
        NonNullList<ItemStack> items = NonNullList.withSize(BINDER_SLOT_COUNT, ItemStack.EMPTY);
        contents.copyInto(items);
        for (int i = 0; i < BINDER_SLOT_COUNT; i++) {
            binderContents.setItem(i, i < items.size() ? items.get(i) : ItemStack.EMPTY);
        }
    }

    private void saveBinderContents(Player player) {
        ItemStack binderStack = player.getItemInHand(hand);
        if (!binderStack.is(TriadItems.CARD_BINDER.get())) {
            return;
        }

        NonNullList<ItemStack> items = NonNullList.withSize(BINDER_SLOT_COUNT, ItemStack.EMPTY);
        for (int i = 0; i < BINDER_SLOT_COUNT; i++) {
            items.set(i, binderContents.getItem(i));
        }
        binderStack.set(DataComponents.CONTAINER, ItemContainerContents.fromItems(items));
    }

    private void normalizeSelection() {
        if (selectedBinderSlot < 0 || selectedBinderSlot >= BINDER_SLOT_COUNT || binderContents.getItem(selectedBinderSlot).isEmpty()) {
            clearSelection();
        }
    }

    private void clearSelection() {
        selectedBinderSlot = NO_SELECTION;
    }

    private static int binderRangeStartFor(ItemStack stack) {
        CardStackData data = stack.get(TriadDataComponents.CARD_DATA);
        if (!stack.is(TriadItems.TRIAD_CARD.get()) || data == null || data.equals(CardStackData.EMPTY)) {
            return -1;
        }

        CardDefinition definition = CardRegistry.get(data.cardId());
        return (definition.level() - 1) * CARDS_PER_LEVEL;
    }

    private final class BinderSlot extends Slot {
        private final int pageIndex;
        private final int expectedLevel;

        private BinderSlot(SimpleContainer container, int slot, int x, int y, int pageIndex, int expectedLevel) {
            super(container, slot, x, y);
            this.pageIndex = pageIndex;
            this.expectedLevel = expectedLevel;
        }

        @Override
        public boolean mayPlace(ItemStack stack) {
            CardStackData data = stack.get(TriadDataComponents.CARD_DATA);
            if (!stack.is(TriadItems.TRIAD_CARD.get()) || data == null || data.equals(CardStackData.EMPTY)) {
                return false;
            }
            return CardRegistry.get(data.cardId()).level() == expectedLevel;
        }

        @Override
        public int getMaxStackSize() {
            return 1;
        }

        @Override
        public boolean isActive() {
            return pageIndex == currentPage;
        }
    }

    private static final class LockedSlot extends Slot {
        private LockedSlot(Inventory inventory, int slot, int x, int y) {
            super(inventory, slot, x, y);
        }

        @Override
        public boolean mayPickup(Player player) {
            return false;
        }

        @Override
        public boolean mayPlace(ItemStack stack) {
            return false;
        }
    }
}
