package com.tiedup.remake.commands;

import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.tiedup.remake.items.ModItems;
import java.util.Optional;
import net.minecraft.ChatFormatting;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;

/**
 * Key management commands.
 *
 * Commands:
 *   /key claim - Claim the key you're holding
 *   /key unclaim - Remove ownership from your key
 *   /key assign <player> - Assign your key to a player
 *   /key public - Make key publicly usable
 *   /key info - Show key info
 *
 * Must hold a collar key in main hand.
 */
public class KeyCommand {

    private static final String TAG_OWNER = "Owner";
    private static final String TAG_OWNER_NAME = "OwnerName";
    private static final String TAG_TARGET = "Target";
    private static final String TAG_TARGET_NAME = "TargetName";
    private static final String TAG_PUBLIC = "Public";

    public static void register(
        CommandDispatcher<CommandSourceStack> dispatcher
    ) {
        dispatcher.register(createKeyCommand());
    }

    /**
     * Create the key command builder (for use as subcommand of /tiedup).
     * @return The command builder
     */
    public static com.mojang.brigadier.builder.LiteralArgumentBuilder<
        CommandSourceStack
    > createKeyCommand() {
        return Commands.literal("key")
            // /key claim
            .then(Commands.literal("claim").executes(KeyCommand::claim))
            // /key unclaim
            .then(Commands.literal("unclaim").executes(KeyCommand::unclaim))
            // /key assign <player>
            .then(
                Commands.literal("assign").then(
                    Commands.argument(
                        "player",
                        EntityArgument.player()
                    ).executes(KeyCommand::assign)
                )
            )
            // /key public
            .then(Commands.literal("public").executes(KeyCommand::togglePublic))
            // /key info
            .then(Commands.literal("info").executes(KeyCommand::info));
    }

    private static ItemStack getHeldKey(ServerPlayer player) {
        ItemStack held = player.getMainHandItem();
        if (
            held.is(ModItems.COLLAR_KEY.get()) ||
            held.is(ModItems.MASTER_KEY.get())
        ) {
            return held;
        }
        return ItemStack.EMPTY;
    }

    private static int claim(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        CommandSourceStack source = context.getSource();

        Optional<ServerPlayer> playerOpt = CommandHelper.getPlayerOrFail(
            source
        );
        if (playerOpt.isEmpty()) return 0;
        ServerPlayer player = playerOpt.get();

        ItemStack key = getHeldKey(player);
        if (key.isEmpty()) {
            source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
            return 0;
        }

        CompoundTag tag = key.getOrCreateTag();

        // Check if already claimed by someone else
        if (
            tag.hasUUID(TAG_OWNER) &&
            !tag.getUUID(TAG_OWNER).equals(player.getUUID())
        ) {
            source.sendFailure(
                Component.translatable("command.tiedup.key.already_claimed")
            );
            return 0;
        }

        tag.putUUID(TAG_OWNER, player.getUUID());
        tag.putString(TAG_OWNER_NAME, player.getName().getString());

        source.sendSuccess(
            () -> Component.translatable("command.tiedup.key.claimed").withStyle(ChatFormatting.GREEN),
            false
        );

        return 1;
    }

    private static int unclaim(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        CommandSourceStack source = context.getSource();

        Optional<ServerPlayer> playerOpt = CommandHelper.getPlayerOrFail(
            source
        );
        if (playerOpt.isEmpty()) return 0;
        ServerPlayer player = playerOpt.get();

        ItemStack key = getHeldKey(player);
        if (key.isEmpty()) {
            source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
            return 0;
        }

        CompoundTag tag = key.getOrCreateTag();

        if (!tag.hasUUID(TAG_OWNER)) {
            source.sendFailure(Component.translatable("command.tiedup.key.not_claimed"));
            return 0;
        }

        if (!tag.getUUID(TAG_OWNER).equals(player.getUUID())) {
            source.sendFailure(Component.translatable("command.tiedup.key.not_owner"));
            return 0;
        }

        tag.remove(TAG_OWNER);
        tag.remove(TAG_OWNER_NAME);

        source.sendSuccess(
            () -> Component.translatable("command.tiedup.key.unclaimed").withStyle(ChatFormatting.GREEN),
            false
        );

        return 1;
    }

