package com.tiedup.remake.core;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.minecraftforge.common.ForgeConfigSpec;
import org.apache.commons.lang3.tuple.Pair;

/**
 * Centralized configuration for TiedUp! Remake.
 * Handles both Server (COMMON) and Client configurations.
 */
public class ModConfig {

    public static final ServerConfig SERVER;
    public static final ForgeConfigSpec SERVER_SPEC;

    public static final ClientConfig CLIENT;
    public static final ForgeConfigSpec CLIENT_SPEC;

    static {
        final Pair<ServerConfig, ForgeConfigSpec> serverSpecPair =
            new ForgeConfigSpec.Builder().configure(ServerConfig::new);
        SERVER = serverSpecPair.getLeft();
        SERVER_SPEC = serverSpecPair.getRight();

        final Pair<ClientConfig, ForgeConfigSpec> clientSpecPair =
            new ForgeConfigSpec.Builder().configure(ClientConfig::new);
        CLIENT = clientSpecPair.getLeft();
        CLIENT_SPEC = clientSpecPair.getRight();
    }

    public static class ServerConfig {

        // === General Mechanics ===
        public final ForgeConfigSpec.IntValue tyingTime;
        public final ForgeConfigSpec.IntValue untyingTime;
        public final ForgeConfigSpec.BooleanValue struggleEnabled;
        public final ForgeConfigSpec.IntValue struggleProbability;
        public final ForgeConfigSpec.IntValue struggleTimer;
        public final ForgeConfigSpec.IntValue struggleMinDecrease;
        public final ForgeConfigSpec.IntValue struggleMaxDecrease;
        public final ForgeConfigSpec.IntValue struggleCollarRandomShock;

        // === Mini-Game Settings ===
        public final ForgeConfigSpec.BooleanValue struggleMiniGameEnabled;
        public final ForgeConfigSpec.BooleanValue lockpickMiniGameEnabled;
        public final ForgeConfigSpec.IntValue kidnapperDetectionRadius;
        public final ForgeConfigSpec.IntValue exhaustionCooldownSeconds;

        // === Restrictions ===
        public final ForgeConfigSpec.DoubleValue tiedSwimSpeedMultiplier;
        public final ForgeConfigSpec.IntValue messageCooldown;

        // === Kidnapping & Bounties ===
        public final ForgeConfigSpec.BooleanValue enslavementEnabled;
        public final ForgeConfigSpec.BooleanValue forcedSeatingEnabled;
        public final ForgeConfigSpec.IntValue maxBountiesPerPlayer;
        public final ForgeConfigSpec.IntValue bountyDuration;
        public final ForgeConfigSpec.IntValue bountyDeliveryRadius;
        public final ForgeConfigSpec.IntValue kidnapBombRadius;
        public final ForgeConfigSpec.IntValue kidnapBombFuse;

        // === NPCs ===
        public final ForgeConfigSpec.BooleanValue damselsSpawn;
        public final ForgeConfigSpec.BooleanValue kidnappersSpawn;
        public final ForgeConfigSpec.DoubleValue kidnapperMaxHealth;
        public final ForgeConfigSpec.DoubleValue kidnapperSpeed;
        public final ForgeConfigSpec.DoubleValue kidnapperDamage;
        public final ForgeConfigSpec.DoubleValue kidnapperFollowRange;
        public final ForgeConfigSpec.IntValue merchantHostileDuration;
        public final ForgeConfigSpec.EnumValue<SpawnGenderMode> spawnGenderMode;

        // === Spawning Configuration ===
        public final ForgeConfigSpec.IntValue damselSpawnRate;
        public final ForgeConfigSpec.IntValue kidnapperSpawnRate;
        public final ForgeConfigSpec.IntValue kidnapperArcherSpawnRate;
        public final ForgeConfigSpec.IntValue kidnapperEliteSpawnRate;
        public final ForgeConfigSpec.IntValue kidnapperMerchantSpawnRate;
        public final ForgeConfigSpec.IntValue masterSpawnRate;

