package com.tiedup.remake.state.struggle;

import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.SystemMessageManager.MessageCategory;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.IHasResistance;
import com.tiedup.remake.items.base.ILockable;
import com.tiedup.remake.state.IPlayerLeashAccess;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.BindModeHelper;
import com.tiedup.remake.v2.bondage.CollarHelper;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import com.tiedup.remake.v2.bondage.component.ComponentType;
import com.tiedup.remake.v2.bondage.component.ResistanceComponent;
import com.tiedup.remake.v2.bondage.datadriven.DataDrivenBondageItem;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;

/**
 *
 * Handles struggling against binds:
 * - Gets/sets resistance from equipped bind item
 * - Drops bondage items when escaping
 * - Unties the player when resistance reaches 0
 *
 * Based on original StruggleBinds from 1.12.2 (lines 17-117)
 */
public class StruggleBinds extends StruggleState {

    /**
     * Get the current bind resistance from the equipped bind item.
     *
     * Based on original StruggleBinds.getResistanceState() (lines 23-25)
     *
     * @param state The player's bind state
     * @return Current resistance value
     */
    @Override
    protected int getResistanceState(PlayerBindState state) {
        return state.getCurrentBindResistance();
    }

    /**
     * Set the current bind resistance on the equipped bind item.
     *
     * Based on original StruggleBinds.setResistanceState() (lines 28-30)
     *
     * @param state The player's bind state
     * @param resistance The new resistance value
     */
    @Override
    protected void setResistanceState(PlayerBindState state, int resistance) {
        state.setCurrentBindResistance(resistance);
    }

    /**
     * Check if the player can struggle against their binds.
     * Returns false if:
     * - No bind item equipped
     * - Bind item has struggle disabled
     *
     * The resistance penalty is applied in StruggleState.struggle().
     *
     * Based on original StruggleBinds.canStruggle() (lines 33-41)
     *
     * @param state The player's bind state
     * @return True if struggling is allowed
     */
    @Override
    protected boolean canStruggle(PlayerBindState state) {
        Player player = state.getPlayer();
        if (player == null) {
            return false;
        }

        ItemStack bindStack = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.ARMS
        );
        if (bindStack.isEmpty() || !BindModeHelper.isBindItem(bindStack)) {
            return false;
        }

        // The locked check has been moved to struggle() where decrease is reduced

