package me.earthme.luminol.commands.backend;

import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
import me.earthme.luminol.collision.CollisionBackendSelector;
import me.earthme.luminol.config.modules.optimizations.AcceleratedCollisionConfig;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import net.minecraft.commands.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.ArgumentNode;
import org.leavesmc.leaves.command.CommandContext;
import org.leavesmc.leaves.command.LiteralNode;
import org.leavesmc.leaves.command.RootNode;

public class NekoBackendCommand extends RootNode {
    private static final String PERM_BASE = "luminol.commands.nekobackend";
    private static final String[] BACKENDS = {"AUTO", "GPU", "FFM", "JAVA", "VANILLA"};

    public NekoBackendCommand() {
        super("nekobackend", PERM_BASE);
        children(
            new StatusSubcommand(),
            new ReloadSubcommand(),
            new SetSubcommand(),
            new BandwidthSubcommand(),
            new ToggleSubcommand()
        );
    }

    public static boolean hasPermission(@NotNull CommandSender sender, String... subcommand) {
        return hasPermission(PERM_BASE, sender, subcommand);
    }

    @Override
    public boolean requires(@NotNull CommandSourceStack source) {
        return hasPermission(source.getSender());
    }

    private static final class StatusSubcommand extends LiteralNode {
        private StatusSubcommand() {
            super("status");
        }

        @Override
        public boolean requires(@NotNull CommandSourceStack source) {
            return hasPermission(source.getSender(), this.name);
        }

        @Override
        protected boolean execute(@NotNull CommandContext context) throws CommandSyntaxException {
            context.getSender().sendMessage(Component.text(
                "[NekoCore] backend=" + CollisionBackendSelector.getCurrentBackendName()
                    + ", configured=" + AcceleratedCollisionConfig.backend
                    + ", enabled=" + AcceleratedCollisionConfig.enabled
                    + ", threshold=" + AcceleratedCollisionConfig.densityThreshold
                    + ", maxCollision=" + AcceleratedCollisionConfig.maxCollision
                    + ", gridSize=" + AcceleratedCollisionConfig.gridSize
                    + ", maxThreads=" + AcceleratedCollisionConfig.maxThreads
            ).color(TextColor.color(85, 255, 85)));
            return true;
        }
    }

    private static final class BandwidthSubcommand extends LiteralNode {
        private BandwidthSubcommand() {
            super("bandwidth");
        }

        @Override
        public boolean requires(@NotNull CommandSourceStack source) {
            return hasPermission(source.getSender(), this.name);
        }

        @Override
        protected boolean execute(@NotNull CommandContext context) {
            double cur = me.sucixr.kitin.network.bandwidth.BandwidthMeter.getCurrentMbps();
            double peak = me.sucixr.kitin.network.bandwidth.BandwidthMeter.getPeakMbps();
            String total = me.sucixr.kitin.network.bandwidth.BandwidthMeter.getTotalHuman();
            // approximate MiB/s for an at-a-glance second unit (Mbps / 8 * 1.048576 inverse ~ /8.389)
            double curMiBs = cur / 8.388608;
            context.getSender().sendMessage(Component.text(String.format(
                "[NekoCore] 出站带宽  现在: %.2f Mbps (%.2f MiB/s)  |  峰值: %.2f Mbps  |  本次累计: %s",
                cur, curMiBs, peak, total
            )).color(TextColor.color(85, 255, 85)));
            return true;
        }
    }

    private static final class ReloadSubcommand extends LiteralNode {
        private ReloadSubcommand() {
            super("reload");
        }

        @Override
        public boolean requires(@NotNull CommandSourceStack source) {
            return hasPermission(source.getSender(), this.name);
        }

        @Override
        protected boolean execute(@NotNull CommandContext context) {
            CollisionBackendSelector.reload();
            context.getSender().sendMessage(Component.text(
                "[NekoCore] backend reloaded, active=" + CollisionBackendSelector.getCurrentBackendName()
            ).color(TextColor.color(85, 255, 85)));
            return true;
        }
    }

    private static final class SetSubcommand extends LiteralNode {
        private SetSubcommand() {
            super("set");
            children(BackendArg::new);
        }

        @Override
        public boolean requires(@NotNull CommandSourceStack source) {
            return hasPermission(source.getSender(), this.name);
        }

        private final class BackendArg extends ArgumentNode<String> {
            private BackendArg() {
                super("backend", StringArgumentType.word());
            }

            @Override
            protected CompletableFuture<Suggestions> getSuggestions(@NotNull CommandContext context, @NotNull SuggestionsBuilder builder) {
                for (String backend : BACKENDS) {
                    builder.suggest(backend);
                }
                return builder.buildFuture();
            }

            @Override
            protected boolean execute(@NotNull CommandContext context) {
                String requested = context.getArgument(BackendArg.class).toUpperCase(Locale.ROOT);
                if (!isValidBackend(requested)) {
                    context.getSender().sendMessage(Component.text(
                        "[NekoCore] invalid backend: " + requested + " (AUTO/GPU/FFM/JAVA/VANILLA)"
                    ).color(TextColor.color(255, 85, 85)));
                    return true;
                }

                String actual = CollisionBackendSelector.setBackend(requested);
                AcceleratedCollisionConfig.backend = requested;
                context.getSender().sendMessage(Component.text(
                    "[NekoCore] backend switched to " + actual
                ).color(TextColor.color(85, 255, 85)));
                return true;
            }
        }
    }

    private static final String[] MODULES = {
        "entity_limiter", "entity_cull", "explosion_optimizer", "afk_entity_tracking", "mob_ai_reducer", "skip_zero_delta_packets", "mob_ai_throttle", "entity_velocity_packet"
    };

    private static final class ToggleSubcommand extends LiteralNode {
        private ToggleSubcommand() {
            super("toggle");
            children(ModuleArg::new);
        }

        @Override
        public boolean requires(@NotNull CommandSourceStack source) {
            return hasPermission(source.getSender(), this.name);
        }

        private final class ModuleArg extends ArgumentNode<String> {
            private ModuleArg() {
                super("module", StringArgumentType.word());
                children(StateArg::new);
            }

            @Override
            protected CompletableFuture<Suggestions> getSuggestions(@NotNull CommandContext context, @NotNull SuggestionsBuilder builder) {
                for (String m : MODULES) builder.suggest(m);
                return builder.buildFuture();
            }

            @Override
            protected boolean execute(@NotNull CommandContext context) {
                // module given without state: report current state
                String module = context.getArgument(ModuleArg.class).toLowerCase(Locale.ROOT);
                Boolean cur = getModuleEnabled(module);
                if (cur == null) {
                    context.getSender().sendMessage(Component.text(
                        "[NekoCore] unknown module: " + module + " (" + String.join("/", MODULES) + ")"
                    ).color(TextColor.color(255, 85, 85)));
                } else {
                    context.getSender().sendMessage(Component.text(
                        "[NekoCore] " + module + " is currently " + (cur ? "ON" : "OFF") + " — use /nekobackend toggle " + module + " <on|off>"
                    ).color(TextColor.color(85, 255, 85)));
                }
                return true;
            }

            private final class StateArg extends ArgumentNode<String> {
                private StateArg() {
                    super("state", StringArgumentType.word());
                }

                @Override
                protected CompletableFuture<Suggestions> getSuggestions(@NotNull CommandContext context, @NotNull SuggestionsBuilder builder) {
                    builder.suggest("on");
                    builder.suggest("off");
                    return builder.buildFuture();
                }

                @Override
                protected boolean execute(@NotNull CommandContext context) {
                    String module = context.getArgument(ModuleArg.class).toLowerCase(Locale.ROOT);
                    String state = context.getArgument(StateArg.class).toLowerCase(Locale.ROOT);
                    if (!state.equals("on") && !state.equals("off")) {
                        context.getSender().sendMessage(Component.text(
                            "[NekoCore] state must be on or off"
                        ).color(TextColor.color(255, 85, 85)));
                        return true;
                    }
                    boolean enable = state.equals("on");
                    boolean ok = setModuleEnabled(module, enable);
                    if (!ok) {
                        context.getSender().sendMessage(Component.text(
                            "[NekoCore] unknown module: " + module + " (" + String.join("/", MODULES) + ")"
                        ).color(TextColor.color(255, 85, 85)));
                        return true;
                    }
                    context.getSender().sendMessage(Component.text(
                        "[NekoCore] " + module + " -> " + (enable ? "ON" : "OFF")
                    ).color(TextColor.color(85, 255, 85)));
                    return true;
                }
            }
        }
    }

    /** Returns current enabled state, or null if unknown module. */
    private static Boolean getModuleEnabled(String module) {
        switch (module) {
            case "entity_limiter": return me.earthme.luminol.config.modules.optimizations.EntityLimiterConfig.enabled;
            case "entity_cull": return me.earthme.luminol.config.modules.optimizations.EntityCullConfig.enabled;
            case "explosion_optimizer": return me.earthme.luminol.config.modules.optimizations.ExplosionOptimizerConfig.enabled;
            case "afk_entity_tracking": return me.earthme.luminol.config.modules.optimizations.AfkEntityTrackingConfig.enabled;
            case "mob_ai_reducer": return me.earthme.luminol.config.modules.optimizations.MobAiReducerConfig.enabled;
            case "skip_zero_delta_packets": return me.earthme.luminol.config.modules.optimizations.SkipZeroDeltaPacketsConfig.skipZeroMove;
            case "mob_ai_throttle": return me.earthme.luminol.config.modules.optimizations.MobAiThrottleConfig.enabled;
            case "entity_velocity_packet": return me.earthme.luminol.config.modules.optimizations.EntityVelocityPacketConfig.enabled;
            default: return null;
        }
    }

    /** Sets enabled state at runtime; returns false for unknown module. Also fires start/register where needed. */
    private static boolean setModuleEnabled(String module, boolean enable) {
        switch (module) {
            case "entity_limiter":
                me.earthme.luminol.config.modules.optimizations.EntityLimiterConfig.enabled = enable;
                if (enable) me.earthme.luminol.entitylimiter.LuminolEntityLimiter.start();
                return true;
            case "entity_cull":
                me.earthme.luminol.config.modules.optimizations.EntityCullConfig.enabled = enable;
                return true;
            case "explosion_optimizer":
                me.earthme.luminol.config.modules.optimizations.ExplosionOptimizerConfig.enabled = enable;
                return true;
            case "afk_entity_tracking":
                me.earthme.luminol.config.modules.optimizations.AfkEntityTrackingConfig.enabled = enable;
                return true;
            case "mob_ai_reducer":
                me.earthme.luminol.config.modules.optimizations.MobAiReducerConfig.enabled = enable;
                if (enable) me.earthme.luminol.mobaireducer.MobAiReducerHandler.register();
                return true;
            case "skip_zero_delta_packets":
                me.earthme.luminol.config.modules.optimizations.SkipZeroDeltaPacketsConfig.skipZeroMove = enable;
                me.earthme.luminol.config.modules.optimizations.SkipZeroDeltaPacketsConfig.skipZeroRotation = enable;
                me.earthme.luminol.config.modules.optimizations.SkipZeroDeltaPacketsConfig.skipZeroMoveAndRotation = enable;
                return true;
            case "mob_ai_throttle":
                me.earthme.luminol.config.modules.optimizations.MobAiThrottleConfig.enabled = enable;
                return true;
            case "entity_velocity_packet":
                me.earthme.luminol.config.modules.optimizations.EntityVelocityPacketConfig.enabled = enable;
                return true;
            default:
                return false;
        }
    }

    private static boolean isValidBackend(String backend) {
        for (String candidate : BACKENDS) {
            if (candidate.equals(backend)) {
                return true;
            }
        }
        return false;
    }
}
