package org.screamingsandals.bedwars.inventories;

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.screamingsandals.bedwars.Main;
import org.screamingsandals.bedwars.api.Team;
import org.screamingsandals.bedwars.api.events.*;
import org.screamingsandals.bedwars.api.game.GameStore;
import org.screamingsandals.bedwars.api.game.ItemSpawnerType;
import org.screamingsandals.bedwars.api.upgrades.Upgrade;
import org.screamingsandals.bedwars.api.upgrades.UpgradeRegistry;
import org.screamingsandals.bedwars.api.upgrades.UpgradeStorage;
import org.screamingsandals.bedwars.game.CurrentTeam;
import org.screamingsandals.bedwars.game.Game;
import org.screamingsandals.bedwars.game.GamePlayer;
import org.screamingsandals.bedwars.utils.Debugger;
import org.screamingsandals.bedwars.utils.Sounds;
import org.screamingsandals.lib.debug.Debug;
import org.screamingsandals.simpleinventories.SimpleInventories;
import org.screamingsandals.simpleinventories.events.GenerateItemEvent;
import org.screamingsandals.simpleinventories.events.PreActionEvent;
import org.screamingsandals.simpleinventories.events.ShopTransactionEvent;
import org.screamingsandals.simpleinventories.inventory.Options;
import org.screamingsandals.simpleinventories.item.ItemProperty;
import org.screamingsandals.simpleinventories.item.PlayerItemInfo;
import org.screamingsandals.simpleinventories.utils.MapReader;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static misat11.lib.lang.I18n.i18n;
import static misat11.lib.lang.I18n.i18nonly;

public class ShopInventory implements Listener {
	private Map<String, SimpleInventories> shopMap = new HashMap<>();
	private Options options = new Options(Main.getInstance());

	public ShopInventory() {
		Bukkit.getServer().getPluginManager().registerEvents(this, Main.getInstance());

		ItemStack backItem = Main.getConfigurator().readDefinedItem("shopback", "BARRIER");
		ItemMeta backItemMeta = backItem.getItemMeta();
		backItemMeta.setDisplayName(i18n("shop_back", false));
		backItem.setItemMeta(backItemMeta);
		options.setBackItem(backItem);

		ItemStack pageBackItem = Main.getConfigurator().readDefinedItem("pageback", "ARROW");
		ItemMeta pageBackItemMeta = backItem.getItemMeta();
		pageBackItemMeta.setDisplayName(i18n("page_back", false));
		pageBackItem.setItemMeta(pageBackItemMeta);
		options.setPageBackItem(pageBackItem);

		ItemStack pageForwardItem = Main.getConfigurator().readDefinedItem("pageforward", "ARROW");
		ItemMeta pageForwardItemMeta = backItem.getItemMeta();
		pageForwardItemMeta.setDisplayName(i18n("page_forward", false));
		pageForwardItem.setItemMeta(pageForwardItemMeta);
		options.setPageForwardItem(pageForwardItem);

		ItemStack cosmeticItem = Main.getConfigurator().readDefinedItem("shopcosmetic", "AIR");
		options.setCosmeticItem(cosmeticItem);
        
		options.setRows(Main.getConfigurator().config.getInt("shop.rows", 4));
		options.setRender_actual_rows(Main.getConfigurator().config.getInt("shop.render-actual-rows", 6));
		options.setRender_offset(Main.getConfigurator().config.getInt("shop.render-offset", 9));
		options.setRender_header_start(Main.getConfigurator().config.getInt("shop.render-header-start", 0));
		options.setRender_footer_start(Main.getConfigurator().config.getInt("shop.render-footer-start", 45));
		options.setItems_on_row(Main.getConfigurator().config.getInt("shop.items-on-row", 9));
		options.setShowPageNumber(Main.getConfigurator().config.getBoolean("shop.show-page-numbers", true));
		options.setInventoryType(InventoryType.valueOf(Main.getConfigurator().config.getString("shop.inventory-type", "CHEST")));
		
		options.setPrefix(i18nonly("item_shop_name", "[BW] Shop"));
		options.setGenericShop(true);
		options.setGenericShopPriceTypeRequired(true);
		options.setAnimationsEnabled(true);
		
		options.registerPlaceholder("team", (key, player, arguments) -> {
			GamePlayer gPlayer = Main.getPlayerGameProfile(player);
			CurrentTeam team = gPlayer.getGame().getPlayerTeam(gPlayer);
			if (arguments.length > 0) {
				String fa = arguments[0];
				switch (fa) {
				case "color":
					return team.teamInfo.color.name();
				case "chatcolor":
					return team.teamInfo.color.chatColor.toString();
				case "maxplayers":
					return Integer.toString(team.teamInfo.maxPlayers);
				case "players":
					return Integer.toString(team.players.size());
				case "hasBed":
					return Boolean.toString(team.isBed);
				}
			}
			return team.getName();
		});
		options.registerPlaceholder("spawner", (key, player, arguments) -> {
			GamePlayer gPlayer = Main.getPlayerGameProfile(player);
			Game game = gPlayer.getGame();
			if (arguments.length > 2) {
				String upgradeBy = arguments[0];
				String upgrade = arguments[1];
				UpgradeStorage upgradeStorage = UpgradeRegistry.getUpgrade("spawner");
				if (upgradeStorage == null) {
					return null;
				}
				List<Upgrade> upgrades = null;
				switch (upgradeBy) {
				case "name":
					upgrades = upgradeStorage.findItemSpawnerUpgrades(game, upgrade);
					break;
				case "team":
					upgrades = upgradeStorage.findItemSpawnerUpgrades(game, game.getPlayerTeam(gPlayer));
					break;
				}

				if (upgrades != null && !upgrades.isEmpty()) {
					String what = "level";
					if (arguments.length > 3) {
						what = arguments[2];
					}
					double heighest = Double.MIN_VALUE;
					switch (what) {
					case "level":
						for (Upgrade upgrad : upgrades) {
							if (upgrad.getLevel() > heighest) {
								heighest = upgrad.getLevel();
							}
						}
						return String.valueOf(heighest);
					case "initial":
						for (Upgrade upgrad : upgrades) {
							if (upgrad.getInitialLevel() > heighest) {
								heighest = upgrad.getInitialLevel();
							}
						}
						return String.valueOf(heighest);
					}
				}
			}
			return "";
		});

		loadNewShop("default", null, true);
	}

