package me.loganb1max.minionsplus.menu;

import me.loganb1max.minionsplus.MinionsPlus;
import me.loganb1max.minionsplus.model.Fuel;
import me.loganb1max.minionsplus.model.Minion;
import me.loganb1max.minionsplus.util.ItemBuilder;
import me.loganb1max.minionsplus.util.Text;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

public class MinionMenu implements Listener {

	private final Minion minion;
	private final Player player;
	private final Inventory inventory;
	private final Map<Integer, Consumer<ClickType>> clickHandlerMap = new HashMap<>();

	public MinionMenu(final Minion minion, final Player player) {
		this.minion = minion;
		this.player = player;
		Bukkit.getPluginManager().registerEvents(this, MinionsPlus.getInstance());
		this.inventory = Bukkit.createInventory(this.player, 9, "Minion Menu");
		rebind();
		this.player.openInventory(this.inventory);
	}

	public void rebind() {
		bindItem(
				minion.getIcon(),
				1,
				clickType -> { }
		);

		bindItem(
				ItemBuilder.of(Material.GOLD_INGOT)
						.name("&6Upgrade")
						.lore("&7Click to upgrade this minion.")
						.lore("")
						.lore(this.minion.isMaxLevel() ? "&aMax Level" : "&7Price: &f$" + this.minion.getPrice(minion.getLevel() + 1))
						.build(),
				3,
				clickType -> {
					if (this.minion.isMaxLevel()) {
						this.player.sendMessage(Text.color("&cThis minion is already max level."));
						return;
					}
					if (!MinionsPlus.getEcon().withdrawPlayer(this.player, this.minion.getPrice(this.minion.getLevel() + 1)).transactionSuccess()) {
						this.player.sendMessage(Text.color("&cYou can not afford this upgrade!"));
						return;
					}
					this.minion.levelUp();
					this.player.sendMessage(Text.color("&aYou have successfully upgraded this minion."));
					rebind();
				}
		);

		bindItem(
				ItemBuilder.of(Material.COAL)
						.name("&6Deposit Energy")
						.lore(MinionsPlus.getInstance().getFuels().stream().map(Fuel::getDisplayString).collect(Collectors.toList()))
						.build(),
				5,
				clickType -> {
					double totalGained = 0.0;
					Set<Fuel> fuels = MinionsPlus.getInstance().getFuels();
					for (int i = 0; i < this.player.getInventory().getSize(); i++) {
						final ItemStack item = this.player.getInventory().getItem(i);
						if (item == null || item.getType() == Material.AIR) continue;
						for (Fuel fuel : fuels) {
							if (!fuel.isEqual(item)) continue;
							this.minion.gainEnergy(item.getAmount() * fuel.getEnergy());
							totalGained += item.getAmount() * fuel.getEnergy();
							this.player.getInventory().setItem(i, new ItemStack(Material.AIR));
						}
					}
					this.player.sendMessage(Text.color("&aYou successfully refueled this minion. Added " + totalGained + " energy."));
					rebind();
				}
		);

		bindItem(
				ItemBuilder.of(Material.CHEST)
						.name("&6Link Chest")
						.lore("&7Click to link a deposit chest.")
						.build(),
				7,
				clickType -> {
					MinionsPlus.getInstance().getLinkingManager().beginLinking(this.player, this.minion);
					this.player.closeInventory();
				}
		);
	}

	private void bindItem(final ItemStack item, final int slot, final Consumer<ClickType> consumer) {
		this.inventory.setItem(slot, item);
		this.clickHandlerMap.remove(slot);
		this.clickHandlerMap.put(slot, consumer);
		this.player.updateInventory();
	}

	@EventHandler(priority = EventPriority.HIGHEST)
	public void onClose(final InventoryCloseEvent e) {
		if (!e.getInventory().equals(this.inventory)) return;
		HandlerList.unregisterAll(this);
	}

	@EventHandler(priority = EventPriority.HIGHEST)
	public void onClick(final InventoryClickEvent e) {
		if (e.getSlotType() == InventoryType.SlotType.OUTSIDE) return;
		if (!e.getClickedInventory().equals(this.inventory)) return;
		e.setCancelled(true);
		this.clickHandlerMap.get(e.getSlot()).accept(e.getClick());
	}

}