        public enum SpawnGenderMode {
            BOTH,
            FEMALE_ONLY,
            MALE_ONLY,
        }

        // === Economy ===
        public final ForgeConfigSpec.IntValue merchantMinTrades;
        public final ForgeConfigSpec.IntValue merchantMaxTrades;
        // Tier prices (in nuggets)
        public final ForgeConfigSpec.IntValue tier1PriceMin;
        public final ForgeConfigSpec.IntValue tier1PriceMax;
        public final ForgeConfigSpec.IntValue tier2PriceMin;
        public final ForgeConfigSpec.IntValue tier2PriceMax;
        public final ForgeConfigSpec.IntValue tier3PriceMin;
        public final ForgeConfigSpec.IntValue tier3PriceMax;
        public final ForgeConfigSpec.IntValue tier4PriceMin;
        public final ForgeConfigSpec.IntValue tier4PriceMax;

        // === Combat & Tools ===
        public final ForgeConfigSpec.DoubleValue whipDamage;
        public final ForgeConfigSpec.IntValue whipResistanceDecrease;

        public final ForgeConfigSpec.IntValue chloroformDurability;
        public final ForgeConfigSpec.IntValue ragWetTime;
        public final ForgeConfigSpec.IntValue chloroformEffectDuration;

        public final ForgeConfigSpec.IntValue ropeArrowBindChance;
        public final ForgeConfigSpec.IntValue archerArrowBindChanceBase;
        public final ForgeConfigSpec.IntValue archerArrowBindChancePerHit;

        public final ForgeConfigSpec.IntValue shockerControllerRadius;

        public final ForgeConfigSpec.DoubleValue taserDamage;
        public final ForgeConfigSpec.IntValue taserStunDuration;

        public final ForgeConfigSpec.IntValue lockpickSuccessChance;
        public final ForgeConfigSpec.IntValue lockpickBreakChance;
        public final ForgeConfigSpec.DoubleValue lockpickJamChance;

        // === Resistances ===
        public final Map<String, ForgeConfigSpec.IntValue> bindResistances =
            new HashMap<>();
        public final ForgeConfigSpec.IntValue padlockResistance;

        // === Gags ===
        public final ForgeConfigSpec.BooleanValue gagTalkProximity;
        public final Map<String, ForgeConfigSpec.DoubleValue> gagComprehension =
            new HashMap<>();
        public final Map<String, ForgeConfigSpec.DoubleValue> gagRange =
            new HashMap<>();

        // === Dialogue ===
        public final ForgeConfigSpec.IntValue dialogueRadius;
        public final ForgeConfigSpec.IntValue dialogueCooldown;

        // === Kidnapper Lairs ===
        public final ForgeConfigSpec.BooleanValue enableLairs;
        public final ForgeConfigSpec.DoubleValue lairDensity;

        // === Kidnapper Capture ===
        public final ForgeConfigSpec.BooleanValue transportToLair;
        public final ForgeConfigSpec.BooleanValue confiscateInventory;

        // === Kidnapper Ransom ===
        public final ForgeConfigSpec.BooleanValue enableRansom;
        public final ForgeConfigSpec.IntValue ransomAmount;
        public final ForgeConfigSpec.ConfigValue<String> ransomItem;
        public final ForgeConfigSpec.IntValue ransomTimeoutMinutes;

        // === Kidnapper Timeout ===
        public final ForgeConfigSpec.IntValue maxCaptivityMinutes;
        public final ForgeConfigSpec.BooleanValue releaseWithInventory;
        public final ForgeConfigSpec.IntValue laborRestSeconds;

        // === Kidnapper Struggle ===
        public final ForgeConfigSpec.BooleanValue struggleMakesNoise;

        // === Kidnapper AI ===
        public final ForgeConfigSpec.BooleanValue enablePunishment;