	public void show(Player player, GameStore store) {
		try {
			boolean parent = true;
			String file = null;
			if (store != null) {
				parent = store.getUseParent();
				file = store.getShopFile();
			}
			if (file != null) {
				if (file.endsWith(".yml")) {
					file = file.substring(0, file.length() - 4);
				}
				String name = (parent ? "+" : "-") + file;
				if (!shopMap.containsKey(name)) {
					if (/*Main.getConfigurator().config.getBoolean("turnOnExperimentalGroovyShop", false) &&*/ new File(Main.getInstance().getDataFolder(), file + ".groovy").exists()) {
						loadNewShop(name, file + ".groovy", parent);
					} else {
						loadNewShop(name, file + ".yml", parent);
					}
				}
				SimpleInventories shop = shopMap.get(name);
				shop.openForPlayer(player);
			} else {
				shopMap.get("default").openForPlayer(player);
			}
		} catch (Throwable ignored) {
			player.sendMessage(i18nonly("prefix") + " Your shop.yml is invalid! Check it out or contact us on Discord.");
		}
	}

	@EventHandler
	public void onGeneratingItem(GenerateItemEvent event) {
		if (!shopMap.containsValue(event.getFormat())) {
			return;
		}

		PlayerItemInfo item = event.getInfo();
		Player player = event.getPlayer();
		Game game = Main.getPlayerGameProfile(player).getGame();
		MapReader reader = item.getReader();
		if (reader.containsKey("price") && reader.containsKey("price-type")) {
			int price = reader.getInt("price");
			ItemSpawnerType type = Main.getSpawnerType((reader.getString("price-type")).toLowerCase());
			if (type == null) {
				return;
			}

			boolean enabled = Main.getConfigurator().config.getBoolean("lore.generate-automatically", true);
			enabled = reader.getBoolean("generate-lore", enabled);

			List<String> loreText = reader.getStringList("generated-lore-text",
				Main.getConfigurator().config.getStringList("lore.text"));

			if (enabled) {
				ItemStack stack = event.getStack();
				ItemMeta stackMeta = stack.getItemMeta();
				List<String> lore = new ArrayList<>();
				if (stackMeta.hasLore()) {
					lore = stackMeta.getLore();
				}
				for (String s : loreText) {
					s = s.replaceAll("%price%", Integer.toString(price));
					s = s.replaceAll("%resource%", type.getItemName());
					s = s.replaceAll("%amount%", Integer.toString(stack.getAmount()));
					lore.add(s);
				}
				stackMeta.setLore(lore);
				stack.setItemMeta(stackMeta);
				event.setStack(stack);
			}
			if (item.hasProperties()) {
				for (ItemProperty property : item.getProperties()) {
					if (property.hasName()) {
						ItemStack newItem = event.getStack();
						BedwarsApplyPropertyToDisplayedItem applyEvent = new BedwarsApplyPropertyToDisplayedItem(game,
							player, newItem, property.getReader(player).convertToMap());
						Main.getInstance().getServer().getPluginManager().callEvent(applyEvent);

						event.setStack(newItem);
					}
				}
			}
		}

	}

