package net.mcreator.phayriosisreborn.config;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.mcreator.phayriosisreborn.network.PhayriosisrebornModVariables;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.config.ModConfig;
import net.minecraftforge.fml.event.config.ModConfigEvent;
import net.minecraftforge.server.ServerLifecycleHooks;

/**
 * World-authoritative, editable Forge server config. A one-time conversion copies existing
 * world settings into the server TOML, never replacing legacy saved choices with defaults.
 */
@Mod.EventBusSubscriber(modid = "phayriosisreborn", bus = Mod.EventBusSubscriber.Bus.FORGE)
public final class PhayriosisServerConfig {
    private static final String MOD_ID = "phayriosisreborn";
    private static final LinkedHashMap<String, ForgeConfigSpec.ConfigValue<?>> VALUES = new LinkedHashMap<>();
    public static final ForgeConfigSpec SPEC;
    private static boolean registered;

    private static ForgeConfigSpec.BooleanValue bool(ForgeConfigSpec.Builder b, String key, boolean def) {
        ForgeConfigSpec.BooleanValue v = b.define(key.substring(key.lastIndexOf('.') + 1), def);
        VALUES.put(key, v);
        return v;
    }
    private static ForgeConfigSpec.DoubleValue dbl(ForgeConfigSpec.Builder b, String key, double def, double min, double max) {
        ForgeConfigSpec.DoubleValue v = b.defineInRange(key.substring(key.lastIndexOf('.') + 1), def, min, max);
        VALUES.put(key, v);
        return v;
    }
    private static ForgeConfigSpec.IntValue integer(ForgeConfigSpec.Builder b, String key, int def, int min, int max) {
        ForgeConfigSpec.IntValue v = b.defineInRange(key.substring(key.lastIndexOf('.') + 1), def, min, max);
        VALUES.put(key, v);
        return v;
    }

    static {
        ForgeConfigSpec.Builder b = new ForgeConfigSpec.Builder();
        b.comment("Per-world Phayriosis server settings. Editable via Configured (if installed),", "or /phayconfig. The first run of an existing world migrates its saved options here.", "Keep the separate global phayriosisreborn-common.toml as a NEW WORLD template only;", "avoid override_existing_world_settings=true in that older file.");
        b.push("meteor");
        bool(b, "meteor.enabled", false);
        dbl(b, "meteor.minimum_day", 7, 0, 1000000);
        dbl(b, "meteor.minimum_phase", 1, 1, 8);
        dbl(b, "meteor.check_interval_ticks", 3600, 20, 12000000);
        dbl(b, "meteor.chance", .5, 0, 1);
        dbl(b, "meteor.min_distance", 210, 0, 100000);
        dbl(b, "meteor.max_distance", 250, 0, 100000);
        bool(b, "meteor.impact_infection_enabled", true);
        b.pop();
        b.push("infection_difficulty");
        integer(b, "infection_difficulty.mode", 1, 0, 4);
        dbl(b, "infection_difficulty.block_spread_speed", 3, 1, 5);
        dbl(b, "infection_difficulty.max_infectable_hardness", 2.9, 0, 100000);
        b.pop();
        b.push("infection");
        bool(b, "infection.block_spread_enabled", true);
        bool(b, "infection.mob_conversions_enabled", true);
        bool(b, "infection.player_conversion_enabled", true);
        bool(b, "infection.natural_infected_spawns_enabled", false);
        bool(b, "infection.dimensional_spawning_enabled", true);
        b.pop();
        b.push("advanced");
        bool(b, "advanced.phase_progression_frozen", false);
        b.pop();
        SPEC = b.build();
    }

    private PhayriosisServerConfig() {}

