package land.chipmunk.chipmunkmod.command;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import land.chipmunk.chipmunkmod.commands.*;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.commands.CommandBuildContext;
import net.minecraft.network.chat.ClickEvent;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.MutableComponent;

public class CommandManager {
    public static CommandManager INSTANCE;
    public CommandDispatcher<FabricClientCommandSource> dispatcher = new CommandDispatcher<>();
    public String prefix;

    public CommandManager (final String prefix, final CommandBuildContext commandRegistryAccess) {
        this.prefix = prefix;

        TestCommand.register(this.dispatcher);
        CoreCommand.register(this.dispatcher);
        UsernameCommand.register(this.dispatcher);
        CloopCommand.register(this.dispatcher);
        ValidateCommand.register(this.dispatcher);
        ItemCommand.register(this.dispatcher, commandRegistryAccess);
        CustomChatCommand.register(this.dispatcher);
        EvalCommand.register(this.dispatcher);
        MusicCommand.register(this.dispatcher);
        RainbowNameCommand.register(this.dispatcher);
        SayCommand.register(this.dispatcher);
        AutoSkinCommand.register(this.dispatcher);
        ReloadConfigCommand.register(this.dispatcher);
        SelfCareCommand.register(this.dispatcher);
        PrefixCommand.register(this.dispatcher);
        AutoPrefixCommand.register(this.dispatcher);
    }

    public static LiteralArgumentBuilder<FabricClientCommandSource> literal (final String name) {
        return LiteralArgumentBuilder.literal(name);
    }

    public static <T> RequiredArgumentBuilder<FabricClientCommandSource, T> argument (final String name, final ArgumentType<T> type) {
        return RequiredArgumentBuilder.argument(name, type);
    }

    public void executeCommand (final String command) {
        final Minecraft client = Minecraft.getInstance();
        final ClientPacketListener networkHandler = client.getConnection();

        if (networkHandler == null) return;

        final FabricClientCommandSource commandSource = (FabricClientCommandSource) networkHandler.getSuggestionsProvider();

        try {
            dispatcher.execute(command, commandSource);
        } catch (final CommandSyntaxException e) {
            commandSource.sendError(ComponentUtils.fromMessage(e.getRawMessage()));
            final Component context = getContext(e);
            if (context != null) commandSource.sendError(context);
        } catch (final Exception e) {
            commandSource.sendError(Component.nullToEmpty(e.getMessage()));
        }
    }

    public Component getContext (final CommandSyntaxException exception) {
        final int _cursor = exception.getCursor();
        final String input = exception.getInput();

        if (input == null || _cursor < 0) {
            return null;
        }
        final MutableComponent text = Component.literal("")
                .withStyle(ChatFormatting.GRAY);
        text.setStyle(text.getStyle().withClickEvent(new ClickEvent.SuggestCommand(prefix + input)));

        final int cursor = Math.min(input.length(), _cursor);

        if (cursor > CommandSyntaxException.CONTEXT_AMOUNT) {
            text.append(Component.literal("..."));
        }

        text
                .append(Component.literal(input.substring(Math.max(0, cursor - CommandSyntaxException.CONTEXT_AMOUNT), cursor)))
                .append(Component.literal(input.substring(cursor)).withStyle(ChatFormatting.RED, ChatFormatting.UNDERLINE))
                .append(Component.translatable("command.context.here").withStyle(ChatFormatting.RED, ChatFormatting.ITALIC));

        return text;
    }
}