        // === Kidnapper Abandon ===
        public final ForgeConfigSpec.IntValue abandonTeleportRadius;
        public final ForgeConfigSpec.BooleanValue abandonKeepsBlindfold;
        public final ForgeConfigSpec.BooleanValue abandonKeepsBinds;

        // === Kidnapper Solo Mode ===
        public final ForgeConfigSpec.BooleanValue enableSoloFallback;
        public final ForgeConfigSpec.DoubleValue soloKeepChance;
        public final ForgeConfigSpec.DoubleValue soloAbandonChance;
        public final ForgeConfigSpec.IntValue soloTimeoutSeconds;

        // === Master NPC (Pet Play) ===
        public final ForgeConfigSpec.BooleanValue enableMasterSpawn;
        public final ForgeConfigSpec.IntValue masterPetCollarResistance;
        public final ForgeConfigSpec.DoubleValue masterSlaveDamageMultiplier;
        public final ForgeConfigSpec.IntValue masterMinDistractionInterval;
        public final ForgeConfigSpec.IntValue masterMaxDistractionInterval;
        public final ForgeConfigSpec.IntValue masterMinDistractionDuration;
        public final ForgeConfigSpec.IntValue masterMaxDistractionDuration;

        // === Cells ===
        public final ForgeConfigSpec.IntValue cellMaxWallDistance;
        public final ForgeConfigSpec.IntValue maxPrisonersPerCell;
        public final ForgeConfigSpec.BooleanValue deleteMarkerOnCellDelete;
        public final ForgeConfigSpec.BooleanValue autoRemoveDestroyedWalls;

