import { ItemStack } from "@minecraft/server";

export class HopperDropperComponent {
    constructor() {
        this.onTick = this.onTick.bind(this);
        this.onPlayerInteract = this.onPlayerInteract.bind(this);

        this.REDSTONE_POWER_BLOCKS = [
            { id: "minecraft:redstone_wire", state: "redstone_signal" },
            { id: "minecraft:powered_comparator", state: "minecraft:cardinal_direction" },
            { id: "minecraft:powered_repeater", state: "minecraft:cardinal_direction" },
            { id: "minecraft:redstone_torch", state: "torch_facing_direction" },
            { id: "minecraft:observer", state: "minecraft:facing_direction" },
            { id: "minecraft:lever", state: "open_bit" },
            { id: "minecraft:lever", state: "open_bit" },
            { id: "_button", state: "button_pressed_bit" },
            { id: "_pressure_plate", state: "redstone_signal" },
            { id: "minecraft:redstone_block", state: null },
        ];
    }

    onPlayerInteract(e) {
        const block = e.block;
        const verticalHalf = block.permutation.getState("minecraft:vertical_half")

        const player = e.player;
        let selectedSlot = player.selectedSlotIndex;
        let handItem = player.getComponent("inventory").container.getItem(selectedSlot);

        if (handItem) {
            if (handItem.typeId == "pb_red:redstone_wrench") {
                // switch hopper dropper direction
                block.dimension.runCommand(`playsound vr.stutterturn @a ${block.location.x} ${block.location.y} ${block.location.z}`)
                block.setPermutation(block.permutation.withState("minecraft:vertical_half", verticalHalf == "top" ? "bottom" : "top"))
                return;
            }
        }
    }

    hasRedstonePower(block) {
        const north = block.north();
        const east = block.east();
        const south = block.south();
        const west = block.west();
        const above = block.above();
        const below = block.below();

        const adjacentBlocks = [north, east, south, west, above, below];
        const directionList = ["north", "east", "south", "west", "up", "down"];

        return adjacentBlocks.some((adjacentBlock, index) => {
            if (!adjacentBlock) return false;

            const redstoneBlock = this.REDSTONE_POWER_BLOCKS.find(({ id }) => adjacentBlock.typeId.includes(id));
            if (!redstoneBlock) return false;

            const { id, state } = redstoneBlock;
            if (id === "minecraft:redstone_block") return true;

            if (id === "minecraft:redstone_wire") {
                let redstonePowerState = adjacentBlock.permutation.getState(state);
                return redstonePowerState > 0;
            }

            if (id === "minecraft:powered_comparator" || id === "minecraft:powered_repeater") {
                return adjacentBlock.permutation.getState(state) === directionList[index];
            }

            if (id === "minecraft:redstone_torch") {
                return adjacentBlock.permutation.getState(state) !== this.getOppositeDirection(directionList[index]) && index !== 4;
            }

            if (id === "minecraft:observer") {
                return adjacentBlock.permutation.getState(state) == directionList[index] && adjacentBlock.permutation.getState("powered_bit") == true;
            }

            if (id === "minecraft:lever") {
                return adjacentBlock.permutation.getState(state) == true;
            }

            if (id === "_button") {
                return adjacentBlock.permutation.getState(state) == true;
            }

            if (id === "_pressure_plate") {
                return adjacentBlock.permutation.getState(state) > 0;
            }

            return false;
        });
    }

    getOppositeDirection(direction) {
        switch (direction) {
            case 'north':
                return 'south';
            case 'south':
                return 'north';
            case 'east':
                return 'west';
            case 'west':
                return 'east';
            case 'up':
                return 'down';
            case 'down':
                return 'up';
        }
    }

    isValidContainer(block) {
        if (!block) return;
        const validContainers = [
            "minecraft:chest",
            "minecraft:hopper",
            "minecraft:barrel",
            "minecraft:dropper",
            "minecraft:dispenser",
            "minecraft:trapped_chest",
            //shulker box
            "minecraft:undyed_shulker_box",
            "minecraft:black_shulker_box",
            "minecraft:blue_shulker_box",
            "minecraft:brown_shulker_box",
            "minecraft:cyan_shulker_box",
            "minecraft:gray_shulker_box",
            "minecraft:green_shulker_box",
            "minecraft:light_blue_shulker_box",
            "minecraft:light_gray_shulker_box",
            "minecraft:lime_shulker_box",
            "minecraft:magenta_shulker_box",
            "minecraft:orange_shulker_box",
            "minecraft:pink_shulker_box",
            "minecraft:purple_shulker_box",
            "minecraft:red_shulker_box",
            "minecraft:white_shulker_box",
            "minecraft:yellow_shulker_box",


        ];
        return validContainers.includes(block.typeId);
    }

