package net.knarcraft.blacksmith.config.scrapper;

import net.knarcraft.blacksmith.config.Setting;
import net.knarcraft.blacksmith.config.SettingValueType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

/**
 * An enum representing all scrapper-related settings
 */
public enum ScrapperSetting implements Setting {

    /**
     * The setting for whether the NPC should drop an item to the ground when finished
     *
     * <p>If set to false, the item will be directly put in the player's inventory instead</p>
     */
    DROP_ITEM("dropItem", SettingValueType.BOOLEAN, true, "Whether the " +
            "item will drop materials resulting from scrapping on the ground, instead of putting them into the user's" +
            " inventory", true, false),

    /**
     * The chance of a scrapper returning no salvage, regardless of item condition
     */
    FAIL_SALVAGE_CHANCE("failSalvageChance", SettingValueType.POSITIVE_DOUBLE, 0,
            "The chance to fail a salvage, thus destroying the item (0-100)",
            true, false),

    /**
     * The setting for which items a scrapper is able to salvage
     */
    SALVAGE_ABLE_ITEMS("salvageAbleItems", SettingValueType.REFORGE_ABLE_ITEMS, "",
            "The items a blacksmith is able to salvage. Setting this only " +
                    "allows NPCs to repair the listed items. This should be set for each individual NPC",
            true, false),

    /**
     * The maximum amount of seconds a player may need to wait for the reforging to finish
     */
    MAX_SALVAGE_DELAY("maxSalvageWaitTimeSeconds", SettingValueType.POSITIVE_INTEGER, 30,
            "The maximum time for a salvaging to finish",
            true, false),

    /**
     * The minimum amount of seconds a player may need to wait for the reforging to finish
     */
    MIN_SALVAGE_DELAY("minSalvageWaitTimeSeconds", SettingValueType.POSITIVE_INTEGER, 5,
            "The minimum time for a salvaging to finish",
            true, false),

    /**
     * The setting for number of seconds a player has to wait between each usage of the blacksmith
     */
    SALVAGE_COOL_DOWN("salvageCoolDownSeconds", SettingValueType.POSITIVE_INTEGER, 60,
            "The cool-down period between each salvage",
            true, false),

    /**
     * The setting for the title used to display which kind of scrapper the NPC is
     *
     * <p>While this should be entirely configurable, values such as armor-scrapper, sword-scrapper and similar, which
     * describe the scrapper's specialization, and thus the range of salvageable items, is expected.</p>
     */
    SCRAPPER_TITLE("scrapperTitle", SettingValueType.STRING, "scrapper",
            "The title describing the scrapper's usage/speciality", true, false),

    /**
     * The setting for whether the NPC should allow salvaging of any craft-able item instead of just repairable items
     */
    EXTENDED_SALVAGE_ENABLED("extendedSalvageEnabled", SettingValueType.BOOLEAN, false,
            "Whether to enable salvaging of non-repairable items, such as planks", true, false),
    
    /*-----------
     | Messages |
     -----------*/

    /**
     * The message displayed when the scrapper is busy with another player
     */
    BUSY_WITH_PLAYER_MESSAGE("busyPlayerMessage", SettingValueType.STRING, "&cI'm busy at the moment. Come " +
            "back later!", "The message to display when another player is using the scrapper",
            true, true),

    /**
     * The message displayed when the scrapper is busy salvaging the player's item
     */
    BUSY_WITH_SALVAGE_MESSAGE("busySalvageMessage", SettingValueType.STRING, "&cI'm working on it. Be " +
            "patient! I'll finish {time}!", "The message to display when the blacksmith is working on the " +
            "salvaging", true, true),

    /**
     * The message displayed if the player needs to wait for the cool-down to expire
     */
    COOL_DOWN_UNEXPIRED_MESSAGE("coolDownUnexpiredMessage", SettingValueType.STRING,
            "&cYou've already had your chance! Give me a break! I'll be ready {time}!",
            "The message to display when the blacksmith is still " +
                    "on a cool-down from the previous re-forging", true, true),

    /**
     * The message displayed if the scrapper encounters an item they cannot salvage
     */
    INVALID_ITEM_MESSAGE("invalidItemMessage", SettingValueType.STRING,
            "&cI'm sorry, but I'm a/an {title}, I don't know how to salvage that!",
            "The message to display if the player tries to salvage" +
                    " an item the scrapper cannot salvage", true, true),

    /**
     * The message displayed if salvaging an item would return in no items
     */
    TOO_DAMAGED_FOR_SALVAGE_MESSAGE("tooDamagedForSalvageMessage", SettingValueType.STRING,
            "&cThat item is too damaged to be salvaged into anything useful", "The message to display " +
            "if salvaging the player's item would result in no salvage", true, true),

    /**
     * The message displayed if a salvage is successful
     */
    SUCCESS_SALVAGE_MESSAGE("successSalvagedMessage", SettingValueType.STRING, "There you go!",
            "The message to display when an item is successfully salvaged", true, true),

    /**
     * The message displayed if a salvage is unsuccessful
     */
    FAIL_SALVAGE_MESSAGE("failSalvageMessage", SettingValueType.STRING, "&cWhoops! The item broke! Maybe " +
            "next time?", "The message to display when a scrapper fails to salvage an item because of the " +
            "fail chance.", true, true),

    /**
     * The message displayed if a player presents a different item after seeing the price to reforge an item
     */
    ITEM_UNEXPECTEDLY_CHANGED_MESSAGE("itemChangedMessage", SettingValueType.STRING, "&cThat's not the item" +
            " you wanted to salvage before!", "The message to display when presenting a different item than" +
            " the one just evaluated", true, true),