        public ServerConfig(ForgeConfigSpec.Builder builder) {
            builder.push("General");
            tyingTime = builder
                .comment("Time in seconds to tie up a player")
                .defineInRange("tyingTime", 5, 1, 60);
            untyingTime = builder
                .comment("Time in seconds to untie a player")
                .defineInRange("untyingTime", 10, 1, 60);
            struggleEnabled = builder
                .comment("Enable struggle mechanic")
                .define("struggleEnabled", true);
            struggleProbability = builder
                .comment("Chance (0-100) to succeed in a struggle attempt")
                .defineInRange("struggleProbability", 40, 0, 100);
            struggleTimer = builder
                .comment("Cooldown between struggle attempts in ticks")
                .defineInRange("struggleTimer", 80, 1, 200);
            struggleMinDecrease = builder
                .comment("Minimum resistance removed per successful struggle")
                .defineInRange("struggleMinDecrease", 1, 1, 100);
            struggleMaxDecrease = builder
                .comment("Maximum resistance removed per successful struggle")
                .defineInRange("struggleMaxDecrease", 10, 1, 100);
            struggleCollarRandomShock = builder
                .comment("Probability % of random shock during collar struggle")
                .defineInRange("struggleCollarRandomShock", 20, 0, 100);

            builder.push("MiniGames");
            struggleMiniGameEnabled = builder
                .comment(
                    "Enable the struggle mini-game (QTE sequence) instead of RNG-based struggle"
                )
                .define("struggleMiniGameEnabled", true);
            lockpickMiniGameEnabled = builder
                .comment(
                    "Enable the lockpick mini-game (sweet spot) instead of RNG-based lockpicking"
                )
                .define("lockpickMiniGameEnabled", true);
            kidnapperDetectionRadius = builder
                .comment(
                    "Radius in blocks where kidnappers penalize struggle mini-game"
                )
                .defineInRange("kidnapperDetectionRadius", 10, 0, 50);
            exhaustionCooldownSeconds = builder
                .comment("Cooldown in seconds after mini-game exhaustion")
                .defineInRange("exhaustionCooldownSeconds", 30, 5, 120);
            builder.pop();
            builder.pop();

            builder.push("Restrictions");
            tiedSwimSpeedMultiplier = builder
                .comment(
                    "Swim speed multiplier when legs are bound (0.0 - 1.0)"
                )
                .defineInRange("tiedSwimSpeedMultiplier", 0.5, 0.0, 1.0);
            messageCooldown = builder
                .comment("Cooldown in ms for restriction chat messages")
                .defineInRange("messageCooldown", 2000, 0, 10000);
            builder.pop();

            builder.push("Kidnapping");
            enslavementEnabled = builder
                .comment("Enable Master/Slave system")
                .define("enslavementEnabled", true);
            forcedSeatingEnabled = builder
                .comment("Enable Forced Seating mechanics (ALT+Click)")
                .define("forcedSeatingEnabled", true);
            maxBountiesPerPlayer = builder
                .comment("Max active bounties per player")
                .defineInRange("maxBountiesPerPlayer", 5, 1, 50);
            bountyDuration = builder
                .comment("Duration of bounties in seconds")
                .defineInRange("bountyDuration", 14400, 60, 86400); // 4 hours default
            bountyDeliveryRadius = builder
                .comment("Radius in blocks to deliver captive for bounty")
                .defineInRange("bountyDeliveryRadius", 5, 1, 20);
            kidnapBombRadius = builder
                .comment("Explosion radius for kidnap bombs")
                .defineInRange("kidnapBombRadius", 5, 1, 20);
            kidnapBombFuse = builder
                .comment("Fuse time for kidnap bombs in ticks")
                .defineInRange("kidnapBombFuse", 80, 20, 600);
            builder.pop();

            builder.push("NPCs");
            damselsSpawn = builder
                .comment("Enable natural spawning of Damsels")
                .define("damselsSpawn", true);
            kidnappersSpawn = builder
                .comment(
                    "Enable natural spawning of Kidnappers (includes Elite, Archer, Merchant)"
                )
                .define("kidnappersSpawn", true);
            kidnapperMaxHealth = builder
                .comment("Max Health for Kidnappers")
                .defineInRange("kidnapperMaxHealth", 20.0, 1.0, 100.0);
            kidnapperSpeed = builder
                .comment("Movement Speed for Kidnappers")
                .defineInRange("kidnapperSpeed", 0.27, 0.1, 1.0);
            kidnapperDamage = builder
                .comment("Attack Damage for Kidnappers")
                .defineInRange("kidnapperDamage", 6.0, 1.0, 20.0);
            kidnapperFollowRange = builder
                .comment("Follow Range for Kidnappers")
                .defineInRange("kidnapperFollowRange", 60.0, 16.0, 128.0);
            merchantHostileDuration = builder
                .comment("Ticks a Merchant stays hostile after being attacked")
                .defineInRange("merchantHostileDuration", 6000, 0, 72000); // 5 mins

            builder.push("Spawning");
            spawnGenderMode = builder
                .comment(
                    "Gender spawn mode for NPCs: BOTH, FEMALE_ONLY, MALE_ONLY"
                )
                .defineEnum("spawnGenderMode", SpawnGenderMode.BOTH);

            // NPC spawn rates (0-100 percentage multiplier)
            damselSpawnRate = builder
                .comment(
                    "Spawn rate for Damsels (0-100, percentage of base spawn weight)"
                )
                .defineInRange("damselSpawnRate", 60, 0, 100);
            kidnapperSpawnRate = builder
                .comment(
                    "Spawn rate for base Kidnappers (0-100, percentage of base spawn weight)"
                )
                .defineInRange("kidnapperSpawnRate", 70, 0, 100);
            kidnapperArcherSpawnRate = builder
                .comment(
                    "Spawn rate for Kidnapper Archers (0-100, percentage of base spawn weight)"
                )
                .defineInRange("kidnapperArcherSpawnRate", 70, 0, 100);
            kidnapperEliteSpawnRate = builder
                .comment(
                    "Spawn rate for Kidnapper Elites (0-100, percentage of base spawn weight)"
                )
                .defineInRange("kidnapperEliteSpawnRate", 70, 0, 100);
            kidnapperMerchantSpawnRate = builder
                .comment(
                    "Spawn rate for Kidnapper Merchants (0-100, percentage of base spawn weight)"
                )
                .defineInRange("kidnapperMerchantSpawnRate", 70, 0, 100);
            masterSpawnRate = builder
                .comment(
                    "Spawn rate for Masters (0-100, percentage of base spawn weight)"
                )
                .defineInRange("masterSpawnRate", 100, 0, 100);

            builder.pop();
            builder.pop();

            builder.push("Economy");
            merchantMinTrades = builder
                .comment("Min number of trades offered by Merchant")
                .defineInRange("merchantMinTrades", 8, 1, 20);
            merchantMaxTrades = builder
                .comment("Max number of trades offered by Merchant")
                .defineInRange("merchantMaxTrades", 12, 1, 20);

            builder.push("TierPrices"); // In nuggets
            tier1PriceMin = builder.defineInRange("tier1Min", 9, 1, 640);
            tier1PriceMax = builder.defineInRange("tier1Max", 18, 1, 640);
            tier2PriceMin = builder.defineInRange("tier2Min", 27, 1, 640);
            tier2PriceMax = builder.defineInRange("tier2Max", 45, 1, 640);
            tier3PriceMin = builder.defineInRange("tier3Min", 54, 1, 640);
            tier3PriceMax = builder.defineInRange("tier3Max", 90, 1, 640);
            tier4PriceMin = builder.defineInRange("tier4Min", 90, 1, 640);
            tier4PriceMax = builder.defineInRange("tier4Max", 180, 1, 640);
            builder.pop();
            builder.pop();

            builder.push("CombatTools");
            whipDamage = builder
                .comment("Damage dealt by Whip")
                .defineInRange("whipDamage", 2.0, 0.0, 20.0);
            whipResistanceDecrease = builder
                .comment("Resistance removed per Whip hit")
                .defineInRange("whipResistanceDecrease", 15, 0, 100);

            chloroformDurability = builder
                .comment("Number of uses for Chloroform Bottle")
                .defineInRange("chloroformDurability", 9, 1, 64);
            ragWetTime = builder
                .comment("Ticks before wet rag dries")
                .defineInRange("ragWetTime", 6000, 100, 72000);
            chloroformEffectDuration = builder
                .comment("Ticks knockout effect lasts")
                .defineInRange("chloroformEffectDuration", 200, 20, 6000);

            ropeArrowBindChance = builder
                .comment("Chance % to bind target with Rope Arrow")
                .defineInRange("ropeArrowBindChance", 50, 0, 100);
            archerArrowBindChanceBase = builder
                .comment("Base bind chance % for Archer NPCs")
                .defineInRange("archerArrowBindChanceBase", 10, 0, 100);
            archerArrowBindChancePerHit = builder
                .comment(
                    "Cumulative bind chance % increase per hit for Archer NPCs"
                )
                .defineInRange("archerArrowBindChancePerHit", 10, 0, 100);

            shockerControllerRadius = builder
                .comment("Radius for Shocker Controller")
                .defineInRange("shockerControllerRadius", 50, 1, 200);

            taserDamage = builder
                .comment("Damage dealt by Taser")
                .defineInRange("taserDamage", 5.0, 0.0, 20.0);
            taserStunDuration = builder
                .comment("Ticks Taser stun lasts")
                .defineInRange("taserStunDuration", 100, 0, 600);

            lockpickSuccessChance = builder
                .comment("Chance % to successfully pick a lock")
                .defineInRange("lockpickSuccessChance", 25, 0, 100);
            lockpickBreakChance = builder
                .comment("Chance % to break lockpick on fail")
                .defineInRange("lockpickBreakChance", 15, 0, 100);
            lockpickJamChance = builder
                .comment("Chance % to jam lock on fail")
                .defineInRange("lockpickJamChance", 2.5, 0.0, 100.0);
            builder.pop();

            builder.push("Resistances");
            addResistance(builder, "rope", 100);
            addResistance(builder, "chain", 150);
            addResistance(builder, "armbinder", 180);
            addResistance(builder, "wrap", 200);
            addResistance(builder, "straitjacket", 250);
            addResistance(builder, "latex_sack", 300);
            addResistance(builder, "ribbon", 50);
            addResistance(builder, "vine", 60);
            addResistance(builder, "web", 70);
            addResistance(builder, "slime", 80);
            addResistance(builder, "tape", 90);

            addResistance(builder, "gag", 100);
            addResistance(builder, "blindfold", 100);
            addResistance(builder, "collar", 100);

            padlockResistance = builder
                .comment("Resistance added by a padlock/lock")
                .defineInRange("padlockResistance", 250, 0, 1000);
            builder.pop();

            builder.push("Gags");
            gagTalkProximity = builder
                .comment("Enable proximity chat for gagged players")
                .define("gagTalkProximity", true);

            builder.push("Properties");
            addGagProps(builder, "cloth", 0.4, 15.0);
            addGagProps(builder, "ball", 0.2, 10.0);
            addGagProps(builder, "tape", 0.05, 5.0);
            addGagProps(builder, "stuffed", 0.0, 3.0);
            addGagProps(builder, "panel", 0.05, 4.0);
            addGagProps(builder, "latex", 0.1, 6.0);
            addGagProps(builder, "ring", 0.5, 12.0);
            addGagProps(builder, "bite", 0.3, 10.0);
            addGagProps(builder, "sponge", 0.0, 2.0);
            addGagProps(builder, "baguette", 0.25, 8.0);
            builder.pop();
            builder.pop();

            builder.push("Dialogue");
            dialogueRadius = builder
                .comment("Radius for NPC dialogue broadcast")
                .defineInRange("dialogueRadius", 20, 1, 100);
            dialogueCooldown = builder
                .comment("Cooldown in ticks between same-type dialogues")
                .defineInRange("dialogueCooldown", 100, 20, 1200);
            builder.pop();

            // === KIDNAPPER CONFIGURATION ===
            builder.push("KidnapperAdvanced");

            builder.push("Lairs");
            enableLairs = builder
                .comment(
                    "Enable kidnapper lair structures (camps, outposts, fortresses)"
                )
                .define("enableLairs", true);
            lairDensity = builder
                .comment(
                    "Density multiplier for lair spawning (1.0 = default, 0.5 = half as common)"
                )
                .defineInRange("lairDensity", 1.0, 0.1, 5.0);
            builder.pop();

            builder.push("Capture");
            transportToLair = builder
                .comment(
                    "Kidnappers transport captives to their lair instead of fleeing"
                )
                .define("transportToLair", true);
            confiscateInventory = builder
                .comment(
                    "Kidnappers confiscate captive's inventory when imprisoned"
                )
                .define("confiscateInventory", true);
            builder.pop();

            builder.push("Ransom");
            enableRansom = builder
                .comment("Enable ransom demands for captured players")
                .define("enableRansom", true);
            ransomAmount = builder
                .comment("Default ransom amount")
                .defineInRange("ransomAmount", 16, 1, 64);
            ransomItem = builder
                .comment("Item ID used for ransom payment")
                .define("ransomItem", "minecraft:iron_ingot");
            ransomTimeoutMinutes = builder
                .comment("Time in minutes before ransom demand expires")
                .defineInRange("ransomTimeoutMinutes", 30, 5, 120);
            builder.pop();

            builder.push("Timeout");
            maxCaptivityMinutes = builder
                .comment(
                    "Maximum time in minutes a player can be held captive (0 = unlimited)"
                )
                .defineInRange("maxCaptivityMinutes", 45, 0, 240);
            releaseWithInventory = builder
                .comment(
                    "Return player's inventory when released after timeout"
                )
                .define("releaseWithInventory", true);
            laborRestSeconds = builder
                .comment(
                    "Rest time in seconds between labor tasks for imprisoned players"
                )
                .defineInRange("laborRestSeconds", 120, 10, 600);
            builder.pop();

            builder.push("Struggle");
            struggleMakesNoise = builder
                .comment("Struggling makes noise that alerts nearby kidnappers")
                .define("struggleMakesNoise", true);
            builder.pop();

            builder.push("AI");
            enablePunishment = builder
                .comment(
                    "Kidnappers punish recaptured escapees (tighter binds, add blindfold)"
                )
                .define("enablePunishment", true);
            builder.pop();

            builder.push("Abandon");
            abandonTeleportRadius = builder
                .comment(
                    "Radius in blocks for random teleport when abandoning captive"
                )
                .defineInRange("abandonTeleportRadius", 1000, 100, 5000);
            abandonKeepsBlindfold = builder
                .comment("Captive keeps blindfold when abandoned")
                .define("abandonKeepsBlindfold", true);
            abandonKeepsBinds = builder
                .comment("Captive keeps binds when abandoned")
                .define("abandonKeepsBinds", true);
            builder.pop();

            builder.push("SoloMode");
            enableSoloFallback = builder
                .comment(
                    "Enable fallback behavior when no buyers are available in singleplayer"
                )
                .define("enableSoloFallback", true);
            soloKeepChance = builder
                .comment(
                    "Chance (0.0-1.0) that kidnapper keeps the captive in solo mode"
                )
                .defineInRange("soloKeepChance", 0.6, 0.0, 1.0);
            soloAbandonChance = builder
                .comment(
                    "Chance (0.0-1.0) that kidnapper abandons the captive in solo mode (uses remaining if keep fails)"
                )
                .defineInRange("soloAbandonChance", 0.4, 0.0, 1.0);
            soloTimeoutSeconds = builder
                .comment("Time in seconds before solo mode fallback triggers")
                .defineInRange("soloTimeoutSeconds", 120, 30, 600);
            builder.pop();

            builder.push("MasterNPC");
            enableMasterSpawn = builder
                .comment(
                    "Enable Master NPC spawning in solo mode (pet play system)"
                )
                .define("enableMasterSpawn", true);
            masterPetCollarResistance = builder
                .comment(
                    "Resistance value for pet collar (higher = harder to escape)"
                )
                .defineInRange("petCollarResistance", 250, 50, 1000);
            masterSlaveDamageMultiplier = builder
                .comment(
                    "Damage multiplier for slave attacking master (0.25 = 75% reduction)"
                )
                .defineInRange("slaveDamageMultiplier", 0.25, 0.0, 1.0);
            masterMinDistractionInterval = builder
                .comment(
                    "Minimum time in ticks between distractions (2400 = 2 minutes)"
                )
                .defineInRange("minDistractionInterval", 2400, 600, 12000);
            masterMaxDistractionInterval = builder
                .comment(
                    "Maximum time in ticks between distractions (6000 = 5 minutes)"
                )
                .defineInRange("maxDistractionInterval", 6000, 1200, 24000);
            masterMinDistractionDuration = builder
                .comment(
                    "Minimum distraction duration in ticks (600 = 30 seconds)"
                )
                .defineInRange("minDistractionDuration", 600, 200, 2400);
            masterMaxDistractionDuration = builder
                .comment(
                    "Maximum distraction duration in ticks (1200 = 60 seconds)"
                )
                .defineInRange("maxDistractionDuration", 1200, 400, 4800);
            builder.pop();

            builder.pop(); // KidnapperAdvanced

            builder.push("Cells");
            cellMaxWallDistance = builder
                .comment(
                    "Maximum distance in blocks from cell center to wall markers"
                )
                .defineInRange("maxWallDistance", 32, 8, 64);
            maxPrisonersPerCell = builder
                .comment("Maximum number of prisoners per cell")
                .defineInRange("maxPrisonersPerCell", 1, 1, 4);
            deleteMarkerOnCellDelete = builder
                .comment("Delete marker blocks when a cell is deleted")
                .define("deleteMarkerOnCellDelete", true);
            autoRemoveDestroyedWalls = builder
                .comment(
                    "Automatically remove wall references when wall blocks are destroyed"
                )
                .define("autoRemoveDestroyedWalls", true);
            builder.pop();
        }

