package com.tiedup.remake.commands.subcommands;

import com.mojang.brigadier.arguments.FloatArgumentType;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.tiedup.remake.commands.CommandHelper;
import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.items.ModItems;
import com.tiedup.remake.items.base.AdjustmentHelper;
import com.tiedup.remake.items.base.BindVariant;
import com.tiedup.remake.items.base.BlindfoldVariant;
import com.tiedup.remake.items.base.EarplugsVariant;
import com.tiedup.remake.items.base.GagVariant;
import com.tiedup.remake.items.base.ItemCollar;
import com.tiedup.remake.network.ModNetwork;
import com.tiedup.remake.network.sync.PacketSyncBindState;
import com.tiedup.remake.state.PlayerBindState;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;

/**
 * Bondage-related sub-commands for /tiedup.
 * Handles: tie, untie, gag, ungag, blindfold, unblind, collar, takecollar,
 * takeearplugs, putearplugs, takeclothes, putclothes, fullyrestrain, enslave, free, adjust
 */
@SuppressWarnings("null")
public class BondageSubCommand {

    public static void register(
        LiteralArgumentBuilder<CommandSourceStack> root
    ) {
        // /tiedup tie <player>
        root.then(
            Commands.literal("tie")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::tie)
                )
        );
        // /tiedup untie <player>
        root.then(
            Commands.literal("untie")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::untie)
                )
        );
        // /tiedup gag <player>
        root.then(
            Commands.literal("gag")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::gag)
                )
        );
        // /tiedup ungag <player>
        root.then(
            Commands.literal("ungag")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::ungag)
                )
        );
        // /tiedup blindfold <player>
        root.then(
            Commands.literal("blindfold")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::blindfold)
                )
        );
        // /tiedup unblind <player>
        root.then(
            Commands.literal("unblind")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::unblind)
                )
        );
        // /tiedup collar <player>
        root.then(
            Commands.literal("collar")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::collar)
                )
        );
        // /tiedup takecollar <player>
        root.then(
            Commands.literal("takecollar")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::takecollar)
                )
        );
        // /tiedup takeearplugs <player>
        root.then(
            Commands.literal("takeearplugs")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::takeearplugs)
                )
        );
        // /tiedup putearplugs <player>
        root.then(
            Commands.literal("putearplugs")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::putearplugs)
                )
        );
        // /tiedup takeclothes <player>
        root.then(
            Commands.literal("takeclothes")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::takeclothes)
                )
        );
        // /tiedup putclothes <player>
        root.then(
            Commands.literal("putclothes")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::putclothes)
                )
        );
        // /tiedup fullyrestrain <player>
        root.then(
            Commands.literal("fullyrestrain")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::fullyrestrain)
                )
        );
        // /tiedup enslave <player>
        root.then(
            Commands.literal("enslave")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::enslave)
                )
        );
        // /tiedup free <player>
        root.then(
            Commands.literal("free")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(BondageSubCommand::free)
                )
        );
        // /tiedup adjust <player> <type:gag|blindfold|all> <value>
        root.then(
            Commands.literal("adjust")
                .requires(CommandHelper.REQUIRES_OP)
                .then(
                    Commands.argument("player", EntityArgument.player()).then(
                        Commands.argument("type", StringArgumentType.word())
                            .suggests((ctx, builder) -> {
                                builder.suggest("gag");
                                builder.suggest("blindfold");
                                builder.suggest("all");
                                return builder.buildFuture();
                            })
                            .then(
                                Commands.argument(
                                    "value",
                                    FloatArgumentType.floatArg(-4.0f, 4.0f)
                                ).executes(BondageSubCommand::adjust)
                            )
                    )
                )
        );
    }

    // Command Implementations

    /**
     * /tiedup tie <player>
     *
     * Forcefully tie a player with ropes (default bind).
     * Uses ItemRopes from ModItems.
     */
    private static int tie(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (state.isTiedUp()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " is already tied up"
                    )
                );
            return 0;
        }

        ItemStack ropes = new ItemStack(ModItems.getBind(BindVariant.ROPES));
        state.putBindOn(ropes);

        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            " has been tied up"
                    ),
                true
            );
        SystemMessageManager.sendTiedUp(
            context.getSource().getEntity(),
            targetPlayer
        );

        return 1;
    }

    /**
     * /tiedup untie <player>
     *
     * Forcefully untie a player, removing ALL bondage equipment.
     */
    private static int untie(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (
            !state.isTiedUp() &&
            !state.isGagged() &&
            !state.isBlindfolded() &&
            !state.hasCollar()
        ) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " is not restrained"
                    )
                );
            return 0;
        }

        boolean removed = false;
        if (state.isTiedUp()) {
            state.takeBindOff();
            removed = true;
        }
        if (state.isGagged()) {
            state.takeGagOff();
            removed = true;
        }
        if (state.isBlindfolded()) {
            state.takeBlindfoldOff();
            removed = true;
        }
        if (state.hasCollar()) {
            state.takeCollarOff();
            removed = true;
        }
        if (state.hasEarplugs()) {
            state.takeEarplugsOff();
            removed = true;
        }

        if (removed) {
            CommandHelper.syncPlayerState(targetPlayer, state);

            context
                .getSource()
                .sendSuccess(
                    () ->
                        Component.literal(
                            "\u00a7a" +
                                targetPlayer.getName().getString() +
                                " has been freed from all restraints"
                        ),
                    true
                );
            SystemMessageManager.sendFreed(targetPlayer);

            return 1;
        }

        return 0;
    }

    /**
     * /tiedup gag <player>
     *
     * Forcefully gag a player with cloth gag (default).
     */
    private static int gag(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (state.isGagged()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " is already gagged"
                    )
                );
            return 0;
        }

        ItemStack gag = new ItemStack(ModItems.getGag(GagVariant.CLOTH_GAG));
        state.putGagOn(gag);

        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            " has been gagged"
                    ),
                true
            );
        SystemMessageManager.sendGagged(
            context.getSource().getEntity(),
            targetPlayer
        );

        return 1;
    }

    /**
     * /tiedup blindfold <player>
     *
     * Forcefully blindfold a player with classic blindfold (default).
     */
    private static int blindfold(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (state.isBlindfolded()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " is already blindfolded"
                    )
                );
            return 0;
        }

        ItemStack blindfold = new ItemStack(
            ModItems.getBlindfold(BlindfoldVariant.CLASSIC)
        );
        state.putBlindfoldOn(blindfold);

        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            " has been blindfolded"
                    ),
                true
            );
        SystemMessageManager.sendToTarget(
            context.getSource().getEntity(),
            targetPlayer,
            SystemMessageManager.MessageCategory.BLINDFOLDED
        );

        return 1;
    }

    /**
     * /tiedup collar <player>
     *
     * Give a collar to a player (forces collar even if not tied).
     * The command executor becomes the owner of the collar.
     */
    private static int collar(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (state.hasCollar()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " already has a collar"
                    )
                );
            return 0;
        }

        ItemStack collar = new ItemStack(ModItems.CLASSIC_COLLAR.get());

        if (context.getSource().getEntity() instanceof ServerPlayer executor) {
            ItemCollar collarItem = (ItemCollar) collar.getItem();
            collarItem.addOwner(collar, executor);
        }

        state.putCollarOn(collar);

        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            " has been collared"
                    ),
                true
            );
        SystemMessageManager.sendToTarget(
            context.getSource().getEntity(),
            targetPlayer,
            SystemMessageManager.MessageCategory.COLLARED
        );

        return 1;
    }

    /**
     * /tiedup free <player>
     *
     * Free a player from slavery (removes master relationship).
     * Does NOT remove collar or other equipment.
     */
    private static int free(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (!state.isCaptive()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() + " is not captured"
                    )
                );
            return 0;
        }

        state.free(true);

        PacketSyncBindState statePacket = PacketSyncBindState.fromPlayer(
            targetPlayer
        );
        if (statePacket != null) {
            ModNetwork.sendToPlayer(statePacket, targetPlayer);
        }

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            " has been freed from slavery"
                    ),
                true
            );
        SystemMessageManager.sendFreed(targetPlayer);

        return 1;
    }

    /**
     * /tiedup adjust <player> <type> <value>
     *
     * Adjust the Y position of gags and/or blindfolds on a player.
     * Value range: -4.0 to +4.0 (pixels, 1 pixel = 1/16 block)
     *
     * Types:
     *   - gag: Adjust only gag
     *   - blindfold: Adjust only blindfold
     *   - all: Adjust both gag and blindfold
     */
    private static int adjust(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        String type = StringArgumentType.getString(context, "type");
        float value = FloatArgumentType.getFloat(context, "value");

        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);
        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        boolean adjustedGag = false;
        boolean adjustedBlindfold = false;

        if (type.equals("gag") || type.equals("all")) {
            ItemStack gag = state.getEquipment(
                com.tiedup.remake.v2.BodyRegionV2.MOUTH
            );
            if (!gag.isEmpty()) {
                AdjustmentHelper.setAdjustment(gag, value);
                adjustedGag = true;
            }
        }

        if (type.equals("blindfold") || type.equals("all")) {
            ItemStack blindfold = state.getEquipment(
                com.tiedup.remake.v2.BodyRegionV2.EYES
            );
            if (!blindfold.isEmpty()) {
                AdjustmentHelper.setAdjustment(blindfold, value);
                adjustedBlindfold = true;
            }
        }

        if (
            !type.equals("gag") &&
            !type.equals("blindfold") &&
            !type.equals("all")
        ) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        "Invalid type. Use: gag, blindfold, or all"
                    )
                );
            return 0;
        }

        if (!adjustedGag && !adjustedBlindfold) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " has no " +
                            type +
                            " to adjust"
                    )
                );
            return 0;
        }

        CommandHelper.syncPlayerState(targetPlayer, state);

        String items =
            adjustedGag && adjustedBlindfold
                ? "gag and blindfold"
                : adjustedGag
                    ? "gag"
                    : "blindfold";
        String valueStr = String.format("%.2f", value);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7aAdjusted " +
                            items +
                            " for " +
                            targetPlayer.getName().getString() +
                            " to " +
                            valueStr +
                            " pixels"
                    ),
                true
            );

        return 1;
    }

    /**
     * /tiedup ungag <player>
     *
     * Remove gag from a player.
     */
    private static int ungag(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (!state.isGagged()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() + " is not gagged"
                    )
                );
            return 0;
        }

        state.takeGagOff();
        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            "'s gag has been removed"
                    ),
                true
            );
        SystemMessageManager.sendToTarget(
            context.getSource().getEntity(),
            targetPlayer,
            SystemMessageManager.MessageCategory.UNGAGGED
        );

        return 1;
    }

    /**
     * /tiedup unblind <player>
     *
     * Remove blindfold from a player.
     */
    private static int unblind(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (!state.isBlindfolded()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " is not blindfolded"
                    )
                );
            return 0;
        }

        state.takeBlindfoldOff();
        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            "'s blindfold has been removed"
                    ),
                true
            );
        SystemMessageManager.sendToTarget(
            context.getSource().getEntity(),
            targetPlayer,
            SystemMessageManager.MessageCategory.UNBLINDFOLDED
        );

        return 1;
    }

    /**
     * /tiedup takecollar <player>
     *
     * Remove collar from a player.
     */
    private static int takecollar(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (!state.hasCollar()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " does not have a collar"
                    )
                );
            return 0;
        }

        state.takeCollarOff();
        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            "'s collar has been removed"
                    ),
                true
            );
        SystemMessageManager.sendToTarget(
            context.getSource().getEntity(),
            targetPlayer,
            SystemMessageManager.MessageCategory.UNCOLLARED
        );

        return 1;
    }

    /**
     * /tiedup takeearplugs <player>
     *
     * Remove earplugs from a player.
     */
    private static int takeearplugs(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (!state.hasEarplugs()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " does not have earplugs"
                    )
                );
            return 0;
        }

        state.takeEarplugsOff();
        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            "'s earplugs have been removed"
                    ),
                true
            );
        SystemMessageManager.sendToPlayer(
            targetPlayer,
            SystemMessageManager.MessageCategory.INFO,
            "Your earplugs have been removed!"
        );

        return 1;
    }

    /**
     * /tiedup takeclothes <player>
     *
     * Remove clothes from a player.
     */
    private static int takeclothes(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (!state.hasClothes()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " is not wearing clothes"
                    )
                );
            return 0;
        }

        ItemStack removed = state.takeClothesOff();
        CommandHelper.syncPlayerState(targetPlayer, state);

        if (!removed.isEmpty()) {
            targetPlayer.drop(removed, false);
        }

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "Removed clothes from " +
                            targetPlayer.getName().getString()
                    ),
                true
            );
        return 1;
    }

    /**
     * /tiedup putclothes <player>
     *
     * Put clothes on a player.
     */
    private static int putclothes(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (state.hasClothes()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " already has clothes"
                    )
                );
            return 0;
        }

        ItemStack clothes = new ItemStack(ModItems.CLOTHES.get());
        state.putClothesOn(clothes);
        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            " has been given clothes"
                    ),
                true
            );

        return 1;
    }

    /**
     * /tiedup putearplugs <player>
     *
     * Put earplugs on a player.
     */
    private static int putearplugs(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        if (state.hasEarplugs()) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " already has earplugs"
                    )
                );
            return 0;
        }

        ItemStack earplugs = new ItemStack(
            ModItems.getEarplugs(EarplugsVariant.CLASSIC)
        );
        state.putEarplugsOn(earplugs);
        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            " has been given earplugs"
                    ),
                true
            );
        SystemMessageManager.sendToTarget(
            context.getSource().getEntity(),
            targetPlayer,
            SystemMessageManager.MessageCategory.EARPLUGS_ON
        );

        return 1;
    }

    /**
     * /tiedup fullyrestrain <player>
     *
     * Apply full bondage: bind + gag + blindfold + collar + earplugs
     */
    private static int fullyrestrain(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        int applied = 0;

        if (!state.isTiedUp()) {
            ItemStack ropes = new ItemStack(
                ModItems.getBind(BindVariant.ROPES)
            );
            state.putBindOn(ropes);
            applied++;
        }

        if (!state.isGagged()) {
            ItemStack gag = new ItemStack(
                ModItems.getGag(GagVariant.CLOTH_GAG)
            );
            state.putGagOn(gag);
            applied++;
        }

        if (!state.isBlindfolded()) {
            ItemStack blindfold = new ItemStack(
                ModItems.getBlindfold(BlindfoldVariant.CLASSIC)
            );
            state.putBlindfoldOn(blindfold);
            applied++;
        }

        if (!state.hasCollar()) {
            ItemStack collar = new ItemStack(ModItems.CLASSIC_COLLAR.get());
            if (
                context.getSource().getEntity() instanceof ServerPlayer executor
            ) {
                ItemCollar collarItem = (ItemCollar) collar.getItem();
                collarItem.addOwner(collar, executor);
            }
            state.putCollarOn(collar);
            applied++;
        }

        if (!state.hasEarplugs()) {
            ItemStack earplugs = new ItemStack(
                ModItems.getEarplugs(EarplugsVariant.CLASSIC)
            );
            state.putEarplugsOn(earplugs);
            applied++;
        }

        if (applied == 0) {
            context
                .getSource()
                .sendFailure(
                    Component.literal(
                        targetPlayer.getName().getString() +
                            " is already fully restrained"
                    )
                );
            return 0;
        }

        CommandHelper.syncPlayerState(targetPlayer, state);

        int finalApplied = applied;
        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            " has been fully restrained (" +
                            finalApplied +
                            " items applied)"
                    ),
                true
            );
        SystemMessageManager.sendToPlayer(
            targetPlayer,
            SystemMessageManager.MessageCategory.INFO,
            "You have been fully restrained!"
        );

        return 1;
    }

    /**
     * /tiedup enslave <player>
     *
     * Fully restrain and enslave a player.
     * The command executor becomes the master.
     */
    private static int enslave(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        ServerPlayer targetPlayer = EntityArgument.getPlayer(context, "player");
        PlayerBindState state = PlayerBindState.getInstance(targetPlayer);

        if (state == null) {
            context
                .getSource()
                .sendFailure(Component.literal("Failed to get player state"));
            return 0;
        }

        // First fully restrain
        if (!state.isTiedUp()) {
            ItemStack ropes = new ItemStack(
                ModItems.getBind(BindVariant.ROPES)
            );
            state.putBindOn(ropes);
        }
        if (!state.isGagged()) {
            ItemStack gag = new ItemStack(
                ModItems.getGag(GagVariant.CLOTH_GAG)
            );
            state.putGagOn(gag);
        }
        if (!state.isBlindfolded()) {
            ItemStack blindfold = new ItemStack(
                ModItems.getBlindfold(BlindfoldVariant.CLASSIC)
            );
            state.putBlindfoldOn(blindfold);
        }
        if (!state.hasCollar()) {
            ItemStack collar = new ItemStack(ModItems.CLASSIC_COLLAR.get());
            if (
                context.getSource().getEntity() instanceof ServerPlayer executor
            ) {
                ItemCollar collarItem = (ItemCollar) collar.getItem();
                collarItem.addOwner(collar, executor);
            }
            state.putCollarOn(collar);
        }
        if (!state.hasEarplugs()) {
            ItemStack earplugs = new ItemStack(
                ModItems.getEarplugs(EarplugsVariant.CLASSIC)
            );
            state.putEarplugsOn(earplugs);
        }

        // Capture target (this makes them a captive)
        if (context.getSource().getEntity() instanceof ServerPlayer master) {
            PlayerBindState masterState = PlayerBindState.getInstance(master);
            if (masterState != null && masterState.getCaptorManager() != null) {
                masterState.getCaptorManager().addCaptive(state);
            }
        }

        CommandHelper.syncPlayerState(targetPlayer, state);

        context
            .getSource()
            .sendSuccess(
                () ->
                    Component.literal(
                        "\u00a7a" +
                            targetPlayer.getName().getString() +
                            " has been enslaved"
                    ),
                true
            );
        SystemMessageManager.sendEnslaved(
            context.getSource().getEntity(),
            targetPlayer
        );

        return 1;
    }
}
