package com.tiedup.remake.network.slave;

import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.items.base.ILockable;
import com.tiedup.remake.network.sync.SyncManager;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.util.KidnappedHelper;
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.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;

/**
 * Packet sent from client to server when master uses key to manage slave's items.
 * Handles LOCK, UNLOCK, and REMOVE operations using the keyUUID system.
 *
 */
public class PacketSlaveItemManage {

    /**
     * Action to perform on the item.
     */
    public enum Action {
        LOCK,
        UNLOCK,
        REMOVE,
        TOGGLE_BONDAGE_SERVICE,
    }

    private final UUID targetEntityUUID;
    private final BodyRegionV2 region;
    private final Action action;
    private final UUID keyUUID;
    private final boolean isMasterKey;

    public PacketSlaveItemManage(
        UUID targetEntityUUID,
        BodyRegionV2 region,
        Action action,
        UUID keyUUID,
        boolean isMasterKey
    ) {
        this.targetEntityUUID = targetEntityUUID;
        this.region = region;
        this.action = action;
        this.keyUUID = keyUUID;
        this.isMasterKey = isMasterKey;
    }

    /**
     * Encode packet to buffer.
     */
    public static void encode(PacketSlaveItemManage msg, FriendlyByteBuf buf) {
        buf.writeUUID(msg.targetEntityUUID);
        buf.writeEnum(msg.region);
        buf.writeEnum(msg.action);
        buf.writeBoolean(msg.keyUUID != null);
        if (msg.keyUUID != null) {
            buf.writeUUID(msg.keyUUID);
        }
        buf.writeBoolean(msg.isMasterKey);
    }

    /**
     * Decode packet from buffer.
     */
    public static PacketSlaveItemManage decode(FriendlyByteBuf buf) {
        UUID targetEntityUUID = buf.readUUID();
        BodyRegionV2 region = buf.readEnum(BodyRegionV2.class);
        Action action = buf.readEnum(Action.class);
        UUID keyUUID = buf.readBoolean() ? buf.readUUID() : null;
        boolean isMasterKey = buf.readBoolean();
        return new PacketSlaveItemManage(
            targetEntityUUID,
            region,
            action,
            keyUUID,
            isMasterKey
        );
    }