    getBlockBottom(b, verticalHalf) {
        const loc = b.location;
        if (verticalHalf == "bottom")
            return { x: loc.x + 0.5, y: loc.y - 0.1, z: loc.z + 0.5 };
        else
            return { x: loc.x + 0.5, y: loc.y + 1, z: loc.z + 0.5 };
    }

    getBlockTop(b, verticalHalf) {
        const loc = b.location;
        if (verticalHalf == "bottom")
            return { x: loc.x + 0.5, y: loc.y + 1, z: loc.z + 0.5 };
        else
            return { x: loc.x + 0.5, y: loc.y - 0.7, z: loc.z + 0.5 };
    }

    onTick(e) {
        const block = e.block;
        const verticalHalf = block.permutation.getState("minecraft:vertical_half")
        const chest = verticalHalf == "bottom" ? block.above() : block.below();
        const chestBelow = verticalHalf == "top" ? block.above() : block.below();
        const chestAbove = verticalHalf == "bottom" ? block.above() : block.below();

        if (chest?.typeId == block?.typeId || chestBelow?.typeId == block?.typeId) return;

        if (this.hasRedstonePower(block)) return;

        let itemsToDrop = [];

        let aboveEntityContainerId = undefined

        // Collecte des items depuis le coffre au-dessus
        if (this.isValidContainer(chest)) {
            let inventory = chest?.getComponent("minecraft:inventory")?.container;
            if (!inventory) return;
            for (let slot = 0; slot < inventory.size; slot++) {
                let chestItem = inventory.getItem(slot);
                if (chestItem) {
                    // Vérifier si le coffre en dessous a la place pour cet item
                    if (this.isValidContainer(chestBelow) && (!this.hasSpaceForItemInInventory(chestBelow, chestItem) || (chestItem.typeId.includes("shulker_box") && chestBelow.typeId.includes("shulker_box")))) {
                        continue; // Pas de place pour cet item dans le coffre en dessous
                    }
                    itemsToDrop.push(chestItem);
                    inventory.setItem(slot, new ItemStack("minecraft:air", 1)); // Vider le slot
                    break;
                }
            }

        } else {
            // Collecte des items depuis les entités item au-dessus
            for (const aboveEntity of block.dimension.getEntities({
                location: this.getBlockTop(block, verticalHalf),
                maxDistance: 0.5,
                type: "minecraft:item"
            })) {
                let aboveItem = aboveEntity.getComponent('minecraft:item').itemStack;
                if (aboveItem) {
                    // Vérifier si le coffre en dessous a la place pour cet item
                    if (this.isValidContainer(chestBelow) && (!this.hasSpaceForItemInInventory(chestBelow, aboveItem) || (aboveItem.typeId.includes("shulker_box") && chestBelow.typeId.includes("shulker_box")))) {
                        continue; // Passer cet item s’il n’y a pas de place
                    }
                    try {
                        itemsToDrop.push(aboveItem);
                        aboveEntity.remove();
                    } catch (e) {
                        //  console.warn(e);
                    }
                }
            }

            // Collecte des items depuis les inventaire d'entités au-dessus
            for (const aboveEntityContainer of block.dimension.getEntities({
                location: this.getBlockTop(block, verticalHalf),
                maxDistance: 1
            })) {
                if (aboveEntityContainer.typeId == "minecraft:player") continue
                const inventoryComponent = aboveEntityContainer.getComponent('inventory');
                if (!inventoryComponent) continue
                const inv = inventoryComponent.container
                aboveEntityContainerId = aboveEntityContainer.id

                for (let slot = 0; slot < inv.size; slot++) {
                    let invItem = inv.getItem(slot);
                    if (invItem) {
                        // Vérifier si le coffre en dessous a la place pour cet item
                        if (this.isValidContainer(chestBelow) && (!this.hasSpaceForItemInInventory(chestBelow, invItem) || (invItem.typeId.includes("shulker_box") && chestBelow.typeId.includes("shulker_box")))) {
                            continue; // Passer cet item s’il n’y a pas de place
                        }

                        itemsToDrop.push(invItem);
                        inv.setItem(slot, new ItemStack("minecraft:air", 1)); // Vider le slot
                        break;
                    }
                }
            }
        }

        if (itemsToDrop.length === 0) return;

        // Déposer les items dans le coffre en dessous ou les faire tomber s’il n’y a pas de conteneur valide
        if (this.isValidContainer(chestBelow)) {
            itemsToDrop.forEach((item) => {
                const remainingItems = this.setItemInChest(chestBelow, item);
                if (remainingItems > 0) {
                    // Laisser les items restants au-dessus du bloc si le coffre est plein
                    item.amount = remainingItems;

                    if (this.isValidContainer(chestAbove)) {
                        this.setItemInChest(chestAbove, item);
                    } else {
                        const aboveEntityContainer = block.dimension.getEntities().filter(entity => (entity.id == aboveEntityContainerId))[0]
                        if (aboveEntityContainer) {
                            this.setItemInChest(aboveEntityContainer, item);
                        } else {
                            const dropLoc = this.getBlockBottom(chestAbove, verticalHalf)
                            block.dimension.spawnItem(item, dropLoc).teleport(dropLoc);
                        }

                    }

                }
            });
        } else {

            const belowEntityContainer = block.dimension.getEntities({
                location: this.getBlockBottom(block, verticalHalf),
                maxDistance: 1
            }).filter(entity => (entity.getComponent("minecraft:inventory") && entity.typeId !== "minecraft:player" && entity.id !== aboveEntityContainerId))[0]

            if (belowEntityContainer) {
                itemsToDrop.forEach((item) => {
                    const remainingItems = this.setItemInChest(belowEntityContainer, item);
                    if (remainingItems > 0) {
                        // Laisser les items restants au-dessus du bloc si le coffre est plein
                        item.amount = remainingItems;

                        if (this.isValidContainer(chestAbove)) {
                            this.setItemInChest(chestAbove, item);
                        } else {
                            const aboveEntityContainer = block.dimension.getEntities().filter(entity => (entity.id == aboveEntityContainerId))[0]
                            if (aboveEntityContainer) {
                                this.setItemInChest(aboveEntityContainer, item);
                            } else {
                                const dropLoc = this.getBlockBottom(chestAbove, verticalHalf)
                                block.dimension.spawnItem(item, dropLoc).teleport(dropLoc);
                            }

                        }

                    }
                });

            } else {
                const dropLoc = this.getBlockBottom(block, verticalHalf)
                itemsToDrop.forEach((item) => {
                    block.dimension.spawnItem(item, dropLoc).teleport(dropLoc);
                    block.dimension.runCommand(`playsound random.pop @a ${block.location.x} ${block.location.y} ${block.location.z}`);
                });

            }

        }
    }