        private void addResistance(
            ForgeConfigSpec.Builder builder,
            String name,
            int defaultVal
        ) {
            bindResistances.put(
                name,
                builder.defineInRange(name, defaultVal, 1, 1000)
            );
        }

        private void addGagProps(
            ForgeConfigSpec.Builder builder,
            String name,
            double comp,
            double range
        ) {
            builder.push(name);
            gagComprehension.put(
                name,
                builder.defineInRange("comprehension", comp, 0.0, 1.0)
            );
            gagRange.put(
                name,
                builder.defineInRange("range", range, 0.0, 100.0)
            );
            builder.pop();
        }
    }

    public static class ClientConfig {

        public final ForgeConfigSpec.DoubleValue earplugVolumeMultiplier;
        public final ForgeConfigSpec.DoubleValue blindfoldOverlayOpacity;
        public final ForgeConfigSpec.BooleanValue hardcoreBlindfold;

        // Dynamic Textures (Clothes System)
        public final ForgeConfigSpec.BooleanValue enableDynamicTextures;
        public final ForgeConfigSpec.BooleanValue useTextureHostWhitelist;
        public final ForgeConfigSpec.ConfigValue<
            List<? extends String>
        > textureHostWhitelist;

        // Cell Rendering
        public final ForgeConfigSpec.BooleanValue showCellHighlights;
        public final ForgeConfigSpec.IntValue highlightRenderDistance;

