package net.apunch.blacksmith;

import net.apunch.blacksmith.config.Settings;
import net.citizensnpcs.api.npc.NPC;
import org.bukkit.NamespacedKey;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.Damageable;
import org.bukkit.inventory.meta.ItemMeta;

import java.util.Calendar;
import java.util.Objects;
import java.util.Random;

/**
 * A representation of the session between a player and a blacksmith
 */
public class ReforgeSession implements Runnable {

    private final BlacksmithTrait blacksmithTrait;
    private final Player player;
    private final NPC npc;
    private final ItemStack itemToReforge;
    private int taskId;
    private final Settings config;
    private static final String[] enchantments = new String[Enchantment.values().length];

    /**
     * Instantiates a new session
     *
     * @param blacksmithTrait <p>A reference to the blacksmith trait</p>
     * @param player          <p>The player initiating the session</p>
     * @param npc             <p>The Blacksmith NPC involved in the session</p>
     * @param config          <p>The config to use for the session</p>
     */
    ReforgeSession(BlacksmithTrait blacksmithTrait, Player player, NPC npc, Settings config) {
        this.blacksmithTrait = blacksmithTrait;
        this.player = player;
        this.npc = npc;
        itemToReforge = player.getInventory().getItemInMainHand();
        this.config = config;

        int i = 0;
        for (Enchantment enchantment : Enchantment.values()) {
            enchantments[i++] = enchantment.getKey().asString();
        }
    }

    @Override
    public void run() {
        player.sendMessage(reforgeItemInHand() ? config.getSuccessMessage() : config.getFailMessage());
        if (npc.getEntity() instanceof Player) {
            ((Player) npc.getEntity()).getInventory().setItemInMainHand(null);
        } else {
            Objects.requireNonNull(((LivingEntity) npc.getEntity()).getEquipment()).setItemInMainHand(null);
        }
        if (!config.getDisableDelay()) {
            if (config.getDropItem()) {
                player.getWorld().dropItemNaturally(npc.getEntity().getLocation(), itemToReforge);
            } else {
                player.getInventory().addItem(itemToReforge);
            }
        } else {
            player.getInventory().setItemInMainHand(itemToReforge);
        }
        blacksmithTrait.unsetSession();
        // Start cool down
        Calendar wait = Calendar.getInstance();
        wait.add(Calendar.SECOND, config.getReforgeCoolDown());
        blacksmithTrait.addCoolDown(player.getUniqueId(), wait);
    }

    private boolean reforgeItemInHand() {
        Random random = new Random();
        if (random.nextInt(100) < config.getFailChance()) {
            for (Enchantment enchantment : itemToReforge.getEnchantments().keySet()) {
                // Remove or downgrade enchantments
                if (random.nextBoolean()) {
                    itemToReforge.removeEnchantment(enchantment);
                } else {
                    if (itemToReforge.getEnchantmentLevel(enchantment) > 1) {
                        itemToReforge.removeEnchantment(enchantment);
                        itemToReforge.addEnchantment(enchantment, 1);
                    }
                }
            }
            // Damage the item
            short reforgeDurability = BlacksmithPlugin.getDurability(itemToReforge);
            short durability = (short) (reforgeDurability + reforgeDurability * random.nextInt(8));
            short maxDurability = itemToReforge.getType().getMaxDurability();
            if (durability <= 0) {
                durability = (short) (maxDurability / 3);
            } else if (reforgeDurability + durability > maxDurability) {
                durability = (short) (maxDurability - random.nextInt(maxDurability - 25));
            }
            updateDamage(itemToReforge, maxDurability - durability);
            return false;
        } else {
            updateDamage(itemToReforge, 0);

            // Add random enchantments
            int roll = random.nextInt(100);
            if (roll < config.getExtraEnchantmentChance() && itemToReforge.getEnchantments().keySet().size() < config.getMaxEnchantments()) {
                Enchantment enchantment = Enchantment.getByKey(NamespacedKey.fromString(enchantments[random.nextInt(enchantments.length)]));
                if (Objects.requireNonNull(enchantment).canEnchantItem(itemToReforge)) {
                    int randomBound = enchantment.getMaxLevel() - enchantment.getStartLevel();
                    //A workaround for the random method's bound sometimes being negative
                    if (randomBound >= 0) {
                        itemToReforge.addEnchantment(enchantment, random.nextInt(randomBound) +
                                enchantment.getStartLevel());
                    }
                }

            }
            return true;
        }
    }

    private void updateDamage(ItemStack item, int newDamage) {
        ItemMeta meta = item.getItemMeta();
        Damageable damageable = (Damageable) meta;
        damageable.setDamage(newDamage);
        item.setItemMeta(meta);
    }

    // Return if the session should end
    boolean endSession() {
        // Prevent player from switching items during session
        ItemStack itemInHand = player.getInventory().getItemInMainHand();
        if (!itemToReforge.equals(itemInHand)) {
            player.sendMessage(config.getItemChangedMessage());
            return true;
        }
        if (!BlacksmithPlugin.getInstance().doesPlayerHaveEnough(player)) {
            player.sendMessage(config.getInsufficientFundsMessage());
            return true;
        }
        return false;
    }

    boolean isRunning() {
        return BlacksmithPlugin.getInstance().getServer().getScheduler().isQueued(taskId);
    }

    boolean isInSession(Player other) {
        return player.getName().equals(other.getName());
    }

    void beginReforge() {
        if (!config.getDisableCoolDown()) {
            taskId = BlacksmithPlugin.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(
                    BlacksmithPlugin.getInstance(), this, (new Random().nextInt(config.getMaxReforgeDelay()) +
                            config.getMinReforgeDelay()) * 20L);
        } else {
            taskId = BlacksmithPlugin.getInstance().getServer().getScheduler().scheduleSyncDelayedTask(
                    BlacksmithPlugin.getInstance(), this, 0);
        }
    }

    public Player getPlayer() {
        return player;
    }

}
