package com.tiedup.remake.state.components;

import com.tiedup.remake.items.base.ILockable;
import com.tiedup.remake.state.hosts.IPlayerBindStateHost;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.IV2BondageEquipment;
import com.tiedup.remake.v2.bondage.IV2BondageItem;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import java.util.function.Supplier;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;

/**
 * Component responsible for bondage equipment management.
 * Handles putting on, taking off, and replacing all bondage items.
 *
 * Single Responsibility: Equipment lifecycle management
 * Complexity: MEDIUM (V2 equipment coupling)
 * Risk: MEDIUM (core equipment system)
 *
 * Epic 5F: Uses V2EquipmentHelper.
 * Uses low-level setInRegion for equips because force-equip paths (commands, NPC AI,
 * NBT restore) should bypass V2EquipmentManager conflict resolution.
 */
public class PlayerEquipment {

    private final IPlayerBindStateHost host;

    public PlayerEquipment(IPlayerBindStateHost host) {
        this.host = host;
    }

    // ========== Put On Methods ==========

    /** Equips a bind item and applies speed reduction. */
    public void putBindOn(ItemStack bind) {
        equipInRegion(BodyRegionV2.ARMS, bind);
    }

    /** Equips a gag (enables gag talk if implemented). Issue #14 fix: now calls onEquipped. */
    public void putGagOn(ItemStack gag) {
        equipInRegion(BodyRegionV2.MOUTH, gag);
    }

    /** Equips a blindfold (restricts vision). Issue #14 fix: now calls onEquipped. */
    public void putBlindfoldOn(ItemStack blindfold) {
        equipInRegion(BodyRegionV2.EYES, blindfold);
    }

    /** Equips a collar (starts GPS/Shock monitoring). Issue #14 fix: now calls onEquipped. */
    public void putCollarOn(ItemStack collar) {
        equipInRegion(BodyRegionV2.NECK, collar);
    }

    /** Issue #14 fix: now calls onEquipped. Syncs clothes config to all clients. */
    public void putClothesOn(ItemStack clothes) {
        equipInRegion(BodyRegionV2.TORSO, clothes);
        // Sync clothes config (dynamic textures, etc.) to all tracking clients
        host.syncClothesConfig();
    }

    /** Equips mittens (blocks hand interactions). Mittens system. Issue #14 fix: now calls onEquipped. */
    public void putMittensOn(ItemStack mittens) {
        equipInRegion(BodyRegionV2.HANDS, mittens);
        checkMittensAfterApply();
    }

    /** Equips earplugs (muffles sounds). Issue #14 fix: now calls onEquipped. */
    public void putEarplugsOn(ItemStack earplugs) {
        equipInRegion(BodyRegionV2.EARS, earplugs);
        checkEarplugsAfterApply();
    }

    // ========== Take Off Methods ==========

    /** Removes binds and restores speed. */
    public ItemStack takeBindOff() {
        return V2EquipmentHelper.unequipFromRegion(
            host.getPlayer(),
            BodyRegionV2.ARMS
        );
    }

    public ItemStack takeGagOff() {
        return V2EquipmentHelper.unequipFromRegion(
            host.getPlayer(),
            BodyRegionV2.MOUTH
        );
    }

    public ItemStack takeBlindfoldOff() {
        return V2EquipmentHelper.unequipFromRegion(
            host.getPlayer(),
            BodyRegionV2.EYES
        );
    }

    public ItemStack takeCollarOff() {
        return V2EquipmentHelper.unequipFromRegion(
            host.getPlayer(),
            BodyRegionV2.NECK
        );
    }