        // Check canBeStruggledOut — works for both V1 and V2 via IHasResistance
        if (bindStack.getItem() instanceof IHasResistance resistance) {
            return resistance.canBeStruggledOut(bindStack);
        }
        return true;
    }

    /**
     * Called when the player successfully escapes from their binds.
     * Drops all bondage items and completely unties the player.
     *
     * Based on original StruggleBinds.successAction() (lines 44-47)
     *
     * @param state The player's bind state
     */
    @Override
    protected void successAction(PlayerBindState state) {
        dropBondageItems(state);
        untieTarget(state);
    }

    @Override
    protected MessageCategory getSuccessCategory() {
        return MessageCategory.STRUGGLE_SUCCESS;
    }

    @Override
    protected MessageCategory getFailCategory() {
        return MessageCategory.STRUGGLE_FAIL;
    }

    /**
     * If shocked, the attempt is missed.
     */
    @Override
    protected boolean onAttempt(PlayerBindState state) {
        Player player = state.getPlayer();
        ItemStack collar = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.NECK
        );

        if (!collar.isEmpty() && CollarHelper.canShock(collar)) {
            // V2 shock collar — notify via IHasResistance if available
            if (collar.getItem() instanceof IHasResistance resistance) {
                resistance.notifyStruggle(player);
            }
            return true;
        }
        return true; // No shock collar, proceed normally
    }

    /**
     * Drop the bind items to the ground.
     * Accessories are NOT dropped during bind struggle.
     *
     *
     * Based on original StruggleBinds.dropBondageItems() (lines 50-69)
     *
     * @param state The player's bind state
     */
    private void dropBondageItems(PlayerBindState state) {
        Player player = state.getPlayer();
        if (player == null || player.level() == null) {
            return;
        }

        // ONLY drop the BIND slot
        ItemStack stack = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.ARMS
        );
        if (!stack.isEmpty()) {
            // The bind drops without its padlock (lockable=false)
            ItemStack toDrop = stack.copy();
            if (
                toDrop.getItem() instanceof ILockable lockable &&
                lockable.isLockable(toDrop)
            ) {
                lockable.breakLock(toDrop);
                TiedUpMod.LOGGER.info(
                    "[STRUGGLE] Padlock destroyed on {} during struggle",
                    toDrop.getDisplayName().getString()
                );
            }

            ItemEntity itemEntity = new ItemEntity(
                player.level(),
                player.getX(),
                player.getY(),
                player.getZ(),
                toDrop
            );
            player.level().addFreshEntity(itemEntity);

            TiedUpMod.LOGGER.debug(
                "[STRUGGLE] Dropped {} from {}",
                toDrop.getDisplayName().getString(),
                player.getName().getString()
            );
        }
    }

    /**
     * Remove only the bind items from the player (untie).
     * Accessories like gags, collars, and blindfolds remain equipped.
     *
     * Based on original StruggleBinds.untieTarget() (lines 72-78)
     *
     * @param state The player's bind state
     */
    private void untieTarget(PlayerBindState state) {
        Player player = state.getPlayer();
        if (player == null) {
            return;
        }

        if (state.isCaptive()) {
            state.free();
        }
        // Also detach leash if tied to pole (no captor but still leashed)
        else if (
            player instanceof IPlayerLeashAccess access &&
            access.tiedup$isLeashed()
        ) {
            access.tiedup$detachLeash();
            access.tiedup$dropLeash();
        }

        // 2. ONLY unequip the BIND slot (ropes, etc.)
        V2EquipmentHelper.unequipFromRegion(player, BodyRegionV2.ARMS);

        TiedUpMod.LOGGER.info(
            "[STRUGGLE] {} has struggled out of their binds and leash",
            player.getName().getString()
        );
    }

    /**
     * Called when player completes struggle mini-game successfully.
     *
     * @param state The player's bind state
     */
    public void successActionExternal(PlayerBindState state) {
        successAction(state);
    }

    /**
     *
     * @param seconds Cooldown duration in seconds
     * @param level The level for timer
     */
    public void setExternalCooldown(
        int seconds,
        net.minecraft.world.level.Level level
    ) {
        this.struggleCooldownTimer = new com.tiedup.remake.util.time.Timer(
            seconds,
            level
        );
    }

    /**
     * v2.5: Check if struggle cooldown is active.
     * Used by QTE mini-game to prevent starting a new session during cooldown.
     *
     * @return true if cooldown is active (cannot struggle yet)
     */
    public boolean isCooldownActive() {
        return (
            struggleCooldownTimer != null && !struggleCooldownTimer.isExpired()
        );
    }

    /**
     * v2.5: Get remaining cooldown time in seconds.
     *
     * @return Remaining seconds, or 0 if no cooldown
     */
    public int getRemainingCooldownSeconds() {
        if (
            struggleCooldownTimer == null || struggleCooldownTimer.isExpired()
        ) {
            return 0;
        }
        return struggleCooldownTimer.getSecondsRemaining();
    }

    /**
     * Tighten the player's binds (restore resistance to base value).
     * Called when another player right-clicks with a paddle/whip.
     *
     * Based on original StruggleBinds.tighten() (lines 81-101)
     *
     * @param tightener The player performing the tightening
     * @param state The target player's bind state
     */
    public void tighten(Player tightener, PlayerBindState state) {
        if (state == null || !state.isTiedUp()) {
            return;
        }

        Player target = state.getPlayer();
        if (target == null || target.level() == null) {
            return;
        }

        ItemStack bindStack = V2EquipmentHelper.getInRegion(
            target,
            BodyRegionV2.ARMS
        );
        if (bindStack.isEmpty() || !BindModeHelper.isBindItem(bindStack)) {
            return;
        }

        // Get base resistance: V2 reads from ResistanceComponent directly,
        // V1 reads from SettingsAccessor via item name (BUG-003 fix)
        int baseResistance;
        ResistanceComponent comp = DataDrivenBondageItem.getComponent(
            bindStack, ComponentType.RESISTANCE, ResistanceComponent.class
        );
        if (comp != null) {
            baseResistance = comp.getBaseResistance();
        } else {
            baseResistance = 100;
        }

        // Set current resistance to base (full restore)
        setResistanceState(state, baseResistance);

        TiedUpMod.LOGGER.info(
            "[STRUGGLE] {} tightened {}'s binds (resistance restored to {})",
            tightener.getName().getString(),
            target.getName().getString(),
            baseResistance
        );

        // Send message to target: "X tightened your binds!"
        SystemMessageManager.sendBindsTightened(tightener, target);

        // Send confirmation to tightener
        SystemMessageManager.sendToPlayer(
            tightener,
            "You tightened " + target.getName().getString() + "'s binds!",
            net.minecraft.ChatFormatting.YELLOW
        );
    }
}