    /**
     * Handle packet on server.
     */
    public static void handle(
        PacketSlaveItemManage msg,
        Supplier<NetworkEvent.Context> ctx
    ) {
        ctx
            .get()
            .enqueueWork(() -> {
                ServerPlayer sender = ctx.get().getSender();
                if (sender == null) return;

                // Rate limiting: Prevent item management spam
                if (
                    !com.tiedup.remake.network.PacketRateLimiter.allowPacket(
                        sender,
                        "action"
                    )
                ) {
                    SystemMessageManager.sendToPlayer(
                        sender,
                        SystemMessageManager.MessageCategory.ERROR,
                        "Too fast! Wait a moment."
                    );
                    return;
                }

                // Find target entity (player or NPC)
                LivingEntity targetEntity = findTargetEntity(
                    sender,
                    msg.targetEntityUUID
                );
                if (targetEntity == null) {
                    TiedUpMod.LOGGER.debug(
                        "[PacketSlaveItemManage] Target entity not found: {}",
                        msg.targetEntityUUID
                    );
                    return;
                }

                // Check distance (max 5 blocks)
                if (sender.distanceTo(targetEntity) > 5.0) {
                    TiedUpMod.LOGGER.debug(
                        "[PacketSlaveItemManage] Sender too far from target"
                    );
                    SystemMessageManager.sendToPlayer(
                        sender,
                        SystemMessageManager.MessageCategory.ERROR,
                        "Too far! Get closer to manage items."
                    );
                    return;
                }

                // Get target's kidnapped state
                IBondageState targetState = KidnappedHelper.getKidnappedState(
                    targetEntity
                );
                if (targetState == null) {
                    TiedUpMod.LOGGER.debug(
                        "[PacketSlaveItemManage] Target has no kidnapped state"
                    );
                    return;
                }

                // Target must have a collar
                if (!targetState.hasCollar()) {
                    TiedUpMod.LOGGER.debug(
                        "[PacketSlaveItemManage] Target has no collar"
                    );
                    return;
                }

                // Security: Verify sender owns the collar (or is admin)
                ItemStack collarStack = targetState.getEquipment(
                    BodyRegionV2.NECK
                );
                if (com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collarStack)) {
                    if (
                        !com.tiedup.remake.v2.bondage.CollarHelper.isOwner(collarStack, sender) &&
                        !sender.hasPermissions(2)
                    ) {
                        TiedUpMod.LOGGER.debug(
                            "[PacketSlaveItemManage] Sender is not collar owner and not admin"
                        );
                        return;
                    }
                }

                // Furniture seat blocks this region
                if (
                    targetEntity.isPassenger() &&
                    targetEntity.getVehicle() instanceof
                        com.tiedup.remake.v2.furniture.ISeatProvider provider
                ) {
                    com.tiedup.remake.v2.furniture.SeatDefinition seat =
                        provider.getSeatForPassenger(targetEntity);
                    if (
                        seat != null &&
                        seat.blockedRegions().contains(msg.region)
                    ) {
                        return; // Region blocked by furniture
                    }
                }

                // Get item from region (V2 storage)
                ItemStack itemStack = getItemInRegion(targetState, msg.region);
                if (itemStack.isEmpty()) {
                    TiedUpMod.LOGGER.debug(
                        "[PacketSlaveItemManage] Region {} is empty",
                        msg.region
                    );
                    SystemMessageManager.sendToPlayer(
                        sender,
                        SystemMessageManager.MessageCategory.ERROR,
                        "Region " + msg.region.name() + " is empty."
                    );
                    return;
                }

                // Perform action
                switch (msg.action) {
                    case LOCK -> handleLock(
                        sender,
                        targetEntity,
                        targetState,
                        msg.region,
                        itemStack,
                        msg.keyUUID,
                        msg.isMasterKey
                    );
                    case UNLOCK -> handleUnlock(
                        sender,
                        targetEntity,
                        targetState,
                        msg.region,
                        itemStack,
                        msg.keyUUID,
                        msg.isMasterKey
                    );
                    case REMOVE -> handleRemove(
                        sender,
                        targetEntity,
                        targetState,
                        msg.region,
                        itemStack,
                        msg.keyUUID,
                        msg.isMasterKey
                    );
                    case TOGGLE_BONDAGE_SERVICE -> handleToggleBondageService(
                        sender,
                        targetEntity,
                        targetState,
                        collarStack
                    );
                }

                // Sync changes
                if (targetEntity instanceof ServerPlayer targetPlayer) {
                    SyncManager.syncAll(targetPlayer);
                } else {
                    // For NPCs: V2 sync via EntityDataAccessor (IV2EquipmentHolder)
                    // or packet-based sync for other entity types
                    V2EquipmentHelper.sync(targetEntity);
                }

                TiedUpMod.LOGGER.debug(
                    "[PacketSlaveItemManage] {} performed {} on {}'s {} region",
                    sender.getName().getString(),
                    msg.action,
                    targetEntity.getName().getString(),
                    msg.region
                );
            });

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

    /**
     * Find target entity by UUID (player or NPC).
     */
    static LivingEntity findTargetEntity(ServerPlayer sender, UUID targetUUID) {
        // Try player first
        Player player = sender.level().getPlayerByUUID(targetUUID);
        if (player != null) {
            return player;
        }

        // Try other entities within reasonable range (64 blocks)
        net.minecraft.world.phys.AABB searchBox = sender
            .getBoundingBox()
            .inflate(64);
        for (LivingEntity entity : sender
            .level()
            .getEntitiesOfClass(LivingEntity.class, searchBox)) {
            if (entity.getUUID().equals(targetUUID)) {
                return entity;
            }
        }
        return null;
    }

    /**
     * Get item from a V2 body region.
     */
    private static ItemStack getItemInRegion(
        IBondageState state,
        BodyRegionV2 region
    ) {
        return state.getItemInRegion(region);
    }

    /**
     * SECURITY: Verifies that the player actually holds a Master Key in their inventory.
     * Prevents client-side spoofing of the isMasterKey boolean.
     *
     * @param player The player to check
     * @return true if player holds Master Key in main hand or offhand
     */
    private static boolean verifyMasterKey(ServerPlayer player) {
        ItemStack mainHand = player.getMainHandItem();
        ItemStack offHand = player.getOffhandItem();

        return (
            mainHand.getItem() ==
                com.tiedup.remake.items.ModItems.MASTER_KEY.get() ||
            offHand.getItem() ==
            com.tiedup.remake.items.ModItems.MASTER_KEY.get()
        );
    }

    /**
     * SECURITY FIX: Verifies that the player actually holds a physical key with the specified UUID.
     * Prevents key spoofing exploit where clients could send arbitrary UUIDs to unlock items.
     *
     * @param player The player to check
     * @param keyUUID The key UUID to verify
     * @return true if player possesses a physical ItemKey with matching UUID
     */
    private static boolean verifyPlayerHasKey(
        ServerPlayer player,
        UUID keyUUID
    ) {
        if (keyUUID == null) return false;

        // Search entire player inventory for a key with matching UUID
        for (ItemStack stack : player.getInventory().items) {
            if (
                stack.getItem() instanceof com.tiedup.remake.items.ItemKey key
            ) {
                if (keyUUID.equals(key.getKeyUUID(stack))) {
                    return true; // Found matching key
                }
            }
        }

        // Also check offhand
        ItemStack offhand = player.getOffhandItem();
        if (offhand.getItem() instanceof com.tiedup.remake.items.ItemKey key) {
            if (keyUUID.equals(key.getKeyUUID(offhand))) {
                return true;
            }
        }

        return false; // Player does not possess this key
    }

