package net.knarcraft.blacksmith.manager;

import net.knarcraft.blacksmith.BlacksmithPlugin;
import net.knarcraft.blacksmith.config.GlobalSettings;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicesManager;

import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A class which deals with everything economy
 */
public class EconomyManager {

    private static Economy economy;

    private EconomyManager() {

    }

    /**
     * Sets up Vault economy support
     *
     * @param servicesManager <p>The services manager to use for finding a Vault provider</p>
     * @param logger          <p>The logger to use for logging</p>
     * @return <p>True if Vault was successfully set up</p>
     */
    public static boolean setUp(ServicesManager servicesManager, Logger logger) {
        //If already set up, there is nothing to do
        if (economy != null) {
            return true;
        }
        return setupVault(servicesManager, logger);
    }

    /**
     * Gets whether the given player can pay for re-forging their held item
     *
     * @param player <p>The player holding an item</p>
     * @return <p>Whether the player can pay for the re-forge</p>
     */
    public static boolean canPay(Player player) {
        return economy.getBalance(player) - getHeldItemCost(player) >= 0;
    }

    /**
     * Gets the human-readable cost of the given player's held item
     *
     * @param player <p>The player holding an item</p>
     * @return <p>The formatted cost</p>
     */
    public static String formatCost(Player player) {
        double cost = getHeldItemCost(player);
        return economy.format(cost);
    }

    /**
     * Withdraws the re-forge cost from the given player
     *
     * <p>The cost is automatically calculated from the item in the player's main hand.</p>
     *
     * @param player <p>The player to withdraw from</p>
     */
    public static void withdraw(Player player) {
        economy.withdrawPlayer(player, getHeldItemCost(player));
    }

    /**
     * Gets the current durability of the given item
     *
     * @param itemStack <p>The item to get the durability of</p>
     * @return <p>The durability of the item</p>
     */
    public static short getDurability(ItemStack itemStack) {
        Damageable damageable = (Damageable) itemStack.getItemMeta();
        int maxDurability = itemStack.getType().getMaxDurability();
        if (damageable != null) {
            return (short) (maxDurability - damageable.getDamage());
        } else {
            return (short) maxDurability;
        }
    }

    /**
     * Gets the damage done to the given item
     *
     * @param itemStack <p>The damage done to the item</p>
     * @return <p>The damage done to the item</p>
     */
    public static short getDamage(ItemStack itemStack) {
        Damageable damageable = (Damageable) itemStack.getItemMeta();
        if (damageable != null) {
            return (short) damageable.getDamage();
        } else {
            return 0;
        }
    }

    /**
     * Gets the cost of the item in the given player's main hand
     *
     * @param player <p>The player to calculate the cost for</p>
     * @return <p>The calculated cost</p>
     */
    private static double getHeldItemCost(Player player) {
        return getCost(player.getInventory().getItemInMainHand());
    }

    /**
     * Gets the cost of repairing the given item
     *
     * @param item <p>The item to be repaired</p>
     * @return <p>The cost of the repair</p>
     */
    private static double getCost(ItemStack item) {
        GlobalSettings globalSettings = BlacksmithPlugin.getInstance().getSettings();
        Material material = item.getType();
        //Calculate the base price
        double price = globalSettings.getBasePrice(material);

        // Adjust price based on durability
        double pricePerDurabilityPoint = globalSettings.getPricePerDurabilityPoint(material);
        if (globalSettings.getUseNaturalCost()) {
            //Cost increases with damage
            price += ((double) getDamage(item)) * pricePerDurabilityPoint;
        } else {
            //Cost decreases with damage
            price += ((double) getDurability(item)) * pricePerDurabilityPoint;
        }

        //Increase price for any enchantments
        price += getEnchantmentCost(item);
        return price;
    }

    /**
     * Gets the cost resulting from all enchantments on the given item
     *
     * @param item <p>The item to calculate enchantment cost for</p>
     * @return <p>The resulting enchantment cost</p>
     */
    private static double getEnchantmentCost(ItemStack item) {
        GlobalSettings settings = BlacksmithPlugin.getInstance().getSettings();
        double price = 0;
        for (Enchantment enchantment : item.getEnchantments().keySet()) {
            price += settings.getEnchantmentCost(enchantment) * item.getEnchantmentLevel(enchantment);
        }
        return price;
    }

    /**
     * Sets up Vault for economy
     *
     * @param servicesManager <p>The services manager to use for finding a Vault provider</p>
     * @param logger          <p>The logger to use for logging</p>
     * @return <p>True if Vault was successfully set up</p>
     */
    private static boolean setupVault(ServicesManager servicesManager, Logger logger) {
        // Setup Vault
        RegisteredServiceProvider<Economy> economyProvider = servicesManager.getRegistration(Economy.class);
        if (economyProvider != null) {
            economy = economyProvider.getProvider();
            return true;
        } else {
            // Disable if no economy plugin was found
            logger.log(Level.SEVERE, "Failed to load an economy plugin. Disabling...");
            return false;
        }
    }

}