        public ClientConfig(ForgeConfigSpec.Builder builder) {
            builder.push("Audio");
            earplugVolumeMultiplier = builder
                .comment(
                    "Volume multiplier when wearing earplugs (0.0 = silent)"
                )
                .defineInRange("earplugVolumeMultiplier", 0.15, 0.0, 1.0);
            builder.pop();

            builder.push("Visuals");
            blindfoldOverlayOpacity = builder
                .comment(
                    "Opacity of the blindfold overlay (currently unused, texture is fixed)"
                )
                .defineInRange("blindfoldOverlayOpacity", 1.0, 0.0, 1.0);
            hardcoreBlindfold = builder
                .comment(
                    "Hardcore blindfold mode: when ON, blindfold covers everything including menus (100% opacity). When OFF, blindfold is hidden when a GUI screen is open."
                )
                .define("hardcoreBlindfold", true);
            builder.pop();

            builder.push("DynamicTextures");
            enableDynamicTextures = builder
                .comment(
                    "Enable loading dynamic textures from URLs for clothes items"
                )
                .define("enableDynamicTextures", true);
            useTextureHostWhitelist = builder
                .comment(
                    "Only allow texture URLs from whitelisted hosts (security feature)"
                )
                .define("useTextureHostWhitelist", true);
            textureHostWhitelist = builder
                .comment(
                    "Additional allowed hosts for dynamic textures (besides defaults: imgur, discord CDN, github raw)"
                )
                .defineList("additionalWhitelistedHosts", List.of(), obj ->
                    obj instanceof String
                );
            builder.pop();

            builder.push("CellRendering");
            showCellHighlights = builder
                .comment("Show cell boundary highlights when holding cell wand")
                .define("showCellHighlights", true);
            highlightRenderDistance = builder
                .comment(
                    "Maximum render distance for cell highlights in blocks"
                )
                .defineInRange("highlightRenderDistance", 32, 8, 64);
            builder.pop();
        }
    }
}
