package com.tiedup.remake.network.slave;

import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.v2.bondage.CollarHelper;
import com.tiedup.remake.state.IRestrainable;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.state.PlayerCaptorManager;
import com.tiedup.remake.util.KidnappedHelper;
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.minecraft.world.phys.AABB;
import net.minecraftforge.network.NetworkEvent;

/**
 * Packet for slave management actions from SlaveManagementScreen.
 * Handles: SHOCK, LOCATE, FREE actions on owned slaves.
 *
 *
 * Security: Distance and dimension validation added to prevent griefing
 */
public class PacketSlaveAction {

    /** Maximum interaction range for SHOCK and FREE actions (blocks) */
    private static final double MAX_INTERACTION_RANGE = 100.0;

    public enum Action {
        SHOCK,
        LOCATE,
        FREE,
    }

    private final UUID targetId;
    private final Action action;

    public PacketSlaveAction(UUID targetId, Action action) {
        this.targetId = targetId;
        this.action = action;
    }

    public void encode(FriendlyByteBuf buf) {
        buf.writeUUID(targetId);
        buf.writeEnum(action);
    }

    public static PacketSlaveAction decode(FriendlyByteBuf buf) {
        UUID id = buf.readUUID();
        Action action = buf.readEnum(Action.class);
        return new PacketSlaveAction(id, action);
    }

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