    /**
     * Handle LOCK action - locks the item with the key's UUID.
     * Item must have a padlock attached (isLockable) to be locked.
     */
    private static void handleLock(
        ServerPlayer sender,
        LivingEntity target,
        IBondageState targetState,
        BodyRegionV2 region,
        ItemStack itemStack,
        UUID keyUUID,
        boolean clientClaimsMasterKey
    ) {
        // SECURITY: Verify master key server-side (don't trust client boolean)
        boolean actuallyHasMasterKey = verifyMasterKey(sender);

        // Master key cannot lock items
        if (actuallyHasMasterKey) {
            TiedUpMod.LOGGER.debug(
                "[PacketSlaveItemManage] Master key cannot lock items"
            );
            return;
        }

        // SECURITY: Detect spoofing attempt
        if (clientClaimsMasterKey && !actuallyHasMasterKey) {
            TiedUpMod.LOGGER.warn(
                "SECURITY: Player {} attempted to spoof Master Key in LOCK operation!",
                sender.getName().getString()
            );
            return;
        }

        // Key UUID required for locking
        if (keyUUID == null) {
            TiedUpMod.LOGGER.debug(
                "[PacketSlaveItemManage] No key UUID provided for locking"
            );
            return;
        }

        // SECURITY FIX: Verify player actually possesses the key
        if (!verifyPlayerHasKey(sender, keyUUID)) {
            TiedUpMod.LOGGER.warn(
                "SECURITY: Player {} attempted to lock with non-existent key UUID {}!",
                sender.getName().getString(),
                keyUUID
            );
            return;
        }

        // Item must be ILockable
        if (!(itemStack.getItem() instanceof ILockable lockable)) {
            TiedUpMod.LOGGER.debug(
                "[PacketSlaveItemManage] Item is not ILockable: {}",
                itemStack
            );
            return;
        }

        // Item must have a padlock attached (isLockable = true)
        if (!lockable.isLockable(itemStack)) {
            TiedUpMod.LOGGER.debug(
                "[PacketSlaveItemManage] Item has no padlock attached. Use padlock first."
            );
            return;
        }

        // Item must not already be locked
        if (lockable.isLocked(itemStack)) {
            TiedUpMod.LOGGER.debug(
                "[PacketSlaveItemManage] Item is already locked"
            );
            return;
        }

        // Lock with keyUUID
        lockable.setLockedByKeyUUID(itemStack, keyUUID);

        TiedUpMod.LOGGER.info(
            "[PacketSlaveItemManage] {} locked {}'s {} with key {}",
            sender.getName().getString(),
            target.getName().getString(),
            region,
            keyUUID
        );
    }

    /**
     * Handle UNLOCK action - unlocks the item if key matches or master key.
     * Drops the padlock and removes the lockable state.
     */
    private static void handleUnlock(
        ServerPlayer sender,
        LivingEntity target,
        IBondageState targetState,
        BodyRegionV2 region,
        ItemStack itemStack,
        UUID keyUUID,
        boolean clientClaimsMasterKey
    ) {
        // Item must be ILockable
        if (!(itemStack.getItem() instanceof ILockable lockable)) {
            TiedUpMod.LOGGER.debug(
                "[PacketSlaveItemManage] Item is not ILockable: {}",
                itemStack
            );
            return;
        }

        // Item must be locked
        if (!lockable.isLocked(itemStack)) {
            TiedUpMod.LOGGER.debug(
                "[PacketSlaveItemManage] Item is not locked"
            );
            return;
        }

        // SECURITY: Verify master key server-side (don't trust client boolean)
        boolean actuallyHasMasterKey = verifyMasterKey(sender);

        // SECURITY: Detect spoofing attempt
        if (clientClaimsMasterKey && !actuallyHasMasterKey) {
            TiedUpMod.LOGGER.warn(
                "SECURITY: Player {} attempted to spoof Master Key in UNLOCK operation!",
                sender.getName().getString()
            );
            return;
        }

        // Check if key matches (or ACTUAL master key bypasses)
        if (!actuallyHasMasterKey) {
            UUID lockedByUUID = lockable.getLockedByKeyUUID(itemStack);

            // SECURITY FIX: Verify player actually possesses the key BEFORE checking if it matches
            // Previous exploit: Client could send any UUID that matched lockedByUUID without owning the key
            if (keyUUID != null && !verifyPlayerHasKey(sender, keyUUID)) {
                TiedUpMod.LOGGER.warn(
                    "SECURITY: Player {} attempted to unlock with non-existent key UUID {}!",
                    sender.getName().getString(),
                    keyUUID
                );
                return;
            }

            if (lockedByUUID != null && !lockedByUUID.equals(keyUUID)) {
                TiedUpMod.LOGGER.debug(
                    "[PacketSlaveItemManage] Wrong key! Item locked by different key"
                );
                return;
            }
        }

        // Unlock the item - just unlock, keep padlock attached (can be re-locked)
        lockable.setLockedByKeyUUID(itemStack, null);
        // Note: Padlock is only dropped on REMOVE, not on UNLOCK

        TiedUpMod.LOGGER.info(
            "[PacketSlaveItemManage] {} unlocked {}'s {} (masterKey={})",
            sender.getName().getString(),
            target.getName().getString(),
            region,
            actuallyHasMasterKey
        );
    }

