package leigh.minecraft.link.item;

import leigh.mecha.cli.CommandFrame;
import leigh.mecha.cli.CommandHandler;
import leigh.mecha.log.MechaLogger;
import leigh.mecha.log.MechaLoggerFactory;
import leigh.mecha.util.NumberUtil;
import leigh.minecraft.link.LCMessages;
import leigh.minecraft.link.MinecraftLinkPlugin;
import leigh.minecraft.link.bukkit.BukkitCommandState;
import leigh.minecraft.link.bukkit.BukkitUtil;
import leigh.minecraft.link.faction.Faction;
import leigh.minecraft.link.faction.Reputation;
import leigh.minecraft.link.faction.ReputationLevels;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.ExperienceOrb;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerItemConsumeEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;

/**
 * This class contains routines for working with the LEIGHCO XP Bottles. The basic jist of this is that the XP
 * bottle is a potion which has custom NBT tags on it identifying it as a LEIGH&CO potion bottle, and it stores some
 * given number of points of XP. We provide an interact event handler to provide the effect when the player uses
 * the bottle. The effect spawns an experience orb for the given number of points in front of the player.
 *
 * @author Alex Leigh
 */
public class XPBottle {
    private static final MechaLogger logger = MechaLoggerFactory.getLogger(XPBottle.class);
    public static String ITEM_TYPE_KEY = "leigh.type";
    public static String XP_AMOUNT_KEY = "leigh.item.xpbottle.xp";
    public static String BOTTLE_TYPE = "leigh.item.xpbottle";
    // TODO: Set this to something proper

    private final NamespacedKey itemTypeKey;
    private final NamespacedKey xpAmountKey;
    private final CommandFrame<BukkitCommandState> xpFrame = new CommandFrame<>();

    public XPBottle(MinecraftLinkPlugin plugin) {
        itemTypeKey = new NamespacedKey(plugin, ITEM_TYPE_KEY);
        xpAmountKey = new NamespacedKey(plugin, XP_AMOUNT_KEY);

        // TODO: It would be nice if the caller could craft multiple bottles at once.

        xpFrame.putCommand("display", new CommandHandler<BukkitCommandState>() {
            @Override
            public void handle(String[] cmd, BukkitCommandState state) throws Exception {
                Player p = state.getPlayer();
                int currentExp = BukkitUtil.getPlayerExp(p);
                p.sendMessage("Current experience: " + currentExp);
            }
        });

        xpFrame.putCommand("craft", new CommandHandler<BukkitCommandState>() {
            @Override
            public void handle(String[] cmd, BukkitCommandState state) throws Exception {
                logger.info("craft called. [len: {}] [cmd: {}]", cmd.length, cmd);

                if (cmd.length != 2) {
                    state.sendMessage("Usage: lcl xp craft [xp]");
                    return;
                }

                int amt;
                try {
                    amt = Integer.parseInt(cmd[1]);
                } catch (NumberFormatException e) {
                    state.sendMessage("Usage: lcl xp craft [xp]");
                    return;
                }

                // Check - Does the player have enough rep
                Reputation rep = plugin.getReputation();
                int lcRep = rep.getReputation(state.getPlayer().getUniqueId(), Faction.ID_LEIGHCO);
                if (lcRep < ReputationLevels.LEVEL_EXALTED) {
                    state.getPlayer().sendMessage("You must be Exalted with LEIGH&CO to craft XP bottles.");
                    return;
                }

                // Check - Does the player have enough Xp
                final Player p = state.getPlayer();
                int currentXp = BukkitUtil.getPlayerExp(p);
                if (currentXp < amt) {
                    p.sendMessage("You have insufficient XP to craft the XP bottle. [remaining: " + currentXp + "]");
                    return;
                }

                // Check - Does the player have enough money
                if (!BukkitUtil.chargeMoney(p, 100)) return;

                // We are all good! Process the change.
                logger.info("give exp: {} {}", -amt, currentXp);

                p.giveExp(-amt);
                BukkitUtil.giveItem(p,
                        getXPItem(amt),
                        LCMessages.LEIGHBOY);
            }
        });
    }

    public CommandFrame<BukkitCommandState> getCommandFrame() {
        return xpFrame;
    }

    public ItemStack getXPItem(int amt) {
        ItemStack xp = new ItemStack(Material.POTION);
        ItemMeta xpItem = xp.getItemMeta();

        xpItem.getPersistentDataContainer().set(itemTypeKey, PersistentDataType.STRING, BOTTLE_TYPE);
        xpItem.getPersistentDataContainer().set(xpAmountKey, PersistentDataType.INTEGER, amt);

        assert xpItem != null;

        // TODO: Human format the number
        xpItem.setDisplayName(ChatColor.DARK_PURPLE + "XP (" + NumberUtil.humanSize(amt) + ")");
        xp.setItemMeta(xpItem);

        return xp;
    }

    /**
     * Returns <code>true</code> if this is a LEIGH&CO item of the given type.
     */
    public boolean isItemType(ItemStack item, String type) {
        String typ = item.getItemMeta().getPersistentDataContainer().get(itemTypeKey, PersistentDataType.STRING);
        if (typ != null) {
            return typ.equals(type);
        }
        return false;
    }

    /**
     * Call this on player use in case the use event is for an XP bottle. If it is, the mechanics will apply. If
     * this was a handled event, the event will be cancelled by this method.
     */
    public void playerConsumeEvent(PlayerItemConsumeEvent e) {
        logger.info("XP bottle playerUseEvent called");
        if (e.getItem().getType() == Material.POTION) {
            if (isItemType(e.getItem(), BOTTLE_TYPE)) {
                logger.info("Determined bottle was a XP bottle");
                ExperienceOrb orb = e.getPlayer().getWorld().spawn(e.getPlayer().getLocation(), ExperienceOrb.class);
                Integer amt = e.getItem().getItemMeta().getPersistentDataContainer().get(xpAmountKey,
                        PersistentDataType.INTEGER);
                if (amt == null) {
                    logger.warn("Processed event for LEIGHCO XP Bottle, but no amount was stored in the bottle.");
                    return;
                }
                orb.setExperience(amt);
                // Poof
                // It is implied they consumed it from the hand
                e.getPlayer().getInventory().setItem(EquipmentSlot.HAND, new ItemStack(Material.GLASS_BOTTLE));
                e.setCancelled(true);
            } else {
                logger.info("Determined bottle was not a LC bottle");
            }
        }
    }
}
