package cz.jzitnik.game.ui;

import cz.jzitnik.game.Game;
import cz.jzitnik.game.entities.items.InventoryItem;
import cz.jzitnik.tui.ScreenRenderer;
import org.jline.terminal.MouseEvent;
import org.jline.terminal.Terminal;

import java.util.Optional;

import static cz.jzitnik.game.ui.Inventory.*;

/**
 * Handles mouse click events on the inventory UI, dispatching to appropriate
 * handlers for crafting table, hotbar, and main inventory.
 */
public class InventoryClickHandler {
    /**
     * Processes a mouse click event within the inventory UI.
     *
     * @param mouseEvent    The mouse event to handle.
     * @param terminal      Terminal instance for dimension calculations.
     * @param screenRenderer Renderer to update the screen after changes.
     * @param game          Current game instance.
     * @param moveTopCustom Optional custom top offset.
     * @param i             Optional inventory items array for manipulation.
     */
    public static void click(MouseEvent mouseEvent, Terminal terminal, ScreenRenderer screenRenderer, Game game,
            Optional<Integer> moveTopCustom, Optional<InventoryItem[]> i) {
        if (mouseEvent.getType() != MouseEvent.Type.Pressed)
            return;

        var inventory = game.getInventory();
        int x = mouseEvent.getX();
        int y = mouseEvent.getY();

        int moveLeft = calculateMoveLeft(terminal);
        int moveTop = moveTopCustom.orElseGet(() -> calculateMoveTop(terminal));

        if (handleCraftingTableClick(mouseEvent, x, y, inventory, screenRenderer, game, moveLeft, moveTop, i))
            return;
        if (handleHotbarClick(mouseEvent, x, y, inventory, screenRenderer, game, terminal, moveTop, i))
            return;
        handleInventoryClick(mouseEvent, x, y, inventory, screenRenderer, game, moveLeft, moveTop, i);
    }

    private static int calculateMoveLeft(Terminal terminal) {
        return Math.max(0, (terminal.getWidth() / 2) - ((COLUMN_AMOUNT * (50 + 4) + 2) / 2));
    }

    private static int calculateMoveTop(Terminal terminal) {
        return Math.max(0, (terminal.getHeight() / 2) - ((ROW_AMOUNT * (25 + 1)) / 2));
    }

    private static boolean handleCraftingTableClick(MouseEvent mouseEvent, int x, int y, Inventory inventory,
            ScreenRenderer screenRenderer, Game game, int moveLeft, int moveTop, Optional<InventoryItem[]> i) {
        int startLeftCrafting = moveLeft + (COLUMN_AMOUNT * (50 + 4) + 2) + 20;
        int startTopCrafting = moveTop + 26;

        if (x < startLeftCrafting || y < startTopCrafting || x > startLeftCrafting + 166 || y > startTopCrafting + 51)
            return false;

        int craftX = x - startLeftCrafting;
        int craftY = y - startTopCrafting;

        if (craftX > 116) {
            if (craftY > 10 && craftY < 37) {
                inventory.getSmallCraftingTable().pickup();
                screenRenderer.render(game);
            }
            return true;
        }

        if (craftX > 105) {
            return true;
        }

        int blockIndex = (craftY / 26) * 2 + (craftX / 53);
        handleItemClick(mouseEvent, inventory, inventory.getSmallCraftingTable().getItems(), blockIndex, 29, i);
        screenRenderer.render(game);
        return true;
    }

    private static boolean handleHotbarClick(MouseEvent mouseEvent, int x, int y, Inventory inventory,
            ScreenRenderer screenRenderer, Game game, Terminal terminal, int moveTop, Optional<InventoryItem[]> i) {
        int startLeftHotbar = (terminal.getWidth() / 2) - (INVENTORY_SIZE_PX / 2) + 2;
        int startTopHotbar = moveTop + (ROW_AMOUNT * (25 + 1)) + 10;

        if (x < startLeftHotbar || y < startTopHotbar || x > startLeftHotbar + INVENTORY_SIZE_PX - 2
                || y > startTopHotbar + 26)
            return false;

        int index = (x - startLeftHotbar) / 52;
        handleItemClick(mouseEvent, inventory, inventory.getHotbar(), index, 20, i);
        screenRenderer.render(game);
        return true;
    }

    private static void handleInventoryClick(MouseEvent mouseEvent, int x, int y, Inventory inventory,
            ScreenRenderer screenRenderer, Game game, int moveLeft, int moveTop, Optional<InventoryItem[]> i) {
        int fx = x - moveLeft;
        int fy = y - moveTop;

        int widthPixels = COLUMN_AMOUNT * (50 + 4) + 2;
        int heightPixels = ROW_AMOUNT * (25 + 1);

        if (fx < 0 || fx > widthPixels - 3 || fy < 0 || fy > heightPixels - 1)
            return;

        int blockIndex = (fy / 26) * COLUMN_AMOUNT + (fx / 54);
        handleItemClick(mouseEvent, inventory, inventory.getItems(), blockIndex, 0, i);
        screenRenderer.render(game);
    }

    /**
     * Handles item clicks with left and right mouse button logic for picking up,
     * placing, and merging items.
     *
     * @param mouseEvent The mouse event triggered by the user.
     * @param inventory  The inventory instance to modify.
     * @param items      The item array being manipulated.
     * @param index      The index of the clicked item.
     * @param offset     The offset to apply to the index.
     * @param i          Optional inventory items array for manipulation.
     */
    public static void handleItemClick(MouseEvent mouseEvent, Inventory inventory, Object[] items, int index,
            int offset, Optional<InventoryItem[]> i) {
        int actualIndex = index + offset;

        if (inventory.getSelectedItemInv() == actualIndex) {
            inventory.setSelectedItemInv(-1);
            return;
        }

        switch (mouseEvent.getButton()) {
            case Button1 -> {
                if (items[index] == null && inventory.hasSelectedItem()) {
                    items[index] = inventory.isRightClick() ? inventory.getHalf(i) : inventory.getSelectedItem(i);
                } else if (items[index] != null) {
                    if (inventory.hasSelectedItem()) {
                        // Merge items
                        if (!inventory.mergeItems(inventory.getSelectedItemInv(), actualIndex, i)) {
                            inventory.setSelectedItemInv(index + offset);
                        }
                    } else {
                        inventory.setSelectedItemInv(index + offset);
                    }
                }
            }
            case Button3 -> {
                if (items[index] != null) {
                    if (inventory.hasSelectedItem()) {
                        // Merge just one item into the stack
                        inventory.mergeOne(inventory.getSelectedItemInv(), actualIndex, i);
                    } else {
                        // Pick up one item
                        inventory.setSelectedItemInv(actualIndex);
                        inventory.setRightClick(true);
                    }
                } else {
                    // Place one item
                    items[index] = inventory.getOne(i);
                }
            }
        }
    }
}