	@EventHandler
	public void onPreAction(PreActionEvent event) {
		if (!shopMap.containsValue(event.getFormat()) || event.isCancelled()) {
			return;
		}

		if (!Main.isPlayerInGame(event.getPlayer())) {
			event.setCancelled(true);
		}

		if (Main.getPlayerGameProfile(event.getPlayer()).isSpectator) {
			event.setCancelled(true);
		}
	}

	@EventHandler
	public void onShopTransaction(ShopTransactionEvent event) {
		if (!shopMap.containsValue(event.getFormat()) || event.isCancelled()) {
			return;
		}
		Game game = Main.getPlayerGameProfile(event.getPlayer()).getGame();

		MapReader reader = event.getItem().getReader();
		if (reader.containsKey("upgrade")) {
			handleUpgrade(event);
		} else {
			handleBuy(event);
		}
	}

	@EventHandler
	public void onApplyPropertyToBoughtItem(BedwarsApplyPropertyToItem event) {
		if (event.getPropertyName().equalsIgnoreCase("applycolorbyteam")
			|| event.getPropertyName().equalsIgnoreCase("transform::applycolorbyteam")) {
			Player player = event.getPlayer();
			CurrentTeam team = (CurrentTeam) event.getGame().getTeamOfPlayer(player);

			if (Main.getConfigurator().config.getBoolean("automatic-coloring-in-shop")) {
				event.setStack(Main.applyColor(team.teamInfo.color, event.getStack()));
			}
		}
	}

	private void loadNewShop(String name, String fileName, boolean useParent) {
		SimpleInventories format = new SimpleInventories(options);
		try {
			if (useParent) {
				String shopFileName = "shop.yml";
				if (Main.getConfigurator().config.getBoolean("turnOnExperimentalGroovyShop", false)) {
					shopFileName = "shop.groovy";
				}
				format.loadFromDataFolder(Main.getInstance().getDataFolder(), shopFileName);
			}
			if (fileName != null) {
				format.loadFromDataFolder(Main.getInstance().getDataFolder(), fileName);
			}
		} catch (Exception ignored) {
			Debug.warn("Wrong shop.yml configuration!", true);
			Debug.warn("Your villagers won't work, check validity of your YAML!", true);
		}

		format.generateData();
		shopMap.put(name, format);
	}

