package org.allaymc.api.command.tree;

import org.allaymc.api.AllayAPI;
import org.allaymc.api.block.property.type.BlockPropertyType;
import org.allaymc.api.block.type.BlockType;
import org.allaymc.api.entity.Entity;
import org.allaymc.api.entity.effect.EffectType;
import org.allaymc.api.entity.interfaces.EntityPlayer;
import org.allaymc.api.entity.type.EntityType;
import org.allaymc.api.item.enchantment.EnchantmentType;
import org.allaymc.api.item.type.ItemType;
import org.allaymc.api.player.GameMode;
import org.allaymc.api.world.data.Difficulty;
import org.joml.Vector3fc;

import java.util.List;

/**
 * The {@code CommandNodeFactory} interface defines the structure for creating various types of
 * command nodes. Each command node can hold a specific type of data, such as strings, numbers,
 * game elements, and entities, and it defines how arguments are processed in the command execution tree.
 * <p>
 * This factory is held as an {@link AllayAPI.APIInstanceHolder} and can be accessed using {@link #getFactory()}.
 * The factory allows users to create nodes with different types, and their default values, which are
 * used in command arguments parsing.
 *
 * @author daoge_cmd
 */
public interface CommandNodeFactory {
    AllayAPI.APIInstanceHolder<CommandNodeFactory> FACTORY = AllayAPI.APIInstanceHolder.create();

    static CommandNodeFactory getFactory() {
        return FACTORY.get();
    }

    /**
     * Creates a command node for a key-based argument.
     *
     * @param key          the key name
     * @param parent       the parent node
     * @param defaultValue the default value of the key
     * @return a new {@code CommandNode} for the key
     */
    CommandNode key(String key, CommandNode parent, String defaultValue);

    /**
     * Creates a command node for a string-based argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default string value
     * @return a new {@code CommandNode} for the string argument
     */
    CommandNode str(String name, CommandNode parent, String defaultValue);

    /**
     * Creates a command node for a short number argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default short value
     * @return a new {@code CommandNode} for the short number argument
     */
    CommandNode shortNum(String name, CommandNode parent, short defaultValue);

    /**
     * Creates a command node for an integer argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default integer value
     * @return a new {@code CommandNode} for the integer argument
     */
    CommandNode intNum(String name, CommandNode parent, int defaultValue);

    /**
     * Creates a command node for a long number argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default long value
     * @return a new {@code CommandNode} for the long number argument
     */
    CommandNode longNum(String name, CommandNode parent, long defaultValue);

    /**
     * Creates a command node for a float number argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default float value
     * @return a new {@code CommandNode} for the float number argument
     */
    CommandNode floatNum(String name, CommandNode parent, float defaultValue);

    /**
     * Creates a command node for a double number argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default double value
     * @return a new {@code CommandNode} for the double number argument
     */
    CommandNode doubleNum(String name, CommandNode parent, double defaultValue);

    /**
     * Creates a command node for a boolean argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default boolean value
     * @return a new {@code CommandNode} for the boolean argument
     */
    CommandNode bool(String name, CommandNode parent, boolean defaultValue);

    /**
     * Creates a command node for an enum argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default enum value as a string
     * @param enums        the available enum options
     * @return a new {@code CommandNode} for the enum argument
     */
    CommandNode enums(String name, CommandNode parent, String defaultValue, String[] enums);

    /**
     * Creates a command node for an enum argument, allowing a custom enum name.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param enumName     the name of the enum
     * @param defaultValue the default enum value as a string
     * @param values       the available enum options
     * @return a new {@code CommandNode} for the enum argument
     */
    CommandNode enums(String name, CommandNode parent, String enumName, String defaultValue, String[] values);

    /**
     * Creates a command node for an enum argument, ignoring case.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default enum value as a string
     * @param enums        the available enum options
     * @return a new {@code CommandNode} for the case-insensitive enum argument
     */
    CommandNode enumsIgnoreCase(String name, CommandNode parent, String defaultValue, String[] enums);

    /**
     * Creates a command node for an enum argument with a custom name, ignoring case.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param enumName     the custom name of the enum
     * @param defaultValue the default enum value as a string
     * @param enums        the available enum options
     * @return a new {@code CommandNode} for the case-insensitive enum argument
     */
    CommandNode enumsIgnoreCase(String name, CommandNode parent, String enumName, String defaultValue, String[] enums);