    /** Removes mittens. Mittens system */
    public ItemStack takeMittensOff() {
        ItemStack mittens = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.HANDS
        );
        if (isLocked(mittens, false)) {
            return ItemStack.EMPTY;
        }
        return V2EquipmentHelper.unequipFromRegion(
            host.getPlayer(),
            BodyRegionV2.HANDS
        );
    }

    public ItemStack takeEarplugsOff() {
        ItemStack earplugs = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.EARS
        );
        if (isLocked(earplugs, false)) {
            return ItemStack.EMPTY;
        }
        return V2EquipmentHelper.unequipFromRegion(
            host.getPlayer(),
            BodyRegionV2.EARS
        );
    }

    public ItemStack takeClothesOff() {
        ItemStack clothes = V2EquipmentHelper.unequipFromRegion(
            host.getPlayer(),
            BodyRegionV2.TORSO
        );
        // Sync clothes removal to all tracking clients
        host.syncClothesConfig();
        return clothes;
    }

    /**
     * Tries to remove the collar. Fails if locked unless forced.
     */
    public ItemStack takeCollarOff(boolean force) {
        Player player = host.getPlayer();
        ItemStack collar = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.NECK
        );
        if (collar.isEmpty()) return ItemStack.EMPTY;

        if (collar.getItem() instanceof ILockable lockable) {
            if (!force && lockable.isLocked(collar)) return ItemStack.EMPTY;
            lockable.setLocked(collar, false);
        }
        return V2EquipmentHelper.unequipFromRegion(player, BodyRegionV2.NECK);
    }

    // ========== Replacement Methods ==========

    /** Replaces the blindfold and returns the old one. Issue #14 fix: now calls lifecycle hooks. */
    public ItemStack replaceBlindfold(ItemStack newBlindfold) {
        ItemStack current = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.EYES
        );
        if (current.isEmpty()) return ItemStack.EMPTY;
        return replaceInRegion(BodyRegionV2.EYES, newBlindfold);
    }

    /** Replaces the gag and returns the old one. Issue #14 fix: now calls lifecycle hooks. */
    public ItemStack replaceGag(ItemStack newGag) {
        ItemStack current = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.MOUTH
        );
        if (current.isEmpty()) return ItemStack.EMPTY;
        return replaceInRegion(BodyRegionV2.MOUTH, newGag);
    }

    /** Replaces the collar and returns the old one. Issue #14 fix: now calls lifecycle hooks. */
    public ItemStack replaceCollar(ItemStack newCollar) {
        ItemStack current = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.NECK
        );
        if (current.isEmpty()) return ItemStack.EMPTY;
        return replaceInRegion(BodyRegionV2.NECK, newCollar);
    }

    /**
     * Thread Safety: Synchronized to prevent inventory tearing (gap between remove and add).
     */
    public synchronized ItemStack replaceBind(ItemStack newBind) {
        ItemStack old = takeBindOff();
        if (!old.isEmpty()) {
            putBindOn(newBind);
        }
        return old;
    }

    /**
     * Thread Safety: Synchronized to prevent inventory tearing (gap between remove and add).
     */
    public synchronized ItemStack replaceBind(
        ItemStack newBind,
        boolean force
    ) {
        // Safety: Don't remove current bind if newBind is empty
        if (newBind.isEmpty()) {
            return ItemStack.EMPTY;
        }
        ItemStack current = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.ARMS
        );
        if (isLocked(current, force)) {
            return ItemStack.EMPTY;
        }
        ItemStack old = takeBindOff();
        if (!old.isEmpty()) {
            putBindOn(newBind);
        }
        return old;
    }

    public ItemStack replaceGag(ItemStack newGag, boolean force) {
        ItemStack current = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.MOUTH
        );
        if (isLocked(current, force)) {
            return ItemStack.EMPTY;
        }
        ItemStack old = V2EquipmentHelper.unequipFromRegion(
            host.getPlayer(),
            BodyRegionV2.MOUTH
        );
        putGagOn(newGag);
        return old;
    }

    public ItemStack replaceBlindfold(ItemStack newBlindfold, boolean force) {
        ItemStack current = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.EYES
        );
        if (isLocked(current, force)) {
            return ItemStack.EMPTY;
        }
        ItemStack old = V2EquipmentHelper.unequipFromRegion(
            host.getPlayer(),
            BodyRegionV2.EYES
        );
        putBlindfoldOn(newBlindfold);
        return old;
    }

    public ItemStack replaceCollar(ItemStack newCollar, boolean force) {
        ItemStack current = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.NECK
        );
        if (isLocked(current, force)) {
            return ItemStack.EMPTY;
        }
        ItemStack old = takeCollarOff(force);
        putCollarOn(newCollar);
        return old;
    }

    public ItemStack replaceEarplugs(ItemStack newEarplugs) {
        return replaceEarplugs(newEarplugs, false);
    }

    public ItemStack replaceEarplugs(ItemStack newEarplugs, boolean force) {
        ItemStack current = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.EARS
        );
        if (isLocked(current, force)) {
            return ItemStack.EMPTY;
        }
        ItemStack old = takeEarplugsOff();
        putEarplugsOn(newEarplugs);
        return old;
    }

    public ItemStack replaceMittens(ItemStack newMittens) {
        return replaceMittens(newMittens, false);
    }

    public ItemStack replaceMittens(ItemStack newMittens, boolean force) {
        ItemStack current = V2EquipmentHelper.getInRegion(
            host.getPlayer(),
            BodyRegionV2.HANDS
        );
        if (isLocked(current, force)) {
            return ItemStack.EMPTY;
        }
        ItemStack old = takeMittensOff();
        if (!old.isEmpty() || !hasMittens()) {
            putMittensOn(newMittens);
        }
        return old;
    }

    // ========== Resistance Methods ==========

    /**
     */
    public synchronized int getCurrentBindResistance() {
        Player player = host.getPlayer();
        ItemStack stack = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.ARMS
        );
        if (stack.isEmpty()) return 0;
        // V1 and V2 both implement IHasResistance
        if (stack.getItem() instanceof com.tiedup.remake.items.base.IHasResistance resistance) {
            return resistance.getCurrentResistance(stack, player);
        }
        return 0;
    }

    /**
     */
    public synchronized void setCurrentBindResistance(int resistance) {
        Player player = host.getPlayer();
        ItemStack stack = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.ARMS
        );
        if (stack.isEmpty()) return;
        // V1 and V2 both implement IHasResistance
        if (stack.getItem() instanceof com.tiedup.remake.items.base.IHasResistance resistanceItem) {
            resistanceItem.setCurrentResistance(stack, resistance);
        }
    }

    /**
     */
    public synchronized int getCurrentCollarResistance() {
        Player player = host.getPlayer();
        ItemStack stack = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.NECK
        );
        if (stack.isEmpty()) return 0;
        // V1 and V2 both implement IHasResistance
        if (stack.getItem() instanceof com.tiedup.remake.items.base.IHasResistance resistance) {
            return resistance.getCurrentResistance(stack, player);
        }
        return 0;
    }

    /**
     */
    public synchronized void setCurrentCollarResistance(int resistance) {
        Player player = host.getPlayer();
        ItemStack stack = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.NECK
        );
        if (stack.isEmpty()) return;
        // V1 and V2 both implement IHasResistance
        if (stack.getItem() instanceof com.tiedup.remake.items.base.IHasResistance resistanceItem) {
            resistanceItem.setCurrentResistance(stack, resistance);
        }
    }

    // ========== Helper Methods ==========

    /** Helper to drop an item at the kidnapped player's feet. */
    public void kidnappedDropItem(ItemStack stack) {
        if (stack.isEmpty() || host.getPlayer() == null) return;
        host.getPlayer().drop(stack, false);
    }

    /**
     * Helper to take a bondage item if it's unlocked, and drop it.
     */
    public void takeBondageItemIfUnlocked(
        ItemStack item,
        Supplier<ItemStack> takeOffMethod
    ) {
        if (isLocked(item, false)) {
            return; // Item is locked, cannot remove
        }
        ItemStack removed = takeOffMethod.get();
        if (!removed.isEmpty()) {
            kidnappedDropItem(removed);
        }
    }

    /**
     * Check if an item is locked.
     * @param stack The item to check
     * @param force If true, returns false (bypasses lock)
     * @return true if locked and not forced
     */
    public boolean isLocked(ItemStack stack, boolean force) {
        if (force) return false;
        if (stack.isEmpty()) return false;

        if (stack.getItem() instanceof ILockable lockable) {
            return lockable.isLocked(stack);
        }

        return false;
    }

    private boolean hasMittens() {
        return V2EquipmentHelper.isRegionOccupied(
            host.getPlayer(),
            BodyRegionV2.HANDS
        );
    }

    // ========== Low-level V2 equipment operations ==========

    /**
     * Low-level equip: writes to V2 capability, calls IV2BondageItem lifecycle hooks, syncs.
     *
     * <p>INTENTIONALLY bypasses V2EquipmentManager.tryEquip() because all callers
     * (commands, NPC AI, NBT restore) are force-equip paths that should not be subject
     * to V2 conflict resolution (SMELL-003 reviewed — bypass is by design).
     * <p>Basic safety (isRegionOccupied, canEquip) is still checked here.</p>
     */
    private void equipInRegion(BodyRegionV2 region, ItemStack stack) {
        Player player = host.getPlayer();
        if (player == null || player.level().isClientSide) return;
        if (stack.isEmpty()) return;

        IV2BondageEquipment equip = V2EquipmentHelper.getEquipment(player);
        if (equip == null) return;

        // Check if already occupied
        if (equip.isRegionOccupied(region)) return;

        // Check canEquip via IV2BondageItem interface
        if (stack.getItem() instanceof IV2BondageItem bondageItem) {
            if (!bondageItem.canEquip(stack, player)) return;
        }

        equip.setInRegion(region, stack.copy());

        // Fire lifecycle hook
        if (stack.getItem() instanceof IV2BondageItem bondageItem) {
            bondageItem.onEquipped(stack, player);
        }

        V2EquipmentHelper.sync(player);
    }

    /**
     * Low-level replace: unequips old item with lifecycle hooks, equips new item.
     * Unequips old item (with onUnequipped hook), sets new item, fires onEquipped.
     */
    private ItemStack replaceInRegion(BodyRegionV2 region, ItemStack newStack) {
        Player player = host.getPlayer();
        if (
            player == null || player.level().isClientSide
        ) return ItemStack.EMPTY;

        IV2BondageEquipment equip = V2EquipmentHelper.getEquipment(player);
        if (equip == null) return ItemStack.EMPTY;

        ItemStack oldStack = equip.getInRegion(region);

        // Call onUnequipped for the old item
        if (
            !oldStack.isEmpty() &&
            oldStack.getItem() instanceof IV2BondageItem oldItem
        ) {
            oldItem.onUnequipped(oldStack, player);
        }

        // Set the new item
        equip.setInRegion(region, newStack.copy());

        // Call onEquipped for the new item
        if (
            !newStack.isEmpty() &&
            newStack.getItem() instanceof IV2BondageItem newItem
        ) {
            newItem.onEquipped(newStack, player);
        }

        V2EquipmentHelper.sync(player);
        return oldStack;
    }

    // ========== Callbacks ==========

    public void checkGagAfterApply() {
        // Gag talk handled via ChatEventHandler + GagTalkManager (event-based)
        // No initialization needed here - effects apply automatically on chat
    }

    public void checkBlindfoldAfterApply() {
        // Effects applied client-side via rendering
    }

    public void checkEarplugsAfterApply() {
        // Effects applied client-side via sound system
    }

    public void checkCollarAfterApply() {
        // Collar timers/GPS handled elsewhere
    }

    public void checkMittensAfterApply() {
        // Mittens effects handled elsewhere
    }
}