    /**
     * The message displayed when the scrapper starts salvaging an item
     */
    START_SALVAGE_MESSAGE("startSalvageMessage", SettingValueType.STRING, "&eOk, let's see what I can do...",
            "The message to display once the blacksmith starts re-forging", true, true),

    /**
     * The message displayed if a player is unable to pay the blacksmith
     */
    INSUFFICIENT_FUNDS_MESSAGE("insufficientFundsMessage", SettingValueType.STRING,
            "&cYou don't have enough money to salvage an item!",
            "The message to display when a player cannot pay for the salvaging", true, true),

    /**
     * The message displayed when displaying the cost of reforging the held item to the player
     */
    COST_MESSAGE("costMessage", SettingValueType.STRING,
            "&eIt will cost &a{cost}&e to salvage that item! {yield} Click again to salvage!",
            "The message to display when informing a player about the salvaging cost", true, true),

    /**
     * The message displayed when explaining that all items will be returned as salvage
     */
    FULL_SALVAGE_MESSAGE("fullSalvageMessage", SettingValueType.STRING,
            "&aI should be able to extract all components from that pristine item.&r",
            "The message to display when explaining expected full yield as part of the cost message", true, true),

    /**
     * The message displayed when explaining that only some items will be returned as salvage
     */
    PARTIAL_SALVAGE_MESSAGE("partialSalvageMessage", SettingValueType.STRING,
            "&cI cannot extract all components from that damaged item.&r",
            "The message to display when explaining expected partial yield as part of the cost message", true, true),

    /*------------------
     | Global settings |
     ------------------*/

    /**
     * The setting for the use cost of using the scrapper
     */
    USE_COST("basePrice", SettingValueType.POSITIVE_DOUBLE, 0, "The cost of using a scrapper",
            false, false),

    /**
     * Whether to display exact time in minutes and seconds when displaying a remaining cool-down
     */
    SHOW_EXACT_TIME("showExactTime", SettingValueType.BOOLEAN, "false", "Exact time displays the " +
            "exact number of seconds and minutes remaining as part of the scrapping cool-down and scrapping delay " +
            "messages, instead of just vaguely hinting at the remaining time.", false, false),

    /**
     * Whether to give experience back when salvaging an enchanted item
     */
    GIVE_EXPERIENCE("giveExperience", SettingValueType.BOOLEAN, "true", "Whether enchanted " +
            "salvaged items should return some amount of exp upon salvage", false, false),

    /**
     * Which items are ignored when calculating salvage for a given material
     */
    IGNORED_SALVAGE("trashSalvage", SettingValueType.STRING_LIST,
            new ArrayList<>(List.of("*_SHOVEL;*_PICKAXE;*_AXE;*_HOE;*_SWORD;SHIELD;*_BOW:STICK")),
            "Items treated as trash during salvage calculation. This follows the format: " +
                    "\"MATERIAL[;MATERIAL2][;MATERIAL3]:TRASH_MATERIAL[;TRASH_MATERIAL2]\", so the material or " +
                    "materials listed will treat the material specified after the \":\" as trash when calculating " +
                    "salvage unless all non-trash items can be given as well(* matches any character).",
            false, false),
    ;

    private final String path;
    private final String childPath;
    private final Object value;
    private final String commandName;
    private final SettingValueType valueType;
    private final String description;
    private final boolean isPerNPC;
    private final boolean isMessage;

    /**
     * Instantiates a new setting
     *
     * @param key         <p>The configuration key for this setting</p>
     * @param valueType   <p>The type of value used by this setting</p>
     * @param value       <p>The default value of this setting</p>
     * @param description <p>The description describing this setting</p>
     * @param isPerNPC    <p>Whether this setting is per-NPC or global</p>
     * @param isMessage   <p>Whether this option is for an NPC message</p>
     */
    ScrapperSetting(String key, SettingValueType valueType, Object value,
                    @NotNull String description, boolean isPerNPC, boolean isMessage) {
        if (isPerNPC) {
            if (isMessage) {
                this.path = "scrapper.defaults.messages." + key;
            } else {
                this.path = "scrapper.defaults." + key;
            }
        } else {
            this.path = "scrapper.global." + key;
        }
        this.value = value;
        this.valueType = valueType;
        this.childPath = key;
        if (key.contains(".")) {
            String[] pathParts = key.split("\\.");
            this.commandName = pathParts[0];
        } else {
            this.commandName = key;
        }
        this.description = description;
        this.isPerNPC = isPerNPC;
        this.isMessage = isMessage;
    }

    @Override
    public @NotNull String getPath() {
        return this.path;
    }

    @Override
    public @NotNull String getChildPath() {
        return this.childPath;
    }

    @Override
    public @NotNull Object getDefaultValue() {
        return this.value;
    }

    @Override
    public @NotNull String getCommandName() {
        return this.commandName;
    }

    @Override
    public @NotNull SettingValueType getValueType() {
        return this.valueType;
    }

    @Override
    public @NotNull String getDescription() {
        return this.description;
    }

    @Override
    public boolean isPerNPC() {
        return this.isPerNPC;
    }

    @Override
    public boolean isMessage() {
        return this.isMessage;
    }

    /**
     * Gets the scrapper setting specified by the input string
     *
     * @param input <p>The input to check</p>
     * @return <p>The matching scrapper setting, or null if not found</p>
     */
    public static @Nullable ScrapperSetting getSetting(@NotNull String input) {
        for (ScrapperSetting scrapperSetting : ScrapperSetting.values()) {
            if (input.equalsIgnoreCase(scrapperSetting.getCommandName())) {
                return scrapperSetting;
            }
        }
        return null;
    }

}