package net.minecraft.server.commands;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import java.util.Collection;
import java.util.Collections;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.commands.arguments.GameModeArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.GameType;

public class GameModeCommand {
    public static final int PERMISSION_LEVEL = 2;

    public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
        dispatcher.register(
            Commands.literal("gamemode")
                .requires(Commands.hasPermission(2))
                .then(
                    Commands.argument("gamemode", GameModeArgument.gameMode())
                        .executes(
                            commandContext -> setMode(
                                commandContext,
                                Collections.singleton(commandContext.getSource().getPlayerOrException()),
                                GameModeArgument.getGameMode(commandContext, "gamemode")
                            )
                        )
                        .then(
                            Commands.argument("target", EntityArgument.players())
                                .executes(
                                    context -> setMode(context, EntityArgument.getPlayers(context, "target"), GameModeArgument.getGameMode(context, "gamemode"))
                                )
                        )
                )
        );
    }

    private static void logGamemodeChange(CommandSourceStack source, ServerPlayer player, GameType gameType) {
        Component component = Component.translatable("gameMode." + gameType.getName());
        if (source.getEntity() == player) {
            source.sendSuccess(() -> Component.translatable("commands.gamemode.success.self", component), true);
        } else {
            if (source.getLevel().getGameRules().getBoolean(GameRules.RULE_SENDCOMMANDFEEDBACK)) {
                player.sendSystemMessage(Component.translatable("gameMode.changed", component));
            }

            source.sendSuccess(() -> Component.translatable("commands.gamemode.success.other", player.getDisplayName(), component), true);
        }
    }

    private static int setMode(CommandContext<CommandSourceStack> source, Collection<ServerPlayer> players, GameType gameType) {
        int i = 0;

        for (ServerPlayer serverPlayer : players) {
            serverPlayer.getBukkitEntity().taskScheduler.schedule((ServerPlayer nmsEntity) -> { // Folia - region threading
            if (setGameMode(source.getSource(), nmsEntity, gameType)) { // Folia - region threading
                //i++; // Folia - region threading
            }
            }, null, 1L); // Folia - region threading
        }

        return i;
    }

    public static void setGameMode(ServerPlayer player, GameType gameMode) {
        setGameMode(player.createCommandSourceStack(), player, gameMode);
    }

    private static boolean setGameMode(CommandSourceStack source, ServerPlayer player, GameType gameMode) {
        // Paper start
        org.bukkit.event.player.PlayerGameModeChangeEvent event = player.setGameMode(gameMode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.COMMAND, net.kyori.adventure.text.Component.empty());
        if (event != null && !event.isCancelled()) {
            // Paper end
            logGamemodeChange(source, player, gameMode);
            return true;
        } else if (event != null && event.cancelMessage() != null) {
            source.sendSuccess(() -> io.papermc.paper.adventure.PaperAdventure.asVanilla(event.cancelMessage()), true);
            return false;
            // Paper end - Expand PlayerGameModeChangeEvent
        } else {
            return false;
        }
    }
}
