package com.tiedup.remake.items;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.IKnife;
import com.tiedup.remake.items.base.ILockable;
import com.tiedup.remake.items.base.KnifeVariant;
import com.tiedup.remake.v2.bondage.BindModeHelper;
import com.tiedup.remake.network.sync.SyncManager;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import java.util.List;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
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.item.UseAnim;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.Nullable;

/**
 * Generic knife item created from KnifeVariant enum.
 * Replaces individual knife classes (ItemStoneKnife, ItemIronKnife, ItemGoldenKnife).
 *
 * v2.5 Changes:
 * - Added active cutting mechanic (hold right-click)
 * - Per-tier cutting speed: Stone=5, Iron=8, Golden=12 resistance/second
 * - Durability consumed per second = cutting speed (1 dura = 1 resistance)
 * - Can cut binds directly or locked accessories
 */
public class GenericKnife extends Item implements IKnife {

    private final KnifeVariant variant;

    public GenericKnife(KnifeVariant variant) {
        super(
            new Item.Properties()
                .stacksTo(1)
                .durability(variant.getDurability())
        );
        this.variant = variant;
    }

    /**
     * Get the variant this knife was created from.
     */
    public KnifeVariant getVariant() {
        return variant;
    }

    // ==================== TOOLTIP ====================

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

        int remaining = stack.getMaxDamage() - stack.getDamageValue();
        int speed = variant.getCuttingSpeed();
        int cuttingSeconds = remaining / speed;

        // Show cutting speed
        tooltip.add(
            Component.translatable("item.tiedup.knife.cutting_speed", speed)
                .withStyle(ChatFormatting.GRAY)
        );