                // Rate limiting: Prevent action 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) {
            TiedUpMod.LOGGER.warn(
                "[PACKET] PacketSlaveAction: No PlayerBindState for sender {}",
                sender.getName().getString()
            );
            return;
        }

        PlayerCaptorManager manager = senderState.getCaptorManager();

        // Find the target - check both formal captives AND collar-owned entities
        IRestrainable targetCaptive = null;
        boolean isFormalCaptive = false;

        // 1. Check formal captives first
        if (manager != null && manager.hasCaptives()) {
            for (IBondageState captive : manager.getCaptives()) {
                LivingEntity entity = captive.asLivingEntity();
                if (
                    entity != null &&
                    entity.getUUID().equals(targetId) &&
                    captive instanceof IRestrainable r
                ) {
                    targetCaptive = r;
                    isFormalCaptive = true;
                    break;
                }
            }
        }

        // 2. If not found, search for nearby entities with collar owned by sender
        if (targetCaptive == null) {
            AABB searchBox = sender.getBoundingBox().inflate(32); // Security: reduced from 100
            for (LivingEntity entity : sender
                .level()
                .getEntitiesOfClass(LivingEntity.class, searchBox)) {
                if (!entity.getUUID().equals(targetId)) continue;

                IRestrainable kidnapped = KidnappedHelper.getKidnappedState(
                    entity
                );
                if (kidnapped != null && kidnapped.hasCollar()) {
                    ItemStack collarStack = kidnapped.getEquipment(
                        BodyRegionV2.NECK
                    );
                    if (CollarHelper.isCollar(collarStack)) {
                        if (CollarHelper.isOwner(collarStack, sender)) {
                            targetCaptive = kidnapped;
                            break;
                        }
                    }
                }
            }
        }

        if (targetCaptive == null) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                "Target not found or you don't own their collar!"
            );
            return;
        }

        LivingEntity targetEntity = targetCaptive.asLivingEntity();
        String targetName = targetCaptive.getKidnappedName();

        // Security: Validate dimension and distance (except for LOCATE which is GPS-based)
        if (action != Action.LOCATE) {
            // Check same dimension
            if (sender.level() != targetEntity.level()) {
                SystemMessageManager.sendToPlayer(
                    sender,
                    SystemMessageManager.MessageCategory.ERROR,
                    targetName + " is in a different dimension!"
                );
                return;
            }

            // Check distance
            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;
            }
        }

        switch (action) {
            case SHOCK -> handleShock(
                sender,
                targetCaptive,
                targetEntity,
                targetName
            );
            case LOCATE -> handleLocate(
                sender,
                targetCaptive,
                targetEntity,
                targetName
            );
            case FREE -> handleFree(
                sender,
                targetCaptive,
                manager,
                targetName,
                isFormalCaptive
            );
        }
    }

    private void handleShock(
        ServerPlayer sender,
        IRestrainable target,
        LivingEntity targetEntity,
        String name
    ) {
        // Check if target has shock collar
        if (!target.hasCollar()) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                name + " is not wearing a collar!"
            );
            return;
        }

        ItemStack collarStack = target.getEquipment(BodyRegionV2.NECK);
        if (!CollarHelper.canShock(collarStack)) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                name + " is not wearing a shock collar!"
            );
            return;
        }

        // Check if sender is owner of the collar or collar is public
        boolean isOwner = CollarHelper.isOwner(collarStack, sender);
        boolean isPublic = CollarHelper.isPublicShock(collarStack);

        if (!isOwner && !isPublic) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                "You don't have permission to shock " + name + "!"
            );
            return;
        }

        // Execute shock
        target.shockKidnapped(
            " (Remote shock from " + sender.getName().getString() + ")",
            3.0f
        );
        SystemMessageManager.sendToPlayer(
            sender,
            SystemMessageManager.MessageCategory.SHOCKER_TRIGGERED,
            name
        );

        TiedUpMod.LOGGER.debug(
            "[PACKET] {} shocked slave {}",
            sender.getName().getString(),
            name
        );
    }

    private void handleLocate(
        ServerPlayer sender,
        IRestrainable target,
        LivingEntity targetEntity,
        String name
    ) {
        // Check if target has GPS collar
        if (!target.hasCollar()) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                name + " is not wearing a collar!"
            );
            return;
        }

        ItemStack collarStack = target.getEquipment(BodyRegionV2.NECK);
        if (!CollarHelper.hasGPS(collarStack)) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                name + " is not wearing a GPS collar!"
            );
            return;
        }

        // Check permissions
        boolean isOwner = CollarHelper.isOwner(collarStack, sender);
        boolean isPublic = CollarHelper.hasPublicTracking(collarStack);

        if (!isOwner && !isPublic) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                "You don't have permission to track " + name + "!"
            );
            return;
        }

        // Check same dimension
        if (
            targetEntity == null ||
            !targetEntity.level().dimension().equals(sender.level().dimension())
        ) {
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                "Cannot locate " + name + " (different dimension or offline)!"
            );
            return;
        }

        // Calculate distance and direction
        double distance = sender.distanceTo(targetEntity);
        String direction = getDirection(sender, targetEntity);

        SystemMessageManager.sendToPlayer(
            sender,
            SystemMessageManager.MessageCategory.LOCATOR_DETECTED,
            name + ": " + (int) distance + "m [" + direction + "]"
        );

        TiedUpMod.LOGGER.debug(
            "[PACKET] {} located slave {} at {}m {}",
            sender.getName().getString(),
            name,
            (int) distance,
            direction
        );
    }

    private void handleFree(
        ServerPlayer sender,
        IRestrainable target,
        PlayerCaptorManager manager,
        String name,
        boolean isFormalCaptive
    ) {
        if (isFormalCaptive && manager != null) {
            // Remove formal captive from manager
            manager.removeCaptive(target, false);
            SystemMessageManager.sendToPlayer(
                sender,
                "Freed " + name + "!",
                ChatFormatting.GREEN
            );
            TiedUpMod.LOGGER.debug(
                "[PACKET] {} freed captive {}",
                sender.getName().getString(),
                name
            );
        } else {
            // For collar-owned entities, just remove collar ownership
            ItemStack collarStack = target.getEquipment(BodyRegionV2.NECK);
            if (CollarHelper.isCollar(collarStack)) {
                CollarHelper.removeOwner(collarStack, sender.getUUID());
                SystemMessageManager.sendToPlayer(
                    sender,
                    "Released collar control of " + name + "!",
                    ChatFormatting.GREEN
                );
                TiedUpMod.LOGGER.debug(
                    "[PACKET] {} released collar control of {}",
                    sender.getName().getString(),
                    name
                );
            }
        }
    }

    private String getDirection(LivingEntity source, LivingEntity target) {
        double dx = target.getX() - source.getX();
        double dz = target.getZ() - source.getZ();

        if (Math.abs(dx) > Math.abs(dz)) {
            return dx > 0 ? "EAST" : "WEST";
        } else {
            return dz > 0 ? "SOUTH" : "NORTH";
        }
    }
}