    /** Called once from the mod's constructor before the config loading window closes. */
    public static synchronized void bootstrap() {
        if (registered) return;
        ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, SPEC, "phayriosisreborn-server.toml");
        registered = true;
    }

    private static boolean b(String k) { return (Boolean) VALUES.get(k).get(); }
    private static double d(String k) { return ((Number) VALUES.get(k).get()).doubleValue(); }
    private static int i(String k) { return ((Number) VALUES.get(k).get()).intValue(); }

    /** The old saved-world representation also remains updated for legacy procedures. */
    private static void apply(ServerLevel level) {
        PhayriosisWorldSettings s = PhayriosisWorldSettings.get(level);
        s.meteorEnabled = b("meteor.enabled");
        s.meteorMinimumDay = d("meteor.minimum_day");
        s.meteorMinimumPhase = d("meteor.minimum_phase");
        s.meteorCheckIntervalTicks = d("meteor.check_interval_ticks");
        s.meteorChance = d("meteor.chance");
        s.meteorMinDistance = d("meteor.min_distance");
        s.meteorMaxDistance = Math.max(s.meteorMinDistance, d("meteor.max_distance"));
        s.meteorInfectionEnabled = b("meteor.impact_infection_enabled");
        s.infectionDifficultyMode = i("infection_difficulty.mode");
        s.nightmareModeEnabled = s.infectionDifficultyMode == 4;
        s.blockSpreadSpeed = d("infection_difficulty.block_spread_speed");
        s.maxInfectableHardness = d("infection_difficulty.max_infectable_hardness");
        s.blockSpreadEnabled = b("infection.block_spread_enabled");
        s.conversionsEnabled = b("infection.mob_conversions_enabled");
        s.playerConversionEnabled = b("infection.player_conversion_enabled");
        s.parasiteSpawningEnabled = b("infection.natural_infected_spawns_enabled");
        s.dimensionalSpawningEnabled = b("infection.dimensional_spawning_enabled");
        s.phaseFreezeEnabled = b("advanced.phase_progression_frozen");
        s.initialized = true;
        s.worldCreationConfigured = true;
        s.m_77762_();
        PhayriosisrebornModVariables.MapVariables legacy = PhayriosisrebornModVariables.MapVariables.get(level);
        if (legacy != null) {
            legacy.blockspread = s.blockSpreadEnabled;
            legacy.conversions = s.conversionsEnabled;
            legacy.playerconversion = s.playerConversionEnabled;
            legacy.doparasitesspawn = s.parasiteSpawningEnabled;
            legacy.doparasitesspawndimension = s.dimensionalSpawningEnabled;
            legacy.nightmaremode = s.nightmareModeEnabled;
            legacy.blockspreadspeed = s.blockSpreadSpeed;
            legacy.maxhardness = s.maxInfectableHardness;
            legacy.phasefrozen = s.phaseFreezeEnabled;
            legacy.syncData(level);
        }
    }

    private static void setIfDifferent(String key, Object value) {
        ForgeConfigSpec.ConfigValue<?> cv = VALUES.get(key);
        if (cv != null && !java.util.Objects.equals(cv.get(), value)) {
            @SuppressWarnings("unchecked") ForgeConfigSpec.ConfigValue<Object> writable = (ForgeConfigSpec.ConfigValue<Object>) cv;
            writable.set(value);
        }
    }
    private static void migrateSavedSettings(ServerLevel level) {
        PhayriosisWorldSettings s = PhayriosisWorldSettings.get(level);
        setIfDifferent("meteor.enabled", s.meteorEnabled);
        setIfDifferent("meteor.minimum_day", s.meteorMinimumDay);
        setIfDifferent("meteor.minimum_phase", s.meteorMinimumPhase);
        setIfDifferent("meteor.check_interval_ticks", s.meteorCheckIntervalTicks);
        setIfDifferent("meteor.chance", s.meteorChance);
        setIfDifferent("meteor.min_distance", s.meteorMinDistance);
        setIfDifferent("meteor.max_distance", Math.max(s.meteorMinDistance, s.meteorMaxDistance));
        setIfDifferent("meteor.impact_infection_enabled", s.meteorInfectionEnabled);
        setIfDifferent("infection_difficulty.mode", (int) s.infectionDifficultyMode);
        setIfDifferent("infection_difficulty.block_spread_speed", s.blockSpreadSpeed);
        setIfDifferent("infection_difficulty.max_infectable_hardness", s.maxInfectableHardness);
        setIfDifferent("infection.block_spread_enabled", s.blockSpreadEnabled);
        setIfDifferent("infection.mob_conversions_enabled", s.conversionsEnabled);
        setIfDifferent("infection.player_conversion_enabled", s.playerConversionEnabled);
        setIfDifferent("infection.natural_infected_spawns_enabled", s.parasiteSpawningEnabled);
        setIfDifferent("infection.dimensional_spawning_enabled", s.dimensionalSpawningEnabled);
        setIfDifferent("advanced.phase_progression_frozen", s.phaseFreezeEnabled);
    }

    @SubscribeEvent(priority = EventPriority.LOWEST)
    public static void serverStarted(ServerStartedEvent e) {
        if (!registered) return;
        ServerLevel level = e.getServer().m_129880_(Level.f_46428_);
        if (level == null) return;
        try {
            PhayriosisServerConfigState state = PhayriosisServerConfigState.get(level);
            if (!state.migrated) {
                // Existing-world saved options and explicit Create World choices win ONCE.
                migrateSavedSettings(level);
                state.migrated = true;
                state.m_77762_();
                System.out.println("[Phayriosis Config] Migrated current world settings to serverconfig/phayriosisreborn-server.toml");
            } else {
                apply(level); // Edited serverconfig file is authoritative after initial migration.
            }
        } catch (RuntimeException ex) {
            System.err.println("[Phayriosis Config] Unable to sync world server config: " + ex);
        }
    }

    public static void applyReloadToCurrentServer() {
        MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
        if (server == null) return;
        server.execute(() -> {
            ServerLevel level = server.m_129880_(Level.f_46428_);
            if (level == null || !PhayriosisServerConfigState.get(level).migrated) return;
            try { apply(level); }
            catch (RuntimeException ex) { System.err.println("[Phayriosis Config] Reload failed: " + ex); }
        });
    }

    @Mod.EventBusSubscriber(modid = "phayriosisreborn", bus = Mod.EventBusSubscriber.Bus.MOD)
    public static final class ReloadEvents {
        private ReloadEvents() {}
        @SubscribeEvent
        public static void reloaded(ModConfigEvent.Reloading e) {
            if (e.getConfig().getSpec() == SPEC) applyReloadToCurrentServer();
        }
    }

    private static void message(CommandSourceStack source, String message) {
        source.m_288197_(() -> Component.m_237113_(message), false);
    }
    @SubscribeEvent
    public static void commands(RegisterCommandsEvent e) {
        e.getDispatcher().register(
            Commands.m_82127_("phayconfig")
              .requires(source -> source.m_6761_(2))
              .then(Commands.m_82127_("list").executes(ctx -> {
                  message(ctx.getSource(), "Phayriosis server settings: " + String.join(", ", VALUES.keySet()));
                  return VALUES.size();
              }))
              .then(Commands.m_82127_("get").then(
                  Commands.m_82129_("key", StringArgumentType.word()).executes(ctx -> {
                      String key = StringArgumentType.getString(ctx, "key").toLowerCase(Locale.ROOT);
                      ForgeConfigSpec.ConfigValue<?> v = VALUES.get(key);
                      if (v == null) { ctx.getSource().m_81352_(Component.m_237113_("Unknown config key. Use /phayconfig list")); return 0; }
                      message(ctx.getSource(), key + " = " + v.get());
                      return 1;
                  })))
              .then(Commands.m_82127_("set").then(
                  Commands.m_82129_("key", StringArgumentType.word()).then(
                      Commands.m_82129_("value", StringArgumentType.word()).executes(ctx -> change(ctx)))))
              .executes(ctx -> { message(ctx.getSource(), "Use /phayconfig list, /phayconfig get <key>, /phayconfig set <key> <value>"); return 1; })
        );
    }
    private static int change(CommandContext<CommandSourceStack> ctx) {
        String key = StringArgumentType.getString(ctx, "key").toLowerCase(Locale.ROOT);
        String raw = StringArgumentType.getString(ctx, "value");
        ForgeConfigSpec.ConfigValue<?> v = VALUES.get(key);
        if (v == null) { ctx.getSource().m_81352_(Component.m_237113_("Unknown config key. Use /phayconfig list")); return 0; }
        try {
            Object old = v.get();
            Object value;
            if (old instanceof Boolean) {
                if (!raw.equalsIgnoreCase("true") && !raw.equalsIgnoreCase("false")) throw new IllegalArgumentException("expected true/false");
                value = Boolean.parseBoolean(raw);
            } else if (old instanceof Integer) value = Integer.parseInt(raw);
            else value = Double.parseDouble(raw);
            if (value instanceof Number n && !Double.isFinite(n.doubleValue())) throw new IllegalArgumentException("non-finite number");
            @SuppressWarnings("unchecked") ForgeConfigSpec.ConfigValue<Object> writable = (ForgeConfigSpec.ConfigValue<Object>) v;
            writable.set(value); // Forge validates numeric ranges and saves per-world server TOML.
            ServerLevel level = ctx.getSource().m_81377_().m_129880_(Level.f_46428_);
            if (level != null) apply(level);
            message(ctx.getSource(), "Saved " + key + " = " + v.get() + " (this world)");
            return 1;
        } catch (IllegalArgumentException ex) {
            ctx.getSource().m_81352_(Component.m_237113_("Invalid " + key + ": " + ex.getMessage()));
            return 0;
        }
    }
}
