package com.tiedup.remake.items;

import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.ILockable;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
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 net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.jetbrains.annotations.Nullable;

/**
 * Lockpick item for picking locks on bondage restraints.
 *
 *
 * Behavior:
 * - 25% chance of success per attempt
 * - SUCCESS: Instant unlock, padlock PRESERVED (lockable=true)
 * - FAIL:
 *   - 2.5% chance to JAM the lock (blocks future lockpick attempts)
 *   - 15% chance to break the lockpick
 *   - If shock collar equipped: SHOCK + notify owners
 * - Cannot be used while wearing mittens
 * - Durability: 10 uses
 */
public class ItemLockpick extends Item {

    private static final Random random = new Random();

    public ItemLockpick() {
        super(new Item.Properties().durability(5)); // 5 tentatives max
    }

    @Override
    public void appendHoverText(
        ItemStack stack,
        @Nullable Level level,
        List<Component> tooltip,
        TooltipFlag flag
    ) {
        super.appendHoverText(stack, level, tooltip, flag);

        tooltip.add(
            Component.translatable("item.tiedup.lockpick.tooltip").withStyle(
                ChatFormatting.GRAY
            )
        );

        int remaining = stack.getMaxDamage() - stack.getDamageValue();
        tooltip.add(
            Component.translatable("item.tiedup.lockpick.uses", remaining, stack.getMaxDamage())
                .withStyle(ChatFormatting.DARK_GRAY)
        );

        // LOW FIX: Removed server config access from client tooltip (desync issue)
        // Success/break chances depend on server config, not client config
        // Displaying client config values here would be misleading in multiplayer
        tooltip.add(
            Component.translatable("item.tiedup.lockpick.chances_hint")
                .withStyle(ChatFormatting.GRAY)
                .withStyle(ChatFormatting.ITALIC)
        );
    }

    /**
     * v2.5: Right-click with lockpick opens the struggle choice screen.
     * This allows the player to choose which locked item to pick.
     */
    @Override
    public InteractionResultHolder<ItemStack> use(
        Level level,
        Player player,
        InteractionHand hand
    ) {
        ItemStack stack = player.getItemInHand(hand);

        PlayerBindState state = PlayerBindState.getInstance(player);
        if (state == null) {
            return InteractionResultHolder.pass(stack);
        }

        // Block mittens
        if (state.hasMittens()) {
            if (!level.isClientSide) {
                SystemMessageManager.sendToPlayer(
                    player,
                    SystemMessageManager.MessageCategory.CANT_USE_ITEM_MITTENS
                );
            }
            return InteractionResultHolder.fail(stack);
        }

        // Client side: open the unified bondage screen
        if (level.isClientSide) {
            openUnifiedBondageScreen();
            return InteractionResultHolder.success(stack);
        }

        return InteractionResultHolder.consume(stack);
    }

    /**
     * Client-only method to open the unified bondage screen.
     * Separated to avoid classloading issues on server.
     * Uses fully qualified names to prevent class loading on server.
     */
    @OnlyIn(Dist.CLIENT)
    private void openUnifiedBondageScreen() {
        net.minecraft.client.Minecraft.getInstance().setScreen(
            new com.tiedup.remake.client.gui.screens.UnifiedBondageScreen()
        );
    }

    /**
     * Check if this lockpick can be used (has durability remaining).
     */
    public static boolean canUse(ItemStack stack) {
        if (stack.isEmpty() || !(stack.getItem() instanceof ItemLockpick)) {
            return false;
        }
        return stack.getDamageValue() < stack.getMaxDamage();
    }

    /**
     * Result of a lockpick attempt.
     */
    public enum PickResult {
        /** Successfully picked the lock - item unlocked, padlock preserved */
        SUCCESS,
        /** Failed but lock still pickable */
        FAIL,
        /** Failed and jammed the lock - lockpick no longer usable on this item */
        JAMMED,
        /** Lockpick broke during attempt */
        BROKE,
        /** Cannot attempt - mittens equipped */
        BLOCKED_MITTENS,
        /** Cannot attempt - lock is jammed */
        BLOCKED_JAMMED,
        /** Cannot attempt - item not locked */
        NOT_LOCKED,
    }

