// 
// Decompiled by Procyon v0.6.0
// 

package com.hypixel.hytale.server.core.command.commands.utility.metacommands;

import java.util.function.Function;
import javax.annotation.Nullable;
import java.util.Iterator;
import com.hypixel.hytale.server.core.command.system.CommandOwner;
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
import java.util.Map;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import com.hypixel.hytale.server.core.command.system.CommandManager;
import java.nio.file.Path;
import java.util.List;
import com.google.gson.Gson;
import java.util.logging.Level;
import com.hypixel.hytale.logger.HytaleLogger;
import java.util.concurrent.CompletableFuture;
import com.hypixel.hytale.server.core.Message;
import java.nio.file.OpenOption;
import java.nio.file.Files;
import java.nio.file.attribute.FileAttribute;
import java.nio.file.Paths;
import com.google.gson.JsonElement;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import javax.annotation.Nonnull;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase;

public class DumpCommandsCommand extends CommandBase
{
    public DumpCommandsCommand() {
        super("dump", "server.commands.meta.dump.desc");
    }
    
    @Override
    protected void executeSync(@Nonnull final CommandContext context) {
        final JsonObject outputJson = new JsonObject();
        final Gson gson = new GsonBuilder().setPrettyPrinting().create();
        final List<CommandDef> modernDefs = this.gatherCommandDefs();
        outputJson.add("modern", gson.toJsonTree(modernDefs));
        CompletableFuture.runAsync(() -> {
            try {
                final String outputStr = gson.toJson(outputJson);
                final Path path = Paths.get("dumps/commands.dump.json", new String[0]);
                Files.createDirectories(path.getParent(), (FileAttribute<?>[])new FileAttribute[0]);
                Files.writeString(path, outputStr, new OpenOption[0]);
                context.sendMessage(Message.translation("server.commands.meta.dump.success").param("file", path.toAbsolutePath().toString()));
            }
            catch (final Throwable t) {
                throw new RuntimeException(t);
            }
        }).exceptionally(t -> {
            context.sendMessage(Message.translation("server.commands.meta.dump.error"));
            HytaleLogger.getLogger().at(Level.SEVERE).withCause(t).log("Couldn't write command dump");
            return null;
        });
    }
    
    private List<CommandDef> gatherCommandDefs() {
        final Map<String, AbstractCommand> registrations = CommandManager.get().getCommandRegistration();
        final List<CommandDef> defs = new ObjectArrayList<CommandDef>(registrations.size() * 2);
        registrations.forEach((name, command) -> this.extractCommand(command, defs));
        return defs;
    }
    
    private void extractCommand(@Nonnull final AbstractCommand command, @Nonnull final List<CommandDef> defs) {
        final String outputName = "/" + command.getFullyQualifiedName();
        final String className = command.getClass().getName();
        final String owner = this.formatNullable(command.getOwner(), CommandOwner::getName);
        final String ownerClass = this.formatNullable(command.getOwner(), o -> o.getClass().getName());
        final String permission = this.formatPermission(command.getPermission());
        final List<String> permissionGroups = command.getPermissionGroups();
        defs.add(new CommandDef(outputName, className, owner, ownerClass, permission, permissionGroups));
        for (final AbstractCommand subCommand : command.getSubCommands().values()) {
            this.extractCommand(subCommand, defs);
        }
    }
    
    private <T> String formatNullable(@Nullable final T something, final Function<T, String> func) {
        try {
            if (something == null) {
                return "NULL";
            }
            return func.apply(something);
        }
        catch (final Throwable t) {
            return "ERROR";
        }
    }
    
    private String formatPermission(@Nullable final String permission) {
        return (permission == null) ? "NULL" : permission;
    }
    
    record CommandDef(String name, String className, String owner, String ownerClass, String permission, List<String> permissionGroups) {}
}
