package com.tiedup.remake.network.selfbondage;

import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.ILockable;
import com.tiedup.remake.network.ModNetwork;
import com.tiedup.remake.network.PacketRateLimiter;
import com.tiedup.remake.network.action.PacketTying;
import com.tiedup.remake.network.sync.SyncManager;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.tasks.TyingPlayerTask;
import com.tiedup.remake.tasks.TyingTask;
import com.tiedup.remake.tasks.V2TyingPlayerTask;
import com.tiedup.remake.util.KidnappedHelper;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.BindModeHelper;
import com.tiedup.remake.v2.bondage.IV2BondageItem;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import com.tiedup.remake.v2.bondage.V2EquipResult;
import java.util.function.Supplier;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;

/**
 * Packet sent from client to server when player attempts self-bondage.
 * Triggered by left-click with bondage item in hand.
 *
 * Self-bondage rules:
 * - Binds: Requires tying task (same duration as normal tying)
 * - Accessories (gag, blindfold, mittens, earplugs): Instant, can be equipped anytime
 * - Collar: NOT allowed (cannot self-collar)
 */
public class PacketSelfBondage {

    private final InteractionHand hand;

    public PacketSelfBondage(InteractionHand hand) {
        this.hand = hand;
    }

    public static void encode(PacketSelfBondage msg, FriendlyByteBuf buf) {
        buf.writeEnum(msg.hand);
    }

    public static PacketSelfBondage decode(FriendlyByteBuf buf) {
        return new PacketSelfBondage(buf.readEnum(InteractionHand.class));
    }

    public static void handle(
        PacketSelfBondage msg,
        Supplier<NetworkEvent.Context> ctx
    ) {
        ctx
            .get()
            .enqueueWork(() -> {
                ServerPlayer player = ctx.get().getSender();
                if (player == null) return;

                // Rate limiting (dedicated bucket: 15 tokens, 6/sec refill for continuous 5 pkt/sec)
                if (!PacketRateLimiter.allowPacket(player, "selfbondage")) {
                    return;
                }

                ItemStack stack = player.getItemInHand(msg.hand);
                if (stack.isEmpty()) return;

                Item item = stack.getItem();

                // Get player's kidnapped state
                IBondageState state = KidnappedHelper.getKidnappedState(player);
                if (state == null) return;

                // Route to appropriate handler based on item type
                // V2 bondage items — use tying task with V2 equip
                if (item instanceof IV2BondageItem v2Item) {
                    handleV2SelfBondage(player, stack, v2Item, state);
                }
                // V1 routes removed — all bondage items are now V2/data-driven
            });

        ctx.get().setPacketHandled(true);
    }

    /**
     * Handle self-bondage with a V2 bondage item.
     * Uses V2TyingPlayerTask for progress, V2EquipmentHelper for equip.
     */
    private static void handleV2SelfBondage(
        ServerPlayer player,
        ItemStack stack,
        IV2BondageItem v2Item,
        IBondageState state
    ) {
        java.util.Set<BodyRegionV2> regions = v2Item.getOccupiedRegions(stack);

        // Cannot self-collar
        if (regions.contains(BodyRegionV2.NECK)) {
            TiedUpMod.LOGGER.debug("[SelfBondage] {} tried to self-collar — blocked", player.getName().getString());
            return;
        }

        // ARMS: tying task with progress bar
        if (regions.contains(BodyRegionV2.ARMS)) {
            handleV2SelfBind(player, stack, v2Item, state);
            return;
        }

        // Accessories (MOUTH, EYES, EARS, HANDS): instant equip
        handleV2SelfAccessory(player, stack, v2Item, state);
    }

    private static void handleV2SelfBind(
        ServerPlayer player,
        ItemStack stack,
        IV2BondageItem v2Item,
        IBondageState state
    ) {
        // Can't self-tie if already tied (check both V1 state and V2 region to prevent dual-bind)
        if (state.isTiedUp() || V2EquipmentHelper.isRegionOccupied(player, BodyRegionV2.ARMS)) {
            TiedUpMod.LOGGER.debug("[SelfBondage] {} tried V2 self-tie but already tied", player.getName().getString());
            return;
        }

        // Check if all target regions are already occupied or blocked
        boolean allBlocked = true;
        for (BodyRegionV2 region : v2Item.getOccupiedRegions(stack)) {
            if (!V2EquipmentHelper.isRegionOccupied(player, region)
                && !V2EquipmentHelper.isRegionBlocked(player, region)) {
                allBlocked = false;
                break;
            }
        }
        if (allBlocked) {
            TiedUpMod.LOGGER.debug("[SelfBondage] {} tried V2 self-equip but all regions occupied", player.getName().getString());
            return;
        }

        PlayerBindState playerState = PlayerBindState.getInstance(player);
        if (playerState == null) return;

        int tyingSeconds = SettingsAccessor.getTyingPlayerTime(player.level().getGameRules());

        V2TyingPlayerTask newTask = new V2TyingPlayerTask(
            stack.copy(), stack, state, player, tyingSeconds, player.level(), player
        );

        TyingTask currentTask = playerState.tasks().getCurrentTyingTask();
        if (currentTask == null
            || !currentTask.isSameTarget(player)
            || currentTask.isOutdated()
            || !ItemStack.matches(currentTask.getBind(), stack)) {
            playerState.tasks().setCurrentTyingTask(newTask);
            newTask.start();
        } else {
            currentTask.update();
        }

        if (playerState.tasks().getCurrentTyingTask() == newTask) {
            newTask.update();
        }

        // Clear completed task to prevent blocking future tying interactions
        TyingTask activeTask = playerState.tasks().getCurrentTyingTask();
        if (activeTask != null && activeTask.isStopped()) {
            playerState.tasks().setCurrentTyingTask(null);
        }
    }

    private static void handleV2SelfAccessory(
        ServerPlayer player,
        ItemStack stack,
        IV2BondageItem v2Item,
        IBondageState state
    ) {
        // Can't equip accessories if arms are fully bound
        ItemStack currentBind = V2EquipmentHelper.getInRegion(player, BodyRegionV2.ARMS);
        if (!currentBind.isEmpty() && BindModeHelper.hasArmsBound(currentBind)) {
            TiedUpMod.LOGGER.debug("[SelfBondage] {} can't self-accessory — arms bound", player.getName().getString());
            return;
        }

        // Check if region is occupied — try to swap
        for (BodyRegionV2 region : v2Item.getOccupiedRegions(stack)) {
            if (V2EquipmentHelper.isRegionOccupied(player, region)) {
                ItemStack existing = V2EquipmentHelper.getInRegion(player, region);
                // Can't swap if locked
                if (existing.getItem() instanceof ILockable lockable && lockable.isLocked(existing)) {
                    TiedUpMod.LOGGER.debug("[SelfBondage] {} can't swap — current is locked", player.getName().getString());
                    return;
                }
            }
        }

        // Equip via V2EquipmentHelper (handles conflict resolution, displaced items)
        V2EquipResult result = V2EquipmentHelper.equipItem(player, stack.copy());
        if (result.isSuccess()) {
            for (ItemStack displaced : result.displaced()) {
                player.spawnAtLocation(displaced);
            }
            stack.shrink(1);
            SyncManager.syncInventory(player);
            TiedUpMod.LOGGER.info("[SelfBondage] {} self-equipped V2 accessory", player.getName().getString());
        }
    }

}