    /**
     * Attempt to pick a lock on a target item.
     *
     * @param player The player attempting to pick
     * @param state The player's bind state
     * @param lockpickStack The lockpick being used
     * @param targetStack The item to pick
     * @param targetRegion The V2 body region of the target item
     * @return The result of the pick attempt
     */
    public static PickResult attemptPick(
        Player player,
        PlayerBindState state,
        ItemStack lockpickStack,
        ItemStack targetStack,
        BodyRegionV2 targetRegion
    ) {
        // Check if lockpick is usable
        if (!canUse(lockpickStack)) {
            return PickResult.BROKE;
        }

        // Check if wearing mittens
        if (state.hasMittens()) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.CANT_USE_ITEM_MITTENS
            );
            return PickResult.BLOCKED_MITTENS;
        }

        // Check if target is lockable and locked
        if (!(targetStack.getItem() instanceof ILockable lockable)) {
            return PickResult.NOT_LOCKED;
        }

        if (!lockable.isLocked(targetStack)) {
            return PickResult.NOT_LOCKED;
        }

        // Check if lock is jammed
        if (lockable.isJammed(targetStack)) {
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.ERROR,
                "This lock is jammed! Use struggle instead."
            );
            return PickResult.BLOCKED_JAMMED;
        }

        // Roll for success
        boolean success =
            random.nextInt(100) < SettingsAccessor.getLockpickSuccessChance();

        if (success) {
            // SUCCESS: Unlock the item, PRESERVE the padlock
            lockable.setLockedByKeyUUID(targetStack, null); // Unlock
            // lockable stays true - padlock preserved!

            SystemMessageManager.sendToPlayer(
                player,
                "Lock picked!",
                ChatFormatting.GREEN
            );

            // Damage lockpick
            damageLockpick(lockpickStack);

            TiedUpMod.LOGGER.info(
                "[LOCKPICK] {} successfully picked lock on {} ({})",
                player.getName().getString(),
                targetStack.getDisplayName().getString(),
                targetRegion
            );

            return PickResult.SUCCESS;
        } else {
            // FAIL: Various bad things can happen

            // 1. Check for shock collar and trigger shock
            triggerShockIfCollar(player, state);

            // 2. Check for jam
            boolean jammed =
                random.nextDouble() * 100 <
                SettingsAccessor.getLockpickJamChance();
            if (jammed) {
                lockable.setJammed(targetStack, true);
                SystemMessageManager.sendToPlayer(
                    player,
                    SystemMessageManager.MessageCategory.ERROR,
                    "The lock jammed! Only struggle can open it now."
                );

                TiedUpMod.LOGGER.info(
                    "[LOCKPICK] {} jammed the lock on {} ({})",
                    player.getName().getString(),
                    targetStack.getDisplayName().getString(),
                    targetRegion
                );

                // Damage lockpick
                boolean broke = damageLockpick(lockpickStack);
                return broke ? PickResult.BROKE : PickResult.JAMMED;
            }

            // 3. Check for break
            boolean broke =
                random.nextInt(100) <
                SettingsAccessor.getLockpickBreakChance();
            if (broke) {
                lockpickStack.shrink(1);
                SystemMessageManager.sendToPlayer(
                    player,
                    SystemMessageManager.MessageCategory.ERROR,
                    "Lockpick broke!"
                );

                TiedUpMod.LOGGER.info(
                    "[LOCKPICK] {}'s lockpick broke while picking {} ({})",
                    player.getName().getString(),
                    targetStack.getDisplayName().getString(),
                    targetRegion
                );

                return PickResult.BROKE;
            }

            // 4. Normal fail - just damage lockpick
            damageLockpick(lockpickStack);
            SystemMessageManager.sendToPlayer(
                player,
                SystemMessageManager.MessageCategory.WARNING,
                "Lockpick slipped..."
            );

            TiedUpMod.LOGGER.debug(
                "[LOCKPICK] {} failed to pick lock on {} ({})",
                player.getName().getString(),
                targetStack.getDisplayName().getString(),
                targetRegion
            );

            return PickResult.FAIL;
        }
    }

    /**
     * Damage the lockpick by 1 use.
     * @return true if the lockpick broke (ran out of durability)
     */
    private static boolean damageLockpick(ItemStack stack) {
        stack.setDamageValue(stack.getDamageValue() + 1);
        if (stack.getDamageValue() >= stack.getMaxDamage()) {
            stack.shrink(1);
            return true;
        }
        return false;
    }

    /**
     * Trigger shock collar if player has one equipped.
     * Also notifies the collar owners.
     */
    private static void triggerShockIfCollar(
        Player player,
        PlayerBindState state
    ) {
        ItemStack collar = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.NECK
        );
        if (collar.isEmpty()) return;

        if (com.tiedup.remake.v2.bondage.CollarHelper.canShock(collar)) {
            // Shock the player
            state.shockKidnapped(" (Failed lockpick attempt)", 2.0f);

            // Notify owners
            notifyOwnersLockpickAttempt(player, collar);

            TiedUpMod.LOGGER.info(
                "[LOCKPICK] {} was shocked for failed lockpick attempt",
                player.getName().getString()
            );
        }
    }

    /**
     * Notify shock collar owners about the lockpick attempt.
     */
    private static void notifyOwnersLockpickAttempt(
        Player player,
        ItemStack collar
    ) {
        if (player.getServer() == null) return;

        Component warning = Component.translatable("item.tiedup.lockpick.alert_prefix")
            .withStyle(ChatFormatting.RED, ChatFormatting.BOLD)
            .append(
                Component.translatable(
                    "item.tiedup.lockpick.alert_attempt", player.getName().getString()
                ).withStyle(ChatFormatting.GOLD)
            );

        List<UUID> owners = com.tiedup.remake.v2.bondage.CollarHelper.getOwners(collar);
        for (UUID ownerId : owners) {
            ServerPlayer owner = player
                .getServer()
                .getPlayerList()
                .getPlayer(ownerId);
            if (owner != null) {
                owner.sendSystemMessage(warning);
            }
        }
    }

    /**
     * Find a lockpick in the player's inventory.
     * @return The first usable lockpick found, or EMPTY if none
     */
    public static ItemStack findLockpickInInventory(Player player) {
        for (ItemStack stack : player.getInventory().items) {
            if (canUse(stack)) {
                return stack;
            }
        }
        return ItemStack.EMPTY;
    }
}
