package com.tiedup.remake.v2.bondage.network;

import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.ItemKey;
import com.tiedup.remake.items.ItemMasterKey;
import com.tiedup.remake.items.base.ILockable;
import com.tiedup.remake.network.PacketRateLimiter;
import com.tiedup.remake.v2.BodyRegionV2;
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
import java.util.UUID;
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.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;

/**
 * C2S packet: player locks or unlocks a V2 bondage item on a target entity.
 *
 * <p>The server checks the player's hands for a key -- no key data is sent by
 * the client. This prevents spoofed key UUIDs.
 *
 * <p>Security model:
 * <ul>
 *   <li>Distance check: sender must be within 4 blocks</li>
 *   <li>Line-of-sight check: sender must see the target</li>
 *   <li>Key authority: server reads key from sender's hands, never from packet</li>
 *   <li>Master key can only UNLOCK (not lock)</li>
 *   <li>Regular key must match the lock UUID to unlock</li>
 * </ul>
 *
 * <p>Rate limited under the "action" bucket (10 tokens, 2/sec refill).
 */
public class PacketV2LockToggle {

    public enum Action {
        LOCK,
        UNLOCK,
    }

    private final int targetEntityId;
    private final BodyRegionV2 region;
    private final Action action;

    public PacketV2LockToggle(
        int targetEntityId,
        BodyRegionV2 region,
        Action action
    ) {
        this.targetEntityId = targetEntityId;
        this.region = region;
        this.action = action;
    }

    public static void encode(PacketV2LockToggle msg, FriendlyByteBuf buf) {
        buf.writeInt(msg.targetEntityId);
        buf.writeEnum(msg.region);
        buf.writeEnum(msg.action);
    }

    public static PacketV2LockToggle decode(FriendlyByteBuf buf) {
        return new PacketV2LockToggle(
            buf.readInt(),
            buf.readEnum(BodyRegionV2.class),
            buf.readEnum(Action.class)
        );
    }

    public static void handle(
        PacketV2LockToggle msg,
        Supplier<NetworkEvent.Context> ctxSupplier
    ) {
        NetworkEvent.Context ctx = ctxSupplier.get();
        ctx.enqueueWork(() -> {
            ServerPlayer sender = ctx.getSender();
            if (sender == null) return;
            if (!PacketRateLimiter.allowPacket(sender, "action")) return;

            handleServer(sender, msg.targetEntityId, msg.region, msg.action);
        });
        ctx.setPacketHandled(true);
    }

    private static void handleServer(
        ServerPlayer sender,
        int targetEntityId,
        BodyRegionV2 region,
        Action action
    ) {
        Entity rawTarget = sender.level().getEntity(targetEntityId);
        if (!(rawTarget instanceof LivingEntity target)) return;

        // Distance + line-of-sight validation
        if (
            sender.distanceTo(target) > 4.0 || !sender.hasLineOfSight(target)
        ) return;

        ItemStack stack = V2EquipmentHelper.getInRegion(target, region);
        if (stack.isEmpty()) return;
        if (!(stack.getItem() instanceof ILockable lockable)) return;

        // Find key in sender's hands -- server-authoritative, never from packet
        ItemStack mainHand = sender.getItemInHand(InteractionHand.MAIN_HAND);
        ItemStack offHand = sender.getItemInHand(InteractionHand.OFF_HAND);

        boolean hasMasterKey =
            mainHand.getItem() instanceof ItemMasterKey ||
            offHand.getItem() instanceof ItemMasterKey;

        ItemKey heldKey = null;
        ItemStack heldKeyStack = ItemStack.EMPTY;
        if (mainHand.getItem() instanceof ItemKey key) {
            heldKey = key;
            heldKeyStack = mainHand;
        } else if (offHand.getItem() instanceof ItemKey key) {
            heldKey = key;
            heldKeyStack = offHand;
        }

        switch (action) {
            case LOCK -> {
                if (lockable.isLocked(stack)) return;
                if (!lockable.isLockable(stack)) return;
                if (hasMasterKey) return; // master key cannot lock
                if (heldKey == null) return;

                UUID keyUUID = heldKey.getKeyUUID(heldKeyStack);
                lockable.setLockedByKeyUUID(stack, keyUUID);

                TiedUpMod.LOGGER.debug(
                    "[V2LockToggle] Locked region {} on entity {}",
                    region.name(),
                    target.getName().getString()
                );
            }
            case UNLOCK -> {
                if (!lockable.isLocked(stack)) return;

                if (hasMasterKey) {
                    lockable.setLockedByKeyUUID(stack, null);
                } else if (heldKey != null) {
                    UUID keyUUID = heldKey.getKeyUUID(heldKeyStack);
                    if (!lockable.matchesKey(stack, keyUUID)) return;
                    lockable.setLockedByKeyUUID(stack, null);
                } else {
                    return;
                }

                // Clear partial knife-cut progress and stale struggle resistance on unlock
                com.tiedup.remake.util.ItemNBTHelper.remove(stack, "knifeCutProgress");
                com.tiedup.remake.util.ItemNBTHelper.remove(stack, "accessoryStruggleResistance");

                TiedUpMod.LOGGER.debug(
                    "[V2LockToggle] Unlocked region {} on entity {}",
                    region.name(),
                    target.getName().getString()
                );
            }
        }

        V2EquipmentHelper.sync(target);
    }
}
