package top.r3944realms.lib39.core.command;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraftforge.common.MinecraftForge;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.lib39.api.event.RegisterCommandHelpEvent;
import top.r3944realms.lib39.base.datagen.value.Lib39LangKey;

import javax.annotation.Nullable;

/**
 * The interface Help command.
 */
public interface IHelpCommand {
    /**
     * Should show toggle failed boolean.
     *
     * @return the boolean
     */
    default boolean shouldShowToggleFailed() {
        return false;
    }

    /**
     * Gets help head.
     *
     * @return the help head
     */
    @Nullable
    default LiteralArgumentBuilder<CommandSourceStack> getHelpHead() {
        return null;
    }

    /**
     * Gets command help manager.
     *
     * @return the command help manager
     */
    ICommandHelpManager getCommandHelpManager();

    /**
     * Build command literal argument builder.
     *
     * @param dispatcher the dispatcher
     * @param context    the context
     * @return the literal argument builder
     */
    default LiteralArgumentBuilder<CommandSourceStack> buildCommand(CommandDispatcher<CommandSourceStack> dispatcher, CommandBuildContext context) {
        LiteralArgumentBuilder<CommandSourceStack> head = getHelpHead();
        if (head == null) {
            head = LiteralArgumentBuilder.literal(getCommandHelpManager().getID().getNamespace());
        }
        LiteralArgumentBuilder<CommandSourceStack> tree = head.requires(this::requestPermission)
                .then(Commands.literal("help").executes(this::handleHelp)
                        .then(Commands.literal("toggle")
                                .then(Commands.argument("hash", IntegerArgumentType.integer()).executes(this::handleHelpToggle))
                        ));
        RegisterCommandHelpEvent registerHelpCommandEvent = new RegisterCommandHelpEvent(tree, getCommandHelpManager(), context);
        MinecraftForge.EVENT_BUS.post(registerHelpCommandEvent);
        dispatcher.register(head);
        return head;
    }

    /**
     * Request permission boolean.
     *
     * @param context the context
     * @return the boolean
     */
    default boolean requestPermission(CommandSourceStack context) {
        return true;
    }

    /**
     * Handle help int.
     *
     * @param context the context
     * @return the int
     */
    default int handleHelp(@NotNull CommandContext<CommandSourceStack> context) {
        ICommandHelpManager commandHelpManager = getCommandHelpManager();
        MutableComponent helpMessage = commandHelpManager.buildCommandTreeHelp();
        sendSuccess(context.getSource(), helpMessage);
        return 1;
    }

    /**
     * Handle help toggle int.
     *
     * @param context the context
     * @return the int
     */
    default int handleHelpToggle(@NotNull CommandContext<CommandSourceStack> context) {
        int hash = IntegerArgumentType.getInteger(context, "hash");
        ICommandHelpManager commandHelpManager = getCommandHelpManager();
        boolean success = commandHelpManager.toggleNodeExpanded(hash);
        if (success) {
            MutableComponent helpMessage = Component.literal("\n".repeat(2)).append(commandHelpManager.buildCommandTreeHelp());
            sendSuccess(context.getSource(), helpMessage);
        } else if (shouldShowToggleFailed()) {
            sendFailure(context.getSource(), Component.translatable(Lib39LangKey.Message.HELP_TOGGLE_FAILED.getKey()));
        }
        return 1;
    }

    /**
     * Send success.
     *
     * @param source the source
     * @param key    the key
     */
    static void sendSuccess(@NotNull CommandSourceStack source, Component key) {
        source.sendSuccess(() -> key, true);
    }

    /**
     * Send failure.
     *
     * @param source the source
     * @param key    the key
     */
    static void sendFailure(@NotNull CommandSourceStack source, Component key) {
        source.sendFailure(key);
    }
}