    // Ajoute un item dans le coffre et retourne le nombre restant s'il est plein
    setItemInChest(chestBelow, item) {
        let remainingAmount = item.amount;
        const destinationContainer = chestBelow.getComponent("minecraft:inventory").container;

        for (let slot = 0; slot < destinationContainer.size; slot++) {
            let slotItem = destinationContainer.getItem(slot);

            if (slotItem && slotItem.isStackableWith(item) && slotItem.amount < slotItem.maxAmount) {
                const availableAmount = slotItem.maxAmount - slotItem.amount;
                const amountToAdd = Math.min(availableAmount, remainingAmount);

                slotItem.amount += amountToAdd;
                destinationContainer.setItem(slot, slotItem);

                remainingAmount -= amountToAdd;
                if (remainingAmount <= 0) return 0; // Tout a été ajouté
            }
        }

        // Si des items restent, chercher un slot vide pour les ajouter
        for (let slot = 0; slot < destinationContainer.size; slot++) {
            let slotItem = destinationContainer.getItem(slot);

            if (!slotItem) {
                item.amount = remainingAmount;
                destinationContainer.setItem(slot, item);
                return 0; // Tous les items ont été ajoutés
            }
        }

        // Retourner le nombre restant si le coffre est plein
        return remainingAmount;
    }

    // Vérifie s’il existe un slot stackable ou un slot vide pour un item spécifique
    hasSpaceForItemInInventory(block, item) {
        let inventory = block.getComponent("minecraft:inventory").container;

        for (let slot = 0; slot < inventory.size; slot++) {
            let slotItem = inventory.getItem(slot);

            if (!slotItem || (slotItem.isStackableWith(item) && slotItem.amount < slotItem.maxAmount)) {
                return true; // Trouvé un slot vide ou stackable pour cet item
            }
        }

        return false; // Pas d’espace disponible pour cet item
    }
}