        // Show cutting time remaining
        tooltip.add(
            Component.translatable("item.tiedup.knife.cutting_time", cuttingSeconds, remaining)
                .withStyle(ChatFormatting.DARK_GRAY)
        );
    }

    // ==================== USE MECHANICS ====================

    @Override
    public int getUseDuration(ItemStack stack) {
        // Max use time: 5 minutes (very long, will stop naturally when bind breaks)
        return 20 * 60 * 5;
    }

    @Override
    public UseAnim getUseAnimation(ItemStack stack) {
        return UseAnim.BOW; // Shows a "using" animation
    }

    /**
     * Called when player right-clicks with knife.
     * Starts cutting if:
     * - Player is tied up (cuts bind)
     * - Player has a knife cut target set (cuts accessory lock)
     */
    @Override
    public InteractionResultHolder<ItemStack> use(
        Level level,
        Player player,
        InteractionHand hand
    ) {
        ItemStack stack = player.getItemInHand(hand);

        // Only check on server side for actual state
        if (!level.isClientSide) {
            PlayerBindState state = PlayerBindState.getInstance(player);
            if (state == null) {
                return InteractionResultHolder.pass(stack);
            }

            // v2.5: Block knife usage if wearing mittens
            if (state.hasMittens()) {
                TiedUpMod.LOGGER.debug(
                    "[GenericKnife] {} cannot use knife - wearing mittens",
                    player.getName().getString()
                );
                return InteractionResultHolder.fail(stack);
            }

            // Priority 1: If tied up, cut the bind
            if (state.isTiedUp()) {
                player.startUsingItem(hand);
                return InteractionResultHolder.consume(stack);
            }

            // Priority 2: If accessory target selected (via StruggleChoiceScreen)
            if (state.getKnifeCutTarget() != null) {
                player.startUsingItem(hand);
                return InteractionResultHolder.consume(stack);
            }

            // Priority 3: If wearing a collar (not tied), auto-target the collar
            if (state.hasCollar()) {
                state.setKnifeCutTarget(BodyRegionV2.NECK);
                player.startUsingItem(hand);
                return InteractionResultHolder.consume(stack);
            }

            // Priority 4: Check other accessories (gag, blindfold, etc.)
            if (state.isGagged()) {
                state.setKnifeCutTarget(BodyRegionV2.MOUTH);
                player.startUsingItem(hand);
                return InteractionResultHolder.consume(stack);
            }
            if (state.isBlindfolded()) {
                state.setKnifeCutTarget(BodyRegionV2.EYES);
                player.startUsingItem(hand);
                return InteractionResultHolder.consume(stack);
            }
            if (state.hasEarplugs()) {
                state.setKnifeCutTarget(BodyRegionV2.EARS);
                player.startUsingItem(hand);
                return InteractionResultHolder.consume(stack);
            }
            // Note: Don't auto-target mittens since you need hands to use knife

            // Nothing to cut
            return InteractionResultHolder.pass(stack);
        }

        // Client side - check mittens and allow use if valid target or has accessories
        PlayerBindState state = PlayerBindState.getInstance(player);
        if (
            state != null &&
            !state.hasMittens() &&
            (state.isTiedUp() ||
                state.getKnifeCutTarget() != null ||
                state.hasCollar() ||
                state.isGagged() ||
                state.isBlindfolded() ||
                state.hasEarplugs())
        ) {
            player.startUsingItem(hand);
            return InteractionResultHolder.consume(stack);
        }

        return InteractionResultHolder.pass(stack);
    }

    /**
     * Called every tick while player holds right-click.
     * Performs the actual cutting logic.
     */
    @Override
    public void onUseTick(
        Level level,
        LivingEntity entity,
        ItemStack stack,
        int remainingTicks
    ) {
        if (level.isClientSide || !(entity instanceof ServerPlayer player)) {
            return;
        }

        // Calculate how many ticks have been used
        int usedTicks = getUseDuration(stack) - remainingTicks;

        // Only process every 20 ticks (1 second)
        if (usedTicks > 0 && usedTicks % 20 == 0) {
            performCutTick(player, stack);
        }
    }

    /**
     * Called when player releases right-click or item breaks.
     */
    @Override
    public void releaseUsing(
        ItemStack stack,
        Level level,
        LivingEntity entity,
        int remainingTicks
    ) {
        if (!level.isClientSide && entity instanceof ServerPlayer player) {
            TiedUpMod.LOGGER.debug(
                "[GenericKnife] {} stopped cutting",
                player.getName().getString()
            );
        }
    }

    /**
     * Perform one "tick" of cutting (called every second while held).
     * Consumes durability and removes resistance based on variant's cutting speed.
     */
    private void performCutTick(ServerPlayer player, ItemStack stack) {
        PlayerBindState state = PlayerBindState.getInstance(player);
        if (state == null) {
            player.stopUsingItem();
            return;
        }

        int speed = variant.getCuttingSpeed();

        // Determine what to cut
        if (state.isTiedUp()) {
            // Cut BIND
            cutBind(player, state, stack, speed);
        } else if (state.getKnifeCutTarget() != null) {
            // Cut ACCESSORY
            cutAccessory(player, state, stack, speed);
        } else {
            // Nothing to cut
            player.stopUsingItem();
            return;
        }

        // Play cutting sound
        player
            .level()
            .playSound(
                null,
                player.blockPosition(),
                SoundEvents.SHEEP_SHEAR,
                SoundSource.PLAYERS,
                0.5f,
                1.2f
            );

        // Consume durability equal to cutting speed
        stack.hurtAndBreak(speed, player, p ->
            p.broadcastBreakEvent(p.getUsedItemHand())
        );

        // Notify nearby guards (Kidnappers, Maids, Traders) about cutting noise
        com.tiedup.remake.minigame.GuardNotificationHelper.notifyNearbyGuards(
            player
        );

        // Force inventory sync so durability bar updates in real-time
        player.inventoryMenu.broadcastChanges();

        // Sync state to clients
        SyncManager.syncBindState(player);
    }

    /**
     * Cut the bind directly.
     */
    private void cutBind(
        ServerPlayer player,
        PlayerBindState state,
        ItemStack knifeStack,
        int speed
    ) {
        // Get bind stack for ILockable check
        ItemStack bindStack = V2EquipmentHelper.getInRegion(
            player,
            BodyRegionV2.ARMS
        );
        if (bindStack.isEmpty() || !BindModeHelper.isBindItem(bindStack)) {
            player.stopUsingItem();
            return;
        }

        // Reduce resistance by cutting speed
        int currentRes = state.getCurrentBindResistance();
        int newRes = Math.max(0, currentRes - speed);
        state.setCurrentBindResistance(newRes);

        TiedUpMod.LOGGER.debug(
            "[GenericKnife] {} cutting bind: resistance {} -> {}",
            player.getName().getString(),
            currentRes,
            newRes
        );

        // Check if escaped
        if (newRes <= 0) {
            state.getStruggle().getStruggleBinds().successActionExternal(state);
            player.stopUsingItem();
            TiedUpMod.LOGGER.info(
                "[GenericKnife] {} escaped by cutting bind!",
                player.getName().getString()
            );
        }
    }

    /**
     * Cut an accessory - either removes lock resistance (if locked) or removes the item directly (if unlocked).
     */
    private void cutAccessory(
        ServerPlayer player,
        PlayerBindState state,
        ItemStack knifeStack,
        int speed
    ) {
        BodyRegionV2 target = state.getKnifeCutTarget();
        if (target == null) {
            player.stopUsingItem();
            return;
        }

        ItemStack accessory = V2EquipmentHelper.getInRegion(player, target);
        if (accessory.isEmpty()) {
            // Target doesn't exist
            state.clearKnifeCutTarget();
            player.stopUsingItem();
            return;
        }

        // Check if the accessory is locked
        boolean isLocked = false;
        if (accessory.getItem() instanceof ILockable lockable) {
            isLocked = lockable.isLocked(accessory);
        }

        if (!isLocked) {
            // NOT locked - directly cut and remove the accessory
            IBondageState kidnapped = KidnappedHelper.getKidnappedState(player);
            if (kidnapped != null) {
                ItemStack removed = removeAccessory(kidnapped, target);
                if (!removed.isEmpty()) {
                    // Drop the removed accessory
                    kidnapped.kidnappedDropItem(removed);
                    TiedUpMod.LOGGER.info(
                        "[GenericKnife] {} cut off unlocked {}",
                        player.getName().getString(),
                        target
                    );
                }
            }

            state.clearKnifeCutTarget();
            player.stopUsingItem();
            return;
        }

        // Accessory IS locked - reduce lock resistance via knife cut progress
        ILockable lockable = (ILockable) accessory.getItem();
        int cutProgress = com.tiedup.remake.util.ItemNBTHelper.getInt(accessory, "knifeCutProgress");
        int newProgress = cutProgress + speed;
        int lockResistance = com.tiedup.remake.core.SettingsAccessor.getPadlockResistance(null);
        com.tiedup.remake.util.ItemNBTHelper.setInt(accessory, "knifeCutProgress", newProgress);

        TiedUpMod.LOGGER.debug(
            "[GenericKnife] {} cutting {} lock: progress {} / {}",
            player.getName().getString(),
            target,
            newProgress,
            lockResistance
        );

        // Check if lock is destroyed
        if (newProgress >= lockResistance) {
            // Destroy the lock (remove padlock, clear lock state)
            lockable.setLockedByKeyUUID(accessory, null); // Unlocks and clears locked state
            lockable.setLockable(accessory, false); // Remove padlock entirely
            com.tiedup.remake.util.ItemNBTHelper.remove(accessory, "knifeCutProgress");
            lockable.setJammed(accessory, false);

            state.clearKnifeCutTarget();
            player.stopUsingItem();

            TiedUpMod.LOGGER.info(
                "[GenericKnife] {} cut through {} lock!",
                player.getName().getString(),
                target
            );
        }
    }

    /**
     * Remove an accessory from the player and return it.
     */
    private ItemStack removeAccessory(
        IBondageState kidnapped,
        BodyRegionV2 target
    ) {
        switch (target) {
            case NECK -> {
                ItemStack collar = kidnapped.getEquipment(BodyRegionV2.NECK);
                if (collar != null && !collar.isEmpty()) {
                    kidnapped.unequip(BodyRegionV2.NECK);
                    return collar;
                }
            }
            case MOUTH -> {
                ItemStack gag = kidnapped.getEquipment(BodyRegionV2.MOUTH);
                if (gag != null && !gag.isEmpty()) {
                    kidnapped.unequip(BodyRegionV2.MOUTH);
                    return gag;
                }
            }
            case EYES -> {
                ItemStack blindfold = kidnapped.getEquipment(BodyRegionV2.EYES);
                if (blindfold != null && !blindfold.isEmpty()) {
                    kidnapped.unequip(BodyRegionV2.EYES);
                    return blindfold;
                }
            }
            case EARS -> {
                ItemStack earplugs = kidnapped.getEquipment(BodyRegionV2.EARS);
                if (earplugs != null && !earplugs.isEmpty()) {
                    kidnapped.unequip(BodyRegionV2.EARS);
                    return earplugs;
                }
            }
            case HANDS -> {
                ItemStack mittens = kidnapped.getEquipment(BodyRegionV2.HANDS);
                if (mittens != null && !mittens.isEmpty()) {
                    kidnapped.unequip(BodyRegionV2.HANDS);
                    return mittens;
                }
            }
            default -> {
            }
        }
        return ItemStack.EMPTY;
    }

    /**
     * Find a knife in the player's inventory.
     *
     * @param player The player to search
     * @return The knife ItemStack, or empty if not found
     */
    public static ItemStack findKnifeInInventory(Player player) {
        // Check main hand first
        ItemStack mainHand = player.getMainHandItem();
        if (mainHand.getItem() instanceof IKnife) {
            return mainHand;
        }

        // Check off hand
        ItemStack offHand = player.getOffhandItem();
        if (offHand.getItem() instanceof IKnife) {
            return offHand;
        }

        // Check inventory
        for (int i = 0; i < player.getInventory().getContainerSize(); i++) {
            ItemStack stack = player.getInventory().getItem(i);
            if (stack.getItem() instanceof IKnife) {
                return stack;
            }
        }

        return ItemStack.EMPTY;
    }
}