	private static String getNameOrCustomNameOfItem(ItemStack stack) {
		try {
			if (stack.hasItemMeta()) {
				ItemMeta meta = stack.getItemMeta();
				if (meta == null) {
					return "";
				}

				if (meta.hasDisplayName()) {
					return meta.getDisplayName();
				}
				if (meta.hasLocalizedName()) {
					return meta.getLocalizedName();
				}
			}
		} catch (Throwable ignored) {
		}

		String normalItemName = stack.getType().name().replace("_", " ").toLowerCase();
		String[] sArray = normalItemName.split(" ");
		StringBuilder stringBuilder = new StringBuilder();

		for (String s : sArray) {
			stringBuilder.append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).append(" ");
		}
		return stringBuilder.toString().trim();
	}

	private void handleBuy(ShopTransactionEvent event) {
		Player player = event.getPlayer();
		Game game = Main.getPlayerGameProfile(event.getPlayer()).getGame();
		ClickType clickType = event.getClickType();
		MapReader mapReader = event.getItem().getReader();
		String priceType = event.getType().toLowerCase();
		ItemSpawnerType type = Main.getSpawnerType(priceType);
		ItemStack newItem = event.getStack();

		int amount = newItem.getAmount();
		int price = event.getPrice();
		int inInventory = 0;

		if (mapReader.containsKey("currency-changer")) {
			String changeItemToName = mapReader.getString("currency-changer");
			ItemSpawnerType changeItemType;
			if (changeItemToName == null) {
				return;
			}

			changeItemType = Main.getSpawnerType(changeItemToName);
			if (changeItemType == null) {
				return;
			}

			newItem = changeItemType.getStack();
		}

		if (clickType.isShiftClick()) {
			double priceOfOne = (double) price / amount;
			double maxStackSize;
			int finalStackSize;

			for (ItemStack itemStack : event.getPlayer().getInventory().getStorageContents()) {
				if (itemStack != null && itemStack.isSimilar(type.getStack())) {
					inInventory = inInventory + itemStack.getAmount();
				}
			}
			if (Main.getConfigurator().config.getBoolean("sell-max-64-per-click-in-shop")) {
				maxStackSize = Math.min(inInventory / priceOfOne, 64);
			} else {
				maxStackSize = inInventory / priceOfOne;
			}

			finalStackSize = (int) maxStackSize;
			if (finalStackSize > amount) {
				price = (int) (priceOfOne * finalStackSize);
				newItem.setAmount(finalStackSize);
				amount = finalStackSize;
			}
		}

		ItemStack materialItem = type.getStack(price);
		if (event.hasPlayerInInventory(materialItem)) {
			if (event.hasProperties()) {
				for (ItemProperty property : event.getProperties()) {
					if (property.hasName()) {
						BedwarsApplyPropertyToBoughtItem applyEvent = new BedwarsApplyPropertyToBoughtItem(game, player,
							newItem, property.getReader(player).convertToMap());
						Main.getInstance().getServer().getPluginManager().callEvent(applyEvent);

						newItem = applyEvent.getStack();
					}
				}
			}

			event.sellStack(materialItem);
			event.buyStack(newItem);

			if (!Main.getConfigurator().config.getBoolean("removePurchaseMessages", false)) {
				player.sendMessage(i18n("buy_succes").replace("%item%", amount + "x " + getNameOrCustomNameOfItem(newItem))
						.replace("%material%", price + " " + type.getItemName()));
			}
			Sounds.playSound(player, player.getLocation(),
				Main.getConfigurator().config.getString("sounds.on_item_buy"), Sounds.ENTITY_ITEM_PICKUP, 1, 1);
		} else {
			if (!Main.getConfigurator().config.getBoolean("removePurchaseMessages", false)) {
				player.sendMessage(i18n("buy_failed").replace("%item%", amount + "x " + getNameOrCustomNameOfItem(newItem))
						.replace("%material%", price + " " + type.getItemName()));
			}
		}
	}

	private void handleUpgrade(ShopTransactionEvent event) {
		Player player = event.getPlayer();
		Game game = Main.getPlayerGameProfile(event.getPlayer()).getGame();
		MapReader mapReader = event.getItem().getReader();
		String priceType = event.getType().toLowerCase();
		ItemSpawnerType itemSpawnerType = Main.getSpawnerType(priceType);

		MapReader upgradeMapReader = mapReader.getMap("upgrade");
		List<MapReader> entities = upgradeMapReader.getMapList("entities");
		String itemName = upgradeMapReader.getString("shop-name", "UPGRADE");

		int price = event.getPrice();
		boolean sendToAll = false;
		boolean isUpgrade = true;
		ItemStack materialItem = itemSpawnerType.getStack(price);

		if (event.hasPlayerInInventory(materialItem)) {
			event.sellStack(materialItem);
			for (MapReader mapEntity : entities) {
				String configuredType = mapEntity.getString("type");
				if (configuredType == null) {
					return;
				}

				UpgradeStorage upgradeStorage = UpgradeRegistry.getUpgrade(configuredType);
				if (upgradeStorage != null) {

					// TODO: Learn SimpleGuiFormat upgrades pre-parsing and automatic renaming old
					// variables
					Team team = game.getTeamOfPlayer(event.getPlayer());
					double addLevels = mapEntity.getDouble("add-levels",
						mapEntity.getDouble("levels", 0) /* Old configuration */);
					/* You shouldn't use it in entities */
					if (mapEntity.containsKey("shop-name")) {
						itemName = mapEntity.getString("shop-name");
					}
					sendToAll = mapEntity.getBoolean("notify-team", false);

					List<Upgrade> upgrades = new ArrayList<>();

					if (mapEntity.containsKey("spawner-name")) {
						String customName = mapEntity.getString("spawner-name");
						upgrades = upgradeStorage.findItemSpawnerUpgrades(game, customName);
					} else if (mapEntity.containsKey("spawner-type")) {
						String mapSpawnerType = mapEntity.getString("spawner-type");
						ItemSpawnerType spawnerType = Main.getSpawnerType(mapSpawnerType);

						upgrades = upgradeStorage.findItemSpawnerUpgrades(game, team, spawnerType);
					} else if (mapEntity.containsKey("team-upgrade")) {
						boolean upgradeAllSpawnersInTeam = mapEntity.getBoolean("team-upgrade");

						if (upgradeAllSpawnersInTeam) {
							upgrades = upgradeStorage.findItemSpawnerUpgrades(game, team);
						}

					} else if (mapEntity.containsKey("customName")) { // Old configuration
						String customName = mapEntity.getString("customName");
						upgrades = upgradeStorage.findItemSpawnerUpgrades(game, customName);
					} else {
						isUpgrade = false;
						Debugger.warn("[BedWars]> Upgrade configuration is invalid.");
					}

					if (isUpgrade) {
						BedwarsUpgradeBoughtEvent bedwarsUpgradeBoughtEvent = new BedwarsUpgradeBoughtEvent(game,
							upgradeStorage, upgrades, player, addLevels);
						Bukkit.getPluginManager().callEvent(bedwarsUpgradeBoughtEvent);

						if (bedwarsUpgradeBoughtEvent.isCancelled()) {
							continue;
						}

						if (upgrades.isEmpty()) {
							continue;
						}

						for (Upgrade upgrade : upgrades) {
							BedwarsUpgradeImprovedEvent improvedEvent = new BedwarsUpgradeImprovedEvent(game,
								upgradeStorage, upgrade, upgrade.getLevel(), upgrade.getLevel() + addLevels);
							Bukkit.getPluginManager().callEvent(improvedEvent);
						}
					}
				}

				if (sendToAll) {
					for (Player player1 : game.getTeamOfPlayer(event.getPlayer()).getConnectedPlayers()) {
						if (!Main.getConfigurator().config.getBoolean("removePurchaseMessages", false)) {
							player1.sendMessage(i18n("buy_succes").replace("%item%", itemName).replace("%material%",
									price + " " + itemSpawnerType.getItemName()));
						}
						Sounds.playSound(player1, player1.getLocation(),
							Main.getConfigurator().config.getString("sounds.on_upgrade_buy"),
							Sounds.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
					}
				} else {
					if (!Main.getConfigurator().config.getBoolean("removePurchaseMessages", false)) {
						player.sendMessage(i18n("buy_succes").replace("%item%", itemName).replace("%material%",
								price + " " + itemSpawnerType.getItemName()));
					}
					Sounds.playSound(player, player.getLocation(),
						Main.getConfigurator().config.getString("sounds.on_upgrade_buy"),
						Sounds.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1);
				}
			}
		} else {
			if (!Main.getConfigurator().config.getBoolean("removePurchaseMessages", false)) {
				player.sendMessage(i18n("buy_failed").replace("%item%", "UPGRADE").replace("%material%",
						price + " " + itemSpawnerType.getItemName()));
			}
		}
	}
}