package com.livingworld.commands;

import java.util.UUID;
import java.util.function.Function;
import java.util.function.IntUnaryOperator;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.arguments.DoubleArgumentType;

import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;

import com.livingworld.core.simulation.SimulationManager;
import com.livingworld.modules.ModuleRegistry;
import com.livingworld.regions.RegionManager;

/**
 * Registers the root Living World debug command tree.
 */
public final class LivingWorldCommandRoot {

    private static final int OPERATOR_PERMISSION_LEVEL = 2;

    private LivingWorldCommandRoot() {
    }

    public static void register(
            CommandDispatcher<CommandSourceStack> dispatcher,
            RegionManager regionManager,
            ModuleRegistry moduleRegistry,
            SimulationManager simulationManager) {
        registerDeferred(
                dispatcher,
                () -> regionManager,
                () -> moduleRegistry,
                () -> simulationManager,
                uuid -> false,
                css -> "Atmosphere not available.",
                css -> "Climate not available.",
                n -> n,
                () -> "Wind not available.",
                css -> "Events not available.",
                () -> "No volcanic regions.");
    }

    public static void registerDeferred(
            CommandDispatcher<CommandSourceStack> dispatcher,
            Supplier<RegionManager> regionManager,
            Supplier<ModuleRegistry> moduleRegistry,
            Supplier<SimulationManager> simulationManager,
            Function<UUID, Boolean> hudToggle,
            Function<CommandSourceStack, String> atmosphereStatus,
            Function<CommandSourceStack, String> climateStatus,
            IntUnaryOperator speedSetter,
            Supplier<String> windStatus,
            Function<CommandSourceStack, String> eventsStatus,
            Supplier<String> volcanoStatus) {
        if (dispatcher == null) {
            throw new IllegalArgumentException("dispatcher must not be null");
        }
        if (regionManager == null) {
            throw new IllegalArgumentException("regionManager must not be null");
        }
        if (moduleRegistry == null) {
            throw new IllegalArgumentException("moduleRegistry must not be null");
        }
        if (simulationManager == null) {
            throw new IllegalArgumentException("simulationManager must not be null");
        }

        dispatcher.register(Commands.literal("lw")
                .requires(source -> source.hasPermission(OPERATOR_PERMISSION_LEVEL))
                .then(Commands.literal("status")
                        .executes(context -> showStatus(
                                context.getSource(),
                                requireService(regionManager, "regionManager"),
                                requireService(moduleRegistry, "moduleRegistry"),
                                requireService(simulationManager, "simulationManager"))))
                .then(Commands.literal("region")
                        .then(Commands.literal("info")
                                .executes(context -> RegionInfoCommand.executeAtSelf(
                                        context.getSource(),
                                        requireService(regionManager, "regionManager")))
                                .then(Commands.argument("x", IntegerArgumentType.integer())
                                        .then(Commands.argument("z", IntegerArgumentType.integer())
                                                .executes(context -> RegionInfoCommand.executeAt(
                                                        context.getSource(),
                                                        requireService(regionManager, "regionManager"),
                                                        IntegerArgumentType.getInteger(context, "x"),
                                                        IntegerArgumentType.getInteger(context, "z"))))))
                        .then(Commands.literal("force-update")
                                .executes(context -> ForceUpdateCommand.executeAtSelf(
                                        context.getSource(),
                                        requireService(regionManager, "regionManager"),
                                        requireService(simulationManager, "simulationManager"))))
                        .then(RegionBordersCommand.build()))
                .then(Commands.literal("modules")
                        .then(Commands.literal("list")
                                .executes(context -> listModules(
                                        context.getSource(),
                                        requireService(moduleRegistry, "moduleRegistry")))))
                .then(Commands.literal("stats")
                        .executes(context -> StatsCommand.execute(
                                context.getSource(),
                                requireService(simulationManager, "simulationManager"))))
                .then(Commands.literal("simulate")
                        .then(Commands.argument(
                                        "ticks",
                                        IntegerArgumentType.integer(
                                                1, SimulationManager.MAX_FORCED_SIMULATION_TICKS))
                                .executes(context -> SimulateCommand.execute(
                                        context.getSource(),
                                        requireService(simulationManager, "simulationManager"),
                                        IntegerArgumentType.getInteger(context, "ticks")))))
                .then(Commands.literal("hud")
                        .executes(context -> toggleHud(context.getSource(), hudToggle)))
                .then(Commands.literal("atmosphere")
                        .executes(context -> {
                            String info = atmosphereStatus.apply(context.getSource());
                            context.getSource().sendSuccess(() -> Component.literal(info), false);
                            return 1;
                        }))
                .then(Commands.literal("climate")
                        .executes(context -> {
                            String info = climateStatus.apply(context.getSource());
                            context.getSource().sendSuccess(() -> Component.literal(info), false);
                            return 1;
                        }))
                .then(Commands.literal("wind")
                        .executes(context -> {
                            String info = windStatus.get();
                            context.getSource().sendSuccess(() -> Component.literal(info), false);
                            return 1;
                        }))
                .then(Commands.literal("speed")
                        .then(Commands.argument("multiplier", IntegerArgumentType.integer(1, 100))
                                .executes(context -> {
                                    int n = IntegerArgumentType.getInteger(context, "multiplier");
                                    int actual = speedSetter.applyAsInt(n);
                                    context.getSource().sendSuccess(
                                            () -> Component.literal("[LW] Simulation speed: " + actual + "x"),
                                            false);
                                    return actual;
                                }))
                        .then(Commands.literal("reset")
                                .executes(context -> {
                                    speedSetter.applyAsInt(1);
                                    context.getSource().sendSuccess(
                                            () -> Component.literal("[LW] Simulation speed reset to 1x"),
                                            false);
                                    return 1;
                                })))
                .then(LivingWorldMapCommand.build(regionManager))
                .then(RegionSetCommand.build(regionManager))
                .then(EcoDemoCommand.build(regionManager))
                .then(Commands.literal("events")
                        .executes(context -> {
                            String info = eventsStatus.apply(context.getSource());
                            context.getSource().sendSuccess(() -> Component.literal(info), false);
                            return 1;
                        }))
                .then(Commands.literal("volcanoes")
                        .executes(context -> {
                            String info = volcanoStatus.get();
                            context.getSource().sendSuccess(() -> Component.literal(info), false);
                            return 1;
                        })));
    }

    private static int toggleHud(CommandSourceStack source, Function<UUID, Boolean> hudToggle) {
        ServerPlayer player;
        try {
            player = source.getPlayerOrException();
        } catch (CommandSyntaxException e) {
            source.sendFailure(Component.literal("/lw hud can only be used by a player"));
            return 0;
        }
        boolean nowEnabled = hudToggle.apply(player.getUUID());
        source.sendSuccess(
                () -> Component.literal("Region HUD " + (nowEnabled ? "enabled" : "disabled")),
                false);
        return 1;
    }

    private static <T> T requireService(Supplier<T> supplier, String name) {
        if (supplier == null) {
            throw new IllegalArgumentException(name + " supplier must not be null");
        }
        T service = supplier.get();
        if (service == null) {
            throw new IllegalStateException(name + " is not ready");
        }
        return service;
    }

    private static int showStatus(
            CommandSourceStack source,
            RegionManager regionManager,
            ModuleRegistry moduleRegistry,
            SimulationManager simulationManager) {
        String message = "Living World status: activeRegions=" + regionManager.getActiveRegions().size()
                + ", enabledModules=" + moduleRegistry.getEnabledModules().size()
                + ", simulationTick=" + simulationManager.getSimulationTickCounter();
        source.sendSuccess(() -> Component.literal(message), false);
        return 1;
    }

    private static int listModules(
            CommandSourceStack source,
            ModuleRegistry moduleRegistry) {
        String moduleIds = moduleRegistry.getAllModules().stream()
                .map(module -> module.getModuleId()
                        + (module.getMetadata().defaultEnabled() ? " [enabled]" : " [disabled]"))
                .collect(Collectors.joining(", "));
        if (moduleIds.isEmpty()) {
            moduleIds = "none";
        }
        String message = "Living World modules: " + moduleIds;
        source.sendSuccess(() -> Component.literal(message), false);
        return 1;
    }

    private static int placeholder(CommandSourceStack source, String message) {
        source.sendFailure(Component.literal(message));
        return 0;
    }
}