    private static int assign(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        CommandSourceStack source = context.getSource();

        Optional<ServerPlayer> playerOpt = CommandHelper.getPlayerOrFail(
            source
        );
        if (playerOpt.isEmpty()) return 0;
        ServerPlayer player = playerOpt.get();

        ServerPlayer target = EntityArgument.getPlayer(context, "player");

        ItemStack key = getHeldKey(player);
        if (key.isEmpty()) {
            source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
            return 0;
        }

        CompoundTag tag = key.getOrCreateTag();

        // Must be owner to assign
        if (
            tag.hasUUID(TAG_OWNER) &&
            !tag.getUUID(TAG_OWNER).equals(player.getUUID())
        ) {
            source.sendFailure(Component.translatable("command.tiedup.key.not_owner"));
            return 0;
        }

        tag.putUUID(TAG_TARGET, target.getUUID());
        tag.putString(TAG_TARGET_NAME, target.getName().getString());

        source.sendSuccess(
            () ->
                Component.translatable(
                    "command.tiedup.key.assigned", target.getName().getString()
                ).withStyle(ChatFormatting.GREEN),
            false
        );

        return 1;
    }

    private static int togglePublic(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        CommandSourceStack source = context.getSource();

        Optional<ServerPlayer> playerOpt = CommandHelper.getPlayerOrFail(
            source
        );
        if (playerOpt.isEmpty()) return 0;
        ServerPlayer player = playerOpt.get();

        ItemStack key = getHeldKey(player);
        if (key.isEmpty()) {
            source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
            return 0;
        }

        CompoundTag tag = key.getOrCreateTag();

        // Must be owner to toggle
        if (
            tag.hasUUID(TAG_OWNER) &&
            !tag.getUUID(TAG_OWNER).equals(player.getUUID())
        ) {
            source.sendFailure(Component.translatable("command.tiedup.key.not_owner"));
            return 0;
        }

        boolean isPublic = !tag.getBoolean(TAG_PUBLIC);
        tag.putBoolean(TAG_PUBLIC, isPublic);

        source.sendSuccess(
            () ->
                Component.translatable(
                    isPublic ? "command.tiedup.key.now_public" : "command.tiedup.key.now_private"
                ).withStyle(ChatFormatting.GREEN),
            false
        );

        return 1;
    }

    private static int info(CommandContext<CommandSourceStack> context)
        throws CommandSyntaxException {
        CommandSourceStack source = context.getSource();

        Optional<ServerPlayer> playerOpt = CommandHelper.getPlayerOrFail(
            source
        );
        if (playerOpt.isEmpty()) return 0;
        ServerPlayer player = playerOpt.get();

        ItemStack key = getHeldKey(player);
        if (key.isEmpty()) {
            source.sendFailure(Component.translatable("command.tiedup.key.must_hold_key"));
            return 0;
        }

        CompoundTag tag = key.getOrCreateTag();

        source.sendSuccess(
            () -> Component.translatable("command.tiedup.key.info_header").withStyle(ChatFormatting.GOLD),
            false
        );

        String ownerName = tag.getString(TAG_OWNER_NAME);
        source.sendSuccess(
            () ->
                Component.translatable(
                    "command.tiedup.key.info_owner",
                    ownerName.isEmpty() ? "Not claimed" : ownerName
                ).withStyle(ChatFormatting.GRAY),
            false
        );

        String targetName = tag.getString(TAG_TARGET_NAME);
        source.sendSuccess(
            () ->
                Component.translatable(
                    "command.tiedup.key.info_assigned",
                    targetName.isEmpty() ? "Not assigned" : targetName
                ).withStyle(ChatFormatting.GRAY),
            false
        );

        boolean isPublic = tag.getBoolean(TAG_PUBLIC);
        source.sendSuccess(
            () -> Component.translatable("command.tiedup.key.info_public", isPublic ? "Yes" : "No").withStyle(ChatFormatting.GRAY),
            false
        );

        return 1;
    }
}
