package com.tiedup.remake.items.base;

import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.network.sync.SyncManager;
import com.tiedup.remake.state.CollarRegistry;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.util.teleport.Position;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;

/**
 * Base class for collar items (classic collar, shock collar, GPS collar, etc.)
 * These items mark ownership and can have various special effects.
 *
 * Based on original ItemCollar from 1.12.2
 *
 *
 * Note: Collars have maxStackSize of 1 (unique items)
 */
public abstract class ItemCollar
    extends Item
    implements IBondageItem, ILockable
{

    // NBT Keys - Basic
    private static final String NBT_OWNERS = "owners";
    private static final String NBT_LOCKED = "locked";
    private static final String NBT_LOCKED_BY_KEY_UUID = "lockedByKeyUUID";
    private static final String NBT_NICKNAME = "nickname";
    private static final String NBT_CURRENT_RESISTANCE = "currentResistance";
    private static final String NBT_CURRENT_RESISTANCE_LEGACY =
        "currentresistance";
    private static final String NBT_CAN_BE_STRUGGLED_OUT = "canBeStruggledOut";

    // NBT Keys - Kidnapping Mode
    private static final String NBT_KIDNAPPING_MODE = "kidnappingMode";
    private static final String NBT_TIE_TO_POLE = "tieToPole";
    private static final String NBT_WARN_MASTERS = "warnMasters";
    private static final String NBT_BONDAGE_SERVICE = "bondageservice";
    private static final String NBT_SERVICE_SENTENCE = "servicesentence";

    // NBT Keys - Blacklist/Whitelist
    private static final String NBT_BLACKLIST = "blacklist";
    private static final String NBT_WHITELIST = "whitelist";

    // NBT Keys - Cell System
    private static final String NBT_CELL_ID = "cellId";

    /**
     * ThreadLocal flag to suppress alert on legitimate collar removals.
     * Set this to true before removing a collar legitimately (camp death, ransom paid, etc.)
     * and reset to false afterward.
     *
     * CRITICAL FIX: Use runWithSuppressedAlert() wrapper to ensure proper cleanup.
     */
    private static final ThreadLocal<Boolean> SUPPRESS_REMOVAL_ALERT =
        ThreadLocal.withInitial(() -> false);

    /**
     * CRITICAL FIX: Safe wrapper for legitimate collar removals.
     * Automatically handles ThreadLocal lifecycle to prevent memory leaks.
     *
     * Use this instead of manual beginLegitimateRemoval() / endLegitimateRemoval().
     *
     * @param action The action to perform with suppressed alerts
     */
    public static void runWithSuppressedAlert(Runnable action) {
        SUPPRESS_REMOVAL_ALERT.set(true);
        try {
            action.run();
        } finally {
            SUPPRESS_REMOVAL_ALERT.set(false);
        }
    }

    /**
     * DEPRECATED: Use runWithSuppressedAlert() instead.
     * Begin a legitimate collar removal that should NOT alert kidnappers.
     * @deprecated Use {@link #runWithSuppressedAlert(Runnable)} to prevent ThreadLocal leaks
     */
    @Deprecated
    public static void beginLegitimateRemoval() {
        SUPPRESS_REMOVAL_ALERT.set(true);
    }

    /**
     * DEPRECATED: Use runWithSuppressedAlert() instead.
     * End a legitimate collar removal sequence.
     * @deprecated Use {@link #runWithSuppressedAlert(Runnable)} to prevent ThreadLocal leaks
     */
    @Deprecated
    public static void endLegitimateRemoval() {
        SUPPRESS_REMOVAL_ALERT.set(false);
    }

    /**
     * Check if removal alerts are currently suppressed.
     */
    public static boolean isRemovalAlertSuppressed() {
        return SUPPRESS_REMOVAL_ALERT.get();
    }

    public ItemCollar(Properties properties) {
        super(properties.stacksTo(1)); // Collars are unique items
    }

    @Override
    public void appendHoverText(
        ItemStack stack,
        @Nullable Level level,
        List<Component> tooltip,
        TooltipFlag flag
    ) {
        // Nickname
        if (hasNickname(stack)) {
            tooltip.add(
                Component.literal("Nickname: ")
                    .withStyle(ChatFormatting.AQUA)
                    .append(
                        Component.literal(getNickname(stack)).withStyle(
                            ChatFormatting.WHITE
                        )
                    )
            );
        }

        // Locked status
        if (isLocked(stack)) {
            tooltip.add(
                Component.literal("Locked").withStyle(ChatFormatting.RED)
            );
        }

        // Kidnapping mode
        if (isKidnappingModeEnabled(stack)) {
            tooltip.add(
                Component.literal("Kidnapping Mode: ON").withStyle(
                    ChatFormatting.DARK_RED
                )
            );
        }

        // Additional flags
        if (shouldTieToPole(stack)) {
            tooltip.add(
                Component.literal("Tie to Pole: ON").withStyle(
                    ChatFormatting.GRAY
                )
            );
        }
        if (isBondageServiceEnabled(stack)) {
            tooltip.add(
                Component.literal("Bondage Service: ON").withStyle(
                    ChatFormatting.LIGHT_PURPLE
                )
            );
        }

        // Cell assignment info
        UUID cellId = getCellId(stack);
        if (cellId != null) {
            tooltip.add(
                Component.literal("Cell: ")
                    .withStyle(ChatFormatting.DARK_PURPLE)
                    .append(
                        Component.literal(
                            cellId.toString().substring(0, 8) + "..."
                        ).withStyle(ChatFormatting.LIGHT_PURPLE)
                    )
            );
        }
    }

    public String getNickname(ItemStack stack) {
        CompoundTag tag = stack.getTag();
        if (tag != null && tag.contains(NBT_NICKNAME)) {
            return tag.getString(NBT_NICKNAME);
        }
        return null;
    }

    public void setNickname(ItemStack stack, String nickname) {
        stack.getOrCreateTag().putString(NBT_NICKNAME, nickname);
    }

    public boolean hasNickname(ItemStack stack) {
        return stack.hasTag() && stack.getTag().contains(NBT_NICKNAME);
    }

    @Override
    public BodyRegionV2 getBodyRegion() {
        return BodyRegionV2.NECK;
    }

    /**
     * Get the item name for GameRule lookup.
     * Used by SettingsAccessor to find resistance value.
     *
     * @return "collar"
     */
    public String getItemName() {
        return "collar";
    }

    /**
     * Check if this collar can shock the wearer.
     * Override in shock collar subclasses.
     *
     * @return true if collar has shock capability
     */
    public boolean canShock() {
        return false;
    }

    /**
     * Check if this collar has GPS tracking.
     * Override in GPS collar subclasses.
     *
     * @return true if collar has GPS capability
     */
    public boolean hasGPS() {
        return false;
    }

    /**
     * Called when player right-clicks another entity with this collar.
     * Allows putting collar on tied-up entities (Players and NPCs) and adds the player as owner.
     *
     * Based on original ItemCollar.itemInteractionForEntity()
     *
     * @param stack The item stack
     * @param player The player using the item
     * @param target The entity being interacted with
     * @param hand The hand holding the item
     * @return SUCCESS if collar equipped/replaced, PASS otherwise
     */
    @Override
    public InteractionResult interactLivingEntity(
        ItemStack stack,
        Player player,
        LivingEntity target,
        InteractionHand hand
    ) {
        // Server-side only
        if (player.level().isClientSide) {
            return InteractionResult.SUCCESS;
        }

        // Check if target can be collared (Player, EntityDamsel, EntityKidnapper)
        IBondageState targetState = KidnappedHelper.getKidnappedState(target);
        if (targetState == null) {
            return InteractionResult.PASS; // Entity cannot be collared
        }

        // Must be tied up
        if (!targetState.isTiedUp()) {
            return InteractionResult.PASS;
        }

        ItemStack newCollar = stack.copy();
        if (!isOwner(newCollar, player)) {
            addOwner(newCollar, player);
        }

        // Case 1: No collar yet - equip new one
        if (!targetState.hasCollar()) {
            targetState.equip(BodyRegionV2.NECK, newCollar);
            stack.shrink(1);

            registerCollarInRegistry(target, newCollar, player);

            // Send screen message to target
            SystemMessageManager.sendToTarget(
                player,
                target,
                SystemMessageManager.MessageCategory.COLLARED
            );

            // Sync equipment to all tracking clients
            if (target instanceof ServerPlayer serverPlayer) {
                SyncManager.syncInventory(serverPlayer);
            }

            TiedUpMod.LOGGER.info(
                "[ItemCollar] {} put collar on {}",
                player.getName().getString(),
                target.getName().getString()
            );

            // Play custom put sound
            player
                .level()
                .playSound(
                    null,
                    player.blockPosition(),
                    com.tiedup.remake.core.ModSounds.COLLAR_PUT.get(),
                    net.minecraft.sounds.SoundSource.PLAYERS,
                    1.0f,
                    1.0f
                );

            return InteractionResult.SUCCESS;
        }
        // Case 2: Already has collar - replace it
        else {
            ItemStack oldCollar = targetState.getEquipment(BodyRegionV2.NECK);
            if (
                oldCollar != null && oldCollar.getItem() instanceof ItemCollar
            ) {
                ItemCollar oldCollarItem = (ItemCollar) oldCollar.getItem();

                // Check if old collar is locked
                if (oldCollarItem.isLocked(oldCollar)) {
                    // Cannot replace locked collar
                    TiedUpMod.LOGGER.info(
                        "[ItemCollar] {} tried to replace locked collar on {}",
                        player.getName().getString(),
                        target.getName().getString()
                    );

                    // Send error message to player (kidnapper)
                    SystemMessageManager.sendToPlayer(
                        player,
                        SystemMessageManager.MessageCategory.ERROR,
                        "Target's collar is locked! You cannot replace it."
                    );
                    return InteractionResult.FAIL;
                }

                // Old collar not locked - replace it
                ItemStack replacedCollar = targetState.replaceEquipment(
                    BodyRegionV2.NECK,
                    newCollar,
                    false
                );
                if (replacedCollar != null) {
                    stack.shrink(1);
                    targetState.kidnappedDropItem(replacedCollar);

                    unregisterCollarFromRegistry(target);
                    registerCollarInRegistry(target, newCollar, player);

                    // Send screen message to target
                    SystemMessageManager.sendToTarget(
                        player,
                        target,
                        SystemMessageManager.MessageCategory.COLLARED
                    );

                    // Sync equipment to all tracking clients
                    if (target instanceof ServerPlayer serverPlayer) {
                        SyncManager.syncInventory(serverPlayer);
                    }

                    TiedUpMod.LOGGER.info(
                        "[ItemCollar] {} replaced collar on {}",
                        player.getName().getString(),
                        target.getName().getString()
                    );

                    return InteractionResult.SUCCESS;
                }
            }
        }

        return InteractionResult.PASS;
    }

    /**
     * Add an owner to this collar.
     * Owners are stored as UUID + name pairs in NBT.
     *
     * From original ItemCollar.addOwner()
     *
     * @param stack The collar ItemStack
     * @param ownerUUID The owner's UUID
     * @param ownerName The owner's display name
     */
    public void addOwner(ItemStack stack, UUID ownerUUID, String ownerName) {
        if (stack.isEmpty() || ownerUUID == null) {
            return;
        }

        CompoundTag tag = stack.getOrCreateTag();
        ListTag owners = tag.contains(NBT_OWNERS)
            ? tag.getList(NBT_OWNERS, Tag.TAG_COMPOUND)
            : new ListTag();

        // Check if already owner
        for (int i = 0; i < owners.size(); i++) {
            CompoundTag ownerTag = owners.getCompound(i);
            if (ownerTag.getUUID("uuid").equals(ownerUUID)) {
                TiedUpMod.LOGGER.debug(
                    "[ItemCollar] {} is already an owner",
                    ownerName
                );
                return;
            }
        }

        // Add new owner
        CompoundTag ownerTag = new CompoundTag();
        ownerTag.putUUID("uuid", ownerUUID);
        ownerTag.putString("name", ownerName != null ? ownerName : "Unknown");
        owners.add(ownerTag);
        tag.put(NBT_OWNERS, owners);

        TiedUpMod.LOGGER.info("[ItemCollar] Added {} as owner", ownerName);
    }

    /**
     * Add an owner to this collar (player version).
     *
     * @param stack The collar ItemStack
     * @param owner The owner player
     */
    public void addOwner(ItemStack stack, Player owner) {
        if (owner == null) {
            return;
        }
        addOwner(stack, owner.getUUID(), owner.getName().getString());
    }

    /**
     * Remove an owner from this collar.
     *
     * @param stack The collar ItemStack
     * @param ownerUUID The owner's UUID to remove
     */
    public void removeOwner(ItemStack stack, UUID ownerUUID) {
        if (stack.isEmpty() || ownerUUID == null) {
            return;
        }

        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.contains(NBT_OWNERS)) {
            return;
        }

        ListTag owners = tag.getList(NBT_OWNERS, Tag.TAG_COMPOUND);
        ListTag newOwners = new ListTag();

        for (int i = 0; i < owners.size(); i++) {
            CompoundTag ownerTag = owners.getCompound(i);
            if (!ownerTag.getUUID("uuid").equals(ownerUUID)) {
                newOwners.add(ownerTag);
            }
        }

        tag.put(NBT_OWNERS, newOwners);
        TiedUpMod.LOGGER.info("[ItemCollar] Removed owner {}", ownerUUID);
    }

    /**
     * Get the list of all owners.
     *
     * @param stack The collar ItemStack
     * @return List of owner UUIDs
     */
    public List<UUID> getOwners(ItemStack stack) {
        if (stack.isEmpty()) {
            return new ArrayList<>();
        }

        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.contains(NBT_OWNERS)) {
            return new ArrayList<>();
        }

        ListTag owners = tag.getList(NBT_OWNERS, Tag.TAG_COMPOUND);
        List<UUID> result = new ArrayList<>();

        for (int i = 0; i < owners.size(); i++) {
            result.add(owners.getCompound(i).getUUID("uuid"));
        }

        return result;
    }

    /**
     * Check if the given player is an owner of this collar.
     * Optimized: uses direct NBT lookup with early-return instead of building list.
     *
     * @param stack The collar ItemStack
     * @param player The player to check
     * @return true if player is an owner
     */
    public boolean isOwner(ItemStack stack, Player player) {
        if (player == null) {
            return false;
        }
        return hasUUIDInList(stack, NBT_OWNERS, player.getUUID());
    }

    /**
     * Check if this collar has any owners.
     *
     * @param stack The collar ItemStack
     * @return true if has at least one owner
     */
    public boolean hasOwner(ItemStack stack) {
        return !getOwners(stack).isEmpty();
    }

    /**
     * Add a player to this collar's blacklist.
     * Blacklisted players will not be targeted when kidnapping mode is active.
     *
     * @param stack The collar ItemStack
     * @param uuid The player's UUID
     * @param name The player's display name
     */
    public void addToBlacklist(ItemStack stack, UUID uuid, String name) {
        addToList(stack, NBT_BLACKLIST, uuid, name);
    }

    /**
     * Add a player to this collar's blacklist (player version).
     */
    public void addToBlacklist(ItemStack stack, Player player) {
        if (player == null) return;
        addToBlacklist(stack, player.getUUID(), player.getName().getString());
    }

    /**
     * Remove a player from this collar's blacklist.
     */
    public void removeFromBlacklist(ItemStack stack, UUID uuid) {
        removeFromList(stack, NBT_BLACKLIST, uuid);
    }

    /**
     * Get all blacklisted player UUIDs.
     */
    public List<UUID> getBlacklist(ItemStack stack) {
        return getListUUIDs(stack, NBT_BLACKLIST);
    }

    /**
     * Check if a player is blacklisted.
     * Optimized: uses direct NBT lookup with early-return instead of building list.
     */
    public boolean isBlacklisted(ItemStack stack, UUID uuid) {
        return hasUUIDInList(stack, NBT_BLACKLIST, uuid);
    }

    /**
     * Check if a player is blacklisted (player version).
     */
    public boolean isBlacklisted(ItemStack stack, Player player) {
        return player != null && isBlacklisted(stack, player.getUUID());
    }

    /**
     * Add a player to this collar's whitelist.
     * When whitelist is not empty, ONLY whitelisted players will be targeted.
     *
     * @param stack The collar ItemStack
     * @param uuid The player's UUID
     * @param name The player's display name
     */
    public void addToWhitelist(ItemStack stack, UUID uuid, String name) {
        addToList(stack, NBT_WHITELIST, uuid, name);
    }

    /**
     * Add a player to this collar's whitelist (player version).
     */
    public void addToWhitelist(ItemStack stack, Player player) {
        if (player == null) return;
        addToWhitelist(stack, player.getUUID(), player.getName().getString());
    }

    /**
     * Remove a player from this collar's whitelist.
     */
    public void removeFromWhitelist(ItemStack stack, UUID uuid) {
        removeFromList(stack, NBT_WHITELIST, uuid);
    }

    /**
     * Get all whitelisted player UUIDs.
     */
    public List<UUID> getWhitelist(ItemStack stack) {
        return getListUUIDs(stack, NBT_WHITELIST);
    }

    /**
     * Check if a player is whitelisted.
     * Optimized: uses direct NBT lookup with early-return instead of building list.
     */
    public boolean isWhitelisted(ItemStack stack, UUID uuid) {
        return hasUUIDInList(stack, NBT_WHITELIST, uuid);
    }

    /**
     * Check if a player is whitelisted (player version).
     */
    public boolean isWhitelisted(ItemStack stack, Player player) {
        return player != null && isWhitelisted(stack, player.getUUID());
    }

    // ========== Helper methods for list management ==========

    /**
     * Check if a UUID exists in a specific list (owners, blacklist, whitelist).
     * Optimized O(n) with early-return instead of O(2n) from building list + contains.
     *
     * @param stack The collar ItemStack
     * @param listKey The NBT key for the list
     * @param uuid The UUID to check
     * @return true if UUID is in the list
     */
    private boolean hasUUIDInList(ItemStack stack, String listKey, UUID uuid) {
        if (stack.isEmpty() || uuid == null) return false;

        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.contains(listKey)) return false;

        ListTag list = tag.getList(listKey, Tag.TAG_COMPOUND);
        for (int i = 0; i < list.size(); i++) {
            if (list.getCompound(i).getUUID("uuid").equals(uuid)) {
                return true; // Early return - no need to build full list
            }
        }
        return false;
    }

    private void addToList(
        ItemStack stack,
        String listKey,
        UUID uuid,
        String name
    ) {
        if (stack.isEmpty() || uuid == null) return;

        CompoundTag tag = stack.getOrCreateTag();
        ListTag list = tag.contains(listKey)
            ? tag.getList(listKey, Tag.TAG_COMPOUND)
            : new ListTag();

        // Check if already in list
        for (int i = 0; i < list.size(); i++) {
            CompoundTag entry = list.getCompound(i);
            if (entry.getUUID("uuid").equals(uuid)) {
                return; // Already in list
            }
        }

        // Add new entry
        CompoundTag entry = new CompoundTag();
        entry.putUUID("uuid", uuid);
        entry.putString("name", name != null ? name : "Unknown");
        list.add(entry);
        tag.put(listKey, list);
    }

    private void removeFromList(ItemStack stack, String listKey, UUID uuid) {
        if (stack.isEmpty() || uuid == null) return;

        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.contains(listKey)) return;

        ListTag list = tag.getList(listKey, Tag.TAG_COMPOUND);
        ListTag newList = new ListTag();

        for (int i = 0; i < list.size(); i++) {
            CompoundTag entry = list.getCompound(i);
            if (!entry.getUUID("uuid").equals(uuid)) {
                newList.add(entry);
            }
        }

        tag.put(listKey, newList);
    }

    private List<UUID> getListUUIDs(ItemStack stack, String listKey) {
        if (stack.isEmpty()) return new ArrayList<>();

        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.contains(listKey)) return new ArrayList<>();

        ListTag list = tag.getList(listKey, Tag.TAG_COMPOUND);
        List<UUID> result = new ArrayList<>();

        for (int i = 0; i < list.size(); i++) {
            result.add(list.getCompound(i).getUUID("uuid"));
        }

        return result;
    }

    /**
     * Check if this collar is locked.
     * Locked collars cannot be removed normally.
     *
     *
     * @param stack The collar ItemStack
     * @return true if locked
     */
    @Override
    public boolean isLocked(ItemStack stack) {
        if (stack.isEmpty()) {
            return false;
        }
        CompoundTag tag = stack.getTag();
        return tag != null && tag.getBoolean(NBT_LOCKED);
    }

    /**
     * Set the locked state of this collar.
     *
     *
     * @param stack The collar ItemStack
     * @param locked true to lock, false to unlock
     * @return The modified ItemStack for chaining
     */
    @Override
    public ItemStack setLocked(ItemStack stack, boolean locked) {
        if (stack.isEmpty()) {
            return stack;
        }
        stack.getOrCreateTag().putBoolean(NBT_LOCKED, locked);
        TiedUpMod.LOGGER.debug("[ItemCollar] Set locked={}", locked);
        return stack;
    }

    /**
     * Check if this collar can be locked.
     * By default, all collars are lockable.
     *
     *
     * @param stack The collar ItemStack
     * @return true if lockable (can accept a padlock)
     */
    @Override
    public boolean isLockable(ItemStack stack) {
        if (stack.isEmpty()) {
            return false;
        }
        CompoundTag tag = stack.getTag();
        // By default, collars are lockable unless explicitly set to false
        return (
            tag == null ||
            !tag.contains("lockable") ||
            tag.getBoolean("lockable")
        );
    }

    /**
     * Set whether this collar can be locked (lockable state).
     *
     *
     * @param stack The collar ItemStack
     * @param state true to make lockable, false to prevent locking
     * @return The modified ItemStack for chaining
     */
    @Override
    public ItemStack setLockable(ItemStack stack, boolean state) {
        if (stack.isEmpty()) {
            return stack;
        }
        stack.getOrCreateTag().putBoolean("lockable", state);
        return stack;
    }

    /**
     * Check if the padlock should be dropped when unlocking.
     * Collars have a built-in lock mechanism, so no padlock to drop.
     *
     *
     * @return false (no padlock to drop for collars)
     */
    @Override
    public boolean dropLockOnUnlock() {
        return false;
    }

    // ========== Key-Lock System ==========

    /**
     * Get the UUID of the key that locked this collar.
     *
     * @param stack The collar ItemStack
     * @return The key UUID or null if not locked with a key
     */
    @Override
    @Nullable
    public UUID getLockedByKeyUUID(ItemStack stack) {
        if (stack.isEmpty()) return null;
        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.hasUUID(NBT_LOCKED_BY_KEY_UUID)) return null;
        return tag.getUUID(NBT_LOCKED_BY_KEY_UUID);
    }

    /**
     * Set the key UUID that locks this collar.
     * Setting a non-null UUID will also set locked=true.
     * Setting null will unlock the collar.
     *
     * @param stack The collar ItemStack
     * @param keyUUID The key UUID or null to unlock
     */
    @Override
    public void setLockedByKeyUUID(ItemStack stack, @Nullable UUID keyUUID) {
        if (stack.isEmpty()) return;
        CompoundTag tag = stack.getOrCreateTag();
        if (keyUUID == null) {
            tag.remove(NBT_LOCKED_BY_KEY_UUID);
            tag.putBoolean(NBT_LOCKED, false);
        } else {
            tag.putUUID(NBT_LOCKED_BY_KEY_UUID, keyUUID);
            tag.putBoolean(NBT_LOCKED, true);
        }
    }

    /**
     * Get the base resistance for this collar from config.
     *
     * BUG-003 fix: Now reads from SettingsAccessor (config) instead of GameRules
     *
     * @param entity The entity (kept for API compatibility)
     * @return Base resistance value from config
     */
    public int getBaseResistance(LivingEntity entity) {
        return SettingsAccessor.getBindResistance(getItemName());
    }

    /**
     * Get the current resistance of this collar.
     * Returns base resistance if not yet set.
     *
     *
     * @param stack The collar ItemStack
     * @param entity The entity (for GameRules lookup)
     * @return Current resistance
     */
    public int getCurrentResistance(ItemStack stack, LivingEntity entity) {
        if (stack.isEmpty()) {
            return 0;
        }

        CompoundTag tag = stack.getTag();
        if (tag != null) {
            // Check new camelCase key first
            if (tag.contains(NBT_CURRENT_RESISTANCE)) {
                return tag.getInt(NBT_CURRENT_RESISTANCE);
            }
            // Migration: check legacy lowercase key
            else if (tag.contains(NBT_CURRENT_RESISTANCE_LEGACY)) {
                int resistance = tag.getInt(NBT_CURRENT_RESISTANCE_LEGACY);
                // Migrate to new key
                tag.remove(NBT_CURRENT_RESISTANCE_LEGACY);
                if (resistance > 0) {
                    tag.putInt(NBT_CURRENT_RESISTANCE, resistance);
                }
                return resistance > 0 ? resistance : getBaseResistance(entity);
            }
        }

        // Not set yet - return base resistance
        return getBaseResistance(entity);
    }

    /**
     * Set the current resistance of this collar.
     *
     * @param stack The collar ItemStack
     * @param resistance The new resistance value
     */
    public void setCurrentResistance(ItemStack stack, int resistance) {
        if (stack.isEmpty()) {
            return;
        }
        stack.getOrCreateTag().putInt(NBT_CURRENT_RESISTANCE, resistance);
    }

    /**
     * Reset the current resistance to base value.
     *
     *
     * @param stack The collar ItemStack
     * @param entity The entity (for GameRules lookup)
     */
    public void resetCurrentResistance(ItemStack stack, LivingEntity entity) {
        setCurrentResistance(stack, getBaseResistance(entity));
    }

    /**
     * Check if this collar can be struggled out of.
     *
     * @param stack The collar ItemStack
     * @return true if struggle is enabled
     */
    public boolean canBeStruggledOut(ItemStack stack) {
        if (stack.isEmpty()) {
            return false;
        }

        CompoundTag tag = stack.getTag();
        if (tag != null && tag.contains(NBT_CAN_BE_STRUGGLED_OUT)) {
            return tag.getBoolean(NBT_CAN_BE_STRUGGLED_OUT);
        }

        return true; // Default: can struggle
    }

    /**
     * Set whether this collar can be struggled out of.
     *
     * @param stack The collar ItemStack
     * @param canStruggle true to enable struggle
     */
    public void setCanBeStruggledOut(ItemStack stack, boolean canStruggle) {
        if (stack.isEmpty()) {
            return;
        }
        stack
            .getOrCreateTag()
            .putBoolean(NBT_CAN_BE_STRUGGLED_OUT, canStruggle);
    }

    // Cell ID (Assigned Cell)

    /**
     * Get the assigned cell ID from this collar.
     *
     * @param stack The collar ItemStack
     * @return Cell UUID or null if not assigned
     */
    @Nullable
    public UUID getCellId(ItemStack stack) {
        if (stack.isEmpty()) {
            return null;
        }

        CompoundTag tag = stack.getTag();
        if (tag == null || !tag.hasUUID(NBT_CELL_ID)) {
            return null;
        }

        return tag.getUUID(NBT_CELL_ID);
    }

    /**
     * Set the assigned cell ID on this collar.
     *
     * @param stack The collar ItemStack
     * @param cellId The cell UUID, or null to clear
     */
    public void setCellId(ItemStack stack, @Nullable UUID cellId) {
        if (stack.isEmpty()) {
            return;
        }

        CompoundTag tag = stack.getOrCreateTag();
        if (cellId == null) {
            tag.remove(NBT_CELL_ID);
        } else {
            tag.putUUID(NBT_CELL_ID, cellId);
        }
    }

    /**
     * Check if this collar has a cell assigned.
     *
     * @param stack The collar ItemStack
     * @return true if a cell is assigned
     */
    public boolean hasCellAssigned(ItemStack stack) {
        return getCellId(stack) != null;
    }

    // Kidnapping Mode

    /**
     * Check if kidnapping mode is enabled.
     * Kidnapping mode allows NPC kidnappers to auto-capture and teleport to prison.
     *
     * @param stack The collar ItemStack
     * @return true if kidnapping mode enabled
     */
    public boolean isKidnappingModeEnabled(ItemStack stack) {
        if (stack.isEmpty()) {
            return false;
        }

        CompoundTag tag = stack.getTag();
        return tag != null && tag.getBoolean(NBT_KIDNAPPING_MODE);
    }

    /**
     * Set kidnapping mode state.
     *
     * @param stack The collar ItemStack
     * @param enabled true to enable kidnapping mode
     */
    public void setKidnappingModeEnabled(ItemStack stack, boolean enabled) {
        if (stack.isEmpty()) {
            return;
        }

        stack.getOrCreateTag().putBoolean(NBT_KIDNAPPING_MODE, enabled);
        TiedUpMod.LOGGER.info(
            "[ItemCollar] Kidnapping mode set to {}",
            enabled
        );
    }

    /**
     * Check if kidnapping mode is fully configured and ready.
     * Requires: kidnapping mode ON + cell assigned
     *
     * @param stack The collar ItemStack
     * @return true if ready for automated kidnapping
     */
    public boolean isKidnappingModeReady(ItemStack stack) {
        if (!isKidnappingModeEnabled(stack)) return false;
        return hasCellAssigned(stack);
    }

    // Tie to Pole (Auto-tie slave in cell)

    /**
     * Check if slave should be auto-tied to nearest pole in prison.
     *
     * @param stack The collar ItemStack
     * @return true if should tie to pole
     */
    public boolean shouldTieToPole(ItemStack stack) {
        if (stack.isEmpty()) {
            return false;
        }

        CompoundTag tag = stack.getTag();
        return tag != null && tag.getBoolean(NBT_TIE_TO_POLE);
    }

    /**
     * Set whether slave should be tied to pole in prison.
     *
     * @param stack The collar ItemStack
     * @param enabled true to enable tie to pole
     */
    public void setTieToPole(ItemStack stack, boolean enabled) {
        if (stack.isEmpty()) {
            return;
        }

        stack.getOrCreateTag().putBoolean(NBT_TIE_TO_POLE, enabled);
    }

    // Warn Masters

    /**
     * Check if owners should be warned when slave is captured.
     *
     * @param stack The collar ItemStack
     * @return true if should warn masters
     */
    public boolean shouldWarnMasters(ItemStack stack) {
        if (stack.isEmpty()) {
            return true; // Default: warn
        }

        CompoundTag tag = stack.getTag();
        if (tag != null && tag.contains(NBT_WARN_MASTERS)) {
            return tag.getBoolean(NBT_WARN_MASTERS);
        }

        return true; // Default: warn
    }

    /**
     * Set whether owners should be warned.
     *
     * @param stack The collar ItemStack
     * @param enabled true to enable warnings
     */
    public void setWarnMasters(ItemStack stack, boolean enabled) {
        if (stack.isEmpty()) {
            return;
        }

        stack.getOrCreateTag().putBoolean(NBT_WARN_MASTERS, enabled);
    }

    // Bondage Service

    /**
     * Check if bondage service is enabled on this collar.
     * Bondage service allows a Damsel to automatically capture attacking players.
     *
     * @param stack The collar ItemStack
     * @return true if bondage service is enabled
     */
    public boolean isBondageServiceEnabled(ItemStack stack) {
        if (stack.isEmpty()) {
            return false;
        }

        CompoundTag tag = stack.getTag();
        return tag != null && tag.getBoolean(NBT_BONDAGE_SERVICE);
    }

    /**
     * Enable or disable bondage service on this collar.
     *
     * @param stack The collar ItemStack
     * @param enabled true to enable bondage service
     */
    public void setBondageServiceEnabled(ItemStack stack, boolean enabled) {
        if (stack.isEmpty()) {
            return;
        }

        stack.getOrCreateTag().putBoolean(NBT_BONDAGE_SERVICE, enabled);
    }

    /**
     * Get the custom service sentence message.
     *
     * @param stack The collar ItemStack
     * @return Custom message or null if not set
     */
    @Nullable
    public String getServiceSentence(ItemStack stack) {
        if (stack.isEmpty()) {
            return null;
        }

        CompoundTag tag = stack.getTag();
        if (tag != null && tag.contains(NBT_SERVICE_SENTENCE)) {
            return tag.getString(NBT_SERVICE_SENTENCE);
        }
        return null;
    }

    /**
     * Set a custom service sentence message.
     *
     * @param stack The collar ItemStack
     * @param sentence The custom message
     */
    public void setServiceSentence(ItemStack stack, String sentence) {
        if (stack.isEmpty()) {
            return;
        }

        if (sentence == null || sentence.isEmpty()) {
            stack.getOrCreateTag().remove(NBT_SERVICE_SENTENCE);
        } else {
            stack.getOrCreateTag().putString(NBT_SERVICE_SENTENCE, sentence);
        }
    }

    /**
     * Register a collar in the global CollarRegistry.
     * Called when a collar is put on an entity.
     *
     * @param wearer The entity wearing the collar
     * @param collarStack The collar ItemStack
     * @param primaryOwner The player putting the collar on (primary owner)
     */
    private void registerCollarInRegistry(
        LivingEntity wearer,
        ItemStack collarStack,
        Player primaryOwner
    ) {
        if (wearer == null || wearer.level().isClientSide()) {
            return;
        }

        // Get server-side registry
        if (
            !(wearer.level() instanceof
                    net.minecraft.server.level.ServerLevel serverLevel)
        ) {
            return;
        }

        CollarRegistry registry = CollarRegistry.get(serverLevel);
        if (registry == null) {
            return;
        }

        // Get all owners from the collar NBT
        java.util.List<UUID> owners = getOwners(collarStack);
        if (owners.isEmpty() && primaryOwner != null) {
            // If no owners yet, use the player putting the collar on
            owners = java.util.List.of(primaryOwner.getUUID());
        }

        // Register all owners
        for (UUID ownerUUID : owners) {
            registry.registerCollar(wearer.getUUID(), ownerUUID);
        }

        // Sync to affected owners
        syncRegistryToOwners(serverLevel, owners);

        TiedUpMod.LOGGER.debug(
            "[CollarRegistry] Registered {} with {} owners",
            wearer.getName().getString(),
            owners.size()
        );
    }

    /**
     * Unregister a collar from the global CollarRegistry.
     * Called when a collar is removed from an entity.
     *
     * @param wearer The entity whose collar is being removed
     */
    private void unregisterCollarFromRegistry(LivingEntity wearer) {
        if (wearer == null || wearer.level().isClientSide()) {
            return;
        }

        if (
            !(wearer.level() instanceof
                    net.minecraft.server.level.ServerLevel serverLevel)
        ) {
            return;
        }

        CollarRegistry registry = CollarRegistry.get(serverLevel);
        if (registry == null) {
            return;
        }

        // Get owners before unregistering (for sync)
        java.util.Set<UUID> owners = registry.getOwners(wearer.getUUID());

        // Unregister the wearer
        registry.unregisterWearer(wearer.getUUID());

        // Sync to affected owners
        syncRegistryToOwners(serverLevel, owners);

        TiedUpMod.LOGGER.debug(
            "[CollarRegistry] Unregistered {}",
            wearer.getName().getString()
        );
    }

    // ESCAPE DETECTION: Collar Removal Alert

    /**
     * Alert nearby kidnappers when a collar is forcibly removed.
     * This indicates a potential escape attempt.
     *
     * Should be called when:
     * - Player struggles out of a collar
     * - Collar is removed by another player without permission
     * - Collar is broken/picked
     *
     * @param wearer The entity whose collar was removed
     * @param forced Whether the removal was forced (struggle, lockpick, etc.)
     */
    public static void onCollarRemoved(LivingEntity wearer, boolean forced) {
        if (wearer == null || wearer.level().isClientSide()) {
            return;
        }

        if (!forced) {
            return; // Only alert on forced removals
        }

        if (
            !(wearer.level() instanceof
                    net.minecraft.server.level.ServerLevel serverLevel)
        ) {
            return;
        }

        TiedUpMod.LOGGER.info(
            "[ItemCollar] {} collar was forcibly removed - alerting kidnappers",
            wearer.getName().getString()
        );

        // Unregister from CollarRegistry
        CollarRegistry registry = CollarRegistry.get(serverLevel);
        if (registry != null) {
            registry.unregisterWearer(wearer.getUUID());
        }

        // Find and alert nearby kidnappers
        net.minecraft.world.phys.AABB searchBox = wearer
            .getBoundingBox()
            .inflate(50, 20, 50);
        java.util.List<com.tiedup.remake.entities.EntityKidnapper> kidnappers =
            serverLevel.getEntitiesOfClass(
                com.tiedup.remake.entities.EntityKidnapper.class,
                searchBox
            );

        for (com.tiedup.remake.entities.EntityKidnapper kidnapper : kidnappers) {
            // Check if this kidnapper owned the collar
            // For now, alert all kidnappers - they'll check on their own
            if (!kidnapper.hasCaptives() && !kidnapper.isTiedUp()) {
                kidnapper.setAlertTarget(wearer);
                kidnapper.setCurrentState(
                    com.tiedup.remake.entities.ai.kidnapper.KidnapperState.ALERT
                );
                kidnapper.broadcastAlert(wearer);

                TiedUpMod.LOGGER.debug(
                    "[ItemCollar] Alerted kidnapper {} about collar removal",
                    kidnapper.getNpcName()
                );
            }
        }
    }

    /**
     * Sync the CollarRegistry to specific owners.
     *
     * @param level The server level
     * @param ownerUUIDs The owners to sync to
     */
    private void syncRegistryToOwners(
        net.minecraft.server.level.ServerLevel level,
        java.util.Collection<UUID> ownerUUIDs
    ) {
        net.minecraft.server.MinecraftServer server = level.getServer();
        if (server == null) {
            return;
        }

        CollarRegistry registry = CollarRegistry.get(server);
        if (registry == null) {
            return;
        }

        for (UUID ownerUUID : ownerUUIDs) {
            net.minecraft.server.level.ServerPlayer owner = server
                .getPlayerList()
                .getPlayer(ownerUUID);
            if (owner != null) {
                // Send full sync to this owner
                java.util.Set<UUID> slaves = registry.getSlaves(ownerUUID);
                com.tiedup.remake.network.ModNetwork.sendToPlayer(
                    new com.tiedup.remake.network.sync.PacketSyncCollarRegistry(
                        slaves
                    ),
                    owner
                );
            }
        }
    }

    // LIFECYCLE HOOKS

    /**
     * Called when a collar is unequipped from an entity.
     * Triggers escape detection for forced removals.
     *
     * @param stack The unequipped collar
     * @param entity The entity that was wearing the collar
     */
    @Override
    public void onUnequipped(ItemStack stack, LivingEntity entity) {
        // Check if this is a legitimate removal (camp death, ransom paid, etc.)
        if (isRemovalAlertSuppressed()) {
            TiedUpMod.LOGGER.debug(
                "[ItemCollar] Collar removal for {} - alert suppressed (legitimate removal)",
                entity.getName().getString()
            );
            return;
        }

        // Alert kidnappers about the collar removal (forced/escape)
        onCollarRemoved(entity, true);
    }

    // TEXTURE SUBFOLDER

    /**
     * Get the texture subfolder for collar items.
     * Issue #12 fix: Eliminates string checks in renderers.
     */
    @Override
    public String getTextureSubfolder() {
        return "collars";
    }
}
