package com.tiedup.remake.network.item;

import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.AdjustmentHelper;
import com.tiedup.remake.network.sync.SyncManager;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.state.PlayerCaptorManager;
import com.tiedup.remake.v2.BodyRegionV2;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.ChatFormatting;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;

/**
 * Packet for adjusting a slave's gag/blindfold remotely.
 *
 *
 * Security: Distance and dimension validation added to prevent griefing
 */
public class PacketAdjustRemote {

    /** Maximum interaction range for remote adjustments (blocks) */
    private static final double MAX_INTERACTION_RANGE = 100.0;

    private final UUID targetId;
    private final BodyRegionV2 region;
    private final float adjustmentValue;
    private final float scaleValue;

    public PacketAdjustRemote(
        UUID targetId,
        BodyRegionV2 region,
        float adjustmentValue,
        float scaleValue
    ) {
        this.targetId = targetId;
        this.region = region;
        this.adjustmentValue = adjustmentValue;
        this.scaleValue = scaleValue;
    }

    public void encode(FriendlyByteBuf buf) {
        buf.writeUUID(targetId);
        buf.writeEnum(region);
        buf.writeFloat(adjustmentValue);
        buf.writeFloat(scaleValue);
    }

    public static PacketAdjustRemote decode(FriendlyByteBuf buf) {
        UUID id = buf.readUUID();
        BodyRegionV2 region = buf.readEnum(BodyRegionV2.class);
        float value = buf.readFloat();
        float scale = buf.readFloat();
        return new PacketAdjustRemote(id, region, value, scale);
    }

    public void handle(Supplier<NetworkEvent.Context> ctx) {
        ctx
            .get()
            .enqueueWork(() -> {
                ServerPlayer sender = ctx.get().getSender();
                if (sender == null) return;

                // Rate limiting: Prevent adjustment spam
                if (
                    !com.tiedup.remake.network.PacketRateLimiter.allowPacket(
                        sender,
                        "action"
                    )
                ) {
                    return;
                }

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

    private void handleServer(ServerPlayer sender) {
        // Get sender's kidnapper manager
        PlayerBindState senderState = PlayerBindState.getInstance(sender);
        if (senderState == null) {
            return;
        }

        // SECURITY: Validate adjustment value (prevent NaN, Infinity, out-of-bounds)
        if (
            Float.isNaN(adjustmentValue) ||
            Float.isInfinite(adjustmentValue) ||
            Float.isNaN(scaleValue) ||
            Float.isInfinite(scaleValue)
        ) {
            TiedUpMod.LOGGER.warn(
                "SECURITY: Invalid adjustment value from {}: adj={}, scale={}",
                sender.getName().getString(),
                adjustmentValue,
                scaleValue
            );
            return;
        }

        // Valid range check (-5.0 to +5.0 pixels, with margin)
        if (adjustmentValue < -5.0f || adjustmentValue > 5.0f) {
            TiedUpMod.LOGGER.debug(
                "[PACKET] Adjustment value out of bounds from {}: {}",
                sender.getName().getString(),
                adjustmentValue
            );
            return;
        }

        // Find the target - first try captives, then collar-owned entities
        IBondageState targetCaptive = null;

        // 1. Check captives first
        PlayerCaptorManager manager = senderState.getCaptorManager();
        if (manager != null) {
            for (IBondageState captive : manager.getCaptives()) {
                LivingEntity entity = captive.asLivingEntity();
                if (entity != null && entity.getUUID().equals(targetId)) {
                    targetCaptive = captive;
                    break;
                }
            }
        }

        // 2. If not found in captives, check nearby collar-owned entities
        if (targetCaptive == null) {
            targetCaptive = findCollarOwnedEntity(sender);
        }

        if (targetCaptive == null) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                "Target not found or not under your control!"
            );
            return;
        }

        LivingEntity targetEntity = targetCaptive.asLivingEntity();
        if (targetEntity == null) {
            TiedUpMod.LOGGER.warn(
                "[PACKET] PacketAdjustRemote: Target entity is null"
            );
            return;
        }
        String targetName = targetCaptive.getKidnappedName();

        // Security: Validate dimension and distance
        if (sender.level() != targetEntity.level()) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                targetName + " is in a different dimension!"
            );
            return;
        }

        double distance = sender.distanceTo(targetEntity);
        if (distance > MAX_INTERACTION_RANGE) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                targetName +
                    " is too far away! (Distance: " +
                    (int) distance +
                    " blocks, Max: " +
                    (int) MAX_INTERACTION_RANGE +
                    ")"
            );
            return;
        }

        // Get the item to adjust
        ItemStack stack = switch (region) {
            case MOUTH -> targetCaptive.getEquipment(BodyRegionV2.MOUTH);
            case EYES -> targetCaptive.getEquipment(BodyRegionV2.EYES);
            default -> ItemStack.EMPTY;
        };

        if (stack.isEmpty()) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                "Target has no " + region.name().toLowerCase() + " item!"
            );
            return;
        }

        if (!AdjustmentHelper.isAdjustable(stack)) {
            TiedUpMod.LOGGER.warn(
                "[PACKET] PacketAdjustRemote: Item {} is not adjustable",
                stack.getItem()
            );
            return;
        }

        // Apply adjustment
        AdjustmentHelper.setAdjustment(stack, adjustmentValue);
        AdjustmentHelper.setScale(stack, scaleValue);

        SystemMessageManager.sendToPlayer(
            sender,
            "Adjusted " +
                targetName +
                "'s " +
                region.name().toLowerCase() +
                " item to " +
                adjustmentValue,
            ChatFormatting.GREEN
        );

        TiedUpMod.LOGGER.debug(
            "[PACKET] {} adjusted {}'s {} to {}",
            sender.getName().getString(),
            targetName,
            region,
            adjustmentValue
        );

        // Sync if target is a player
        if (targetEntity instanceof ServerPlayer targetPlayer) {
            SyncManager.syncInventory(targetPlayer);
        }
    }

    /**
     * Find a collar-owned entity by UUID.
     * Searches nearby entities for one with a collar owned by the sender.
     */
    private IBondageState findCollarOwnedEntity(ServerPlayer sender) {
        net.minecraft.world.phys.AABB searchBox = sender
            .getBoundingBox()
            .inflate(32); // Security: reduced from 100

        for (LivingEntity entity : sender
            .level()
            .getEntitiesOfClass(LivingEntity.class, searchBox)) {
            if (entity == sender) continue;
            if (!entity.getUUID().equals(targetId)) continue;

            IBondageState kidnapped =
                com.tiedup.remake.util.KidnappedHelper.getKidnappedState(
                    entity
                );
            if (kidnapped != null && kidnapped.hasCollar()) {
                ItemStack collarStack = kidnapped.getEquipment(
                    BodyRegionV2.NECK
                );
                if (com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collarStack)) {
                    if (com.tiedup.remake.v2.bondage.CollarHelper.isOwner(collarStack, sender)) {
                        return kidnapped;
                    }
                }
            }
        }

        return null;
    }
}