    /**
     * Creates a command node for an enum argument to this command node based on the specified enum class.
     * Unlike {@link #enums}, the string result will then be mapped back to the type of
     * the provided enum class, which means that you don't need to map string back to
     * enum manually.
     *
     * @param name      the name of the parameter
     * @param parent    the parent node
     * @param enumClass the class of the enum type
     * @return a new {@code CommandNode} for the enum class argument
     */
    <T extends Enum<?>> CommandNode enumClass(String name, CommandNode parent, T defaultValue, Class<T> enumClass);


    /**
     * Creates a command node for an enum argument to this command node based on the specified enum class.
     * Unlike {@link #enums}, the string result will then be mapped back to the type of
     * the provided enum class, which means that you don't need to map string back to
     * enum manually.
     *
     * @param name      the name of the parameter
     * @param parent    the parent node
     * @param enumName  the custom name of the enum
     * @param enumClass the class of the enum type
     * @return a new {@code CommandNode} for the enum class argument
     */
    <T extends Enum<?>> CommandNode enumClass(String name, CommandNode parent, T defaultValue, String enumName, Class<T> enumClass);

    /**
     * Creates a command node for a message argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default message value
     * @return a new {@code CommandNode} for the message argument
     */
    CommandNode msg(String name, CommandNode parent, String defaultValue);

    /**
     * Creates a command node for the remaining arguments.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default value for the remaining arguments
     * @return a new {@code CommandNode} for the remaining arguments
     */
    CommandNode remain(String name, CommandNode parent, List<String> defaultValue);

    /**
     * Creates a command node for a target argument that allows selecting multiple entities.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default target entities
     * @return a new {@code CommandNode} for the target argument
     */
    CommandNode target(String name, CommandNode parent, List<Entity> defaultValue);

    /**
     * Creates a command node for a player target argument that allows selecting multiple player entities.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default target player entities
     * @return a new {@code CommandNode} for the player target argument
     */
    CommandNode playerTarget(String name, CommandNode parent, List<EntityPlayer> defaultValue);

    /**
     * Creates a command node for a game mode argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default game mode
     * @return a new {@code CommandNode} for the game mode argument
     */
    CommandNode gameMode(String name, CommandNode parent, GameMode defaultValue);

    /**
     * Creates a command node for a position argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default position (vector)
     * @return a new {@code CommandNode} for the position argument
     */
    CommandNode pos(String name, CommandNode parent, Vector3fc defaultValue);

    /**
     * Creates a command node for a wildcard target argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default wildcard target
     * @return a new {@code CommandNode} for the wildcard target argument
     */
    CommandNode wildcardTarget(String name, CommandNode parent, String defaultValue);

    /**
     * Creates a command node for an enchantment type argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default enchantment type
     * @return a new {@code CommandNode} for the enchantment type argument
     */
    CommandNode enchantment(String name, CommandNode parent, EnchantmentType defaultValue);

    /**
     * Creates a command node for an effect type argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default effect type
     * @return a new {@code CommandNode} for the effect type argument
     */
    CommandNode effect(String name, CommandNode parent, EffectType defaultValue);

    /**
     * Creates a command node for an item type argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default item type
     * @return a new {@code CommandNode} for the item type argument
     */
    CommandNode itemType(String name, CommandNode parent, ItemType<?> defaultValue);

    /**
     * Creates a command node for a block type argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default block type
     * @return a new {@code CommandNode} for the block type argument
     */
    CommandNode blockType(String name, CommandNode parent, BlockType<?> defaultValue);

    /**
     * Creates a command node for an entity type argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default entity type
     * @return a new {@code CommandNode} for the entity type argument
     */
    CommandNode entityType(String name, CommandNode parent, EntityType<?> defaultValue);

    /**
     * Creates a command node for a difficulty argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default difficulty level
     * @return a new {@code CommandNode} for the difficulty argument
     */
    CommandNode difficulty(String name, CommandNode parent, Difficulty defaultValue);

    /**
     * Creates a command node for block property values.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default list of block property values
     * @return a new {@code CommandNode} for block property values
     */
    CommandNode blockPropertyValues(String name, CommandNode parent, List<BlockPropertyType.BlockPropertyValue<?, ?, ?>> defaultValue);

    /**
     * Creates a command node for a command argument.
     *
     * @param name         the name of the argument
     * @param parent       the parent node
     * @param defaultValue the default command
     * @return a new {@code CommandNode} for the command argument
     */
    CommandNode cmd(String name, CommandNode parent, String defaultValue);
}