    /**
     * Handle REMOVE action - removes item from region and gives to sender.
     * Padlock stays attached to the item (permanent via anvil).
     */
    private static void handleRemove(
        ServerPlayer sender,
        LivingEntity target,
        IBondageState targetState,
        BodyRegionV2 region,
        ItemStack itemStack,
        UUID keyUUID,
        boolean isMasterKey
    ) {
        // Note: Remove button is hidden when locked, so this check is just a safety
        if (itemStack.getItem() instanceof ILockable lockable) {
            if (lockable.isLocked(itemStack)) {
                TiedUpMod.LOGGER.debug(
                    "[PacketSlaveItemManage] Cannot remove: item is locked!"
                );
                return;
            }
        }

        // Remove from region (V2 storage)
        ItemStack removed = removeFromRegion(targetState, region);

        if (!removed.isEmpty()) {
            // Give item to sender (or drop if inventory full)
            // Note: Padlock stays on the item (lockable state preserved)
            if (!sender.getInventory().add(removed.copy())) {
                sender.drop(removed.copy(), false);
            }

            TiedUpMod.LOGGER.info(
                "[PacketSlaveItemManage] {} removed {} from {}'s {} region",
                sender.getName().getString(),
                removed.getDisplayName().getString(),
                target.getName().getString(),
                region
            );
        }
    }

    /**
     * Handle TOGGLE_BONDAGE_SERVICE action - toggles bondage service on the collar.
     * Requires a prison to be configured on the collar.
     */
    private static void handleToggleBondageService(
        ServerPlayer sender,
        LivingEntity target,
        IBondageState targetState,
        ItemStack collarStack
    ) {
        if (
            collarStack.isEmpty() ||
            !com.tiedup.remake.v2.bondage.CollarHelper.isCollar(collarStack)
        ) {
            TiedUpMod.LOGGER.debug(
                "[PacketSlaveItemManage] No collar for bondage service toggle"
            );
            return;
        }

        // Check if cell is configured (required for bondage service)
        if (!com.tiedup.remake.v2.bondage.CollarHelper.hasCellAssigned(collarStack)) {
            TiedUpMod.LOGGER.debug(
                "[PacketSlaveItemManage] Cannot enable bondage service: no cell configured"
            );
            // Send feedback to player
            SystemMessageManager.sendToPlayer(
                sender,
                SystemMessageManager.MessageCategory.ERROR,
                "Cannot enable Bondage Service: Assign a cell first!"
            );
            return;
        }

        // Toggle bondage service
        boolean currentState = com.tiedup.remake.v2.bondage.CollarHelper.isBondageServiceEnabled(collarStack);
        com.tiedup.remake.v2.bondage.CollarHelper.setBondageServiceEnabled(collarStack, !currentState);

        String newState = !currentState ? "enabled" : "disabled";
        TiedUpMod.LOGGER.info(
            "[PacketSlaveItemManage] {} {} bondage service on {}'s collar",
            sender.getName().getString(),
            newState,
            target.getName().getString()
        );

        // Send feedback
        SystemMessageManager.sendChatToPlayer(
            sender,
            "Bondage Service " +
                newState +
                " on " +
                target.getName().getString(),
            !currentState
                ? net.minecraft.ChatFormatting.LIGHT_PURPLE
                : net.minecraft.ChatFormatting.GRAY
        );
    }

    /**
     * Remove item from a V2 body region.
     */
    private static ItemStack removeFromRegion(
        IBondageState state,
        BodyRegionV2 region
    ) {
        return state.unequipFromRegion(region);
    }
}
