package com.tiedup.remake.commands;

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.items.clothes.GenericClothes;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;

/**
 * Command handler for clothes configuration.
 *
 * Subcommands:
 *   /tiedup clothes url set <url>  - Set dynamic texture URL on held clothes
 *   /tiedup clothes url reset      - Remove dynamic texture URL
 *   /tiedup clothes fullskin       - Toggle full-skin mode
 *   /tiedup clothes smallarms      - Toggle small arms forcing
 *   /tiedup clothes keephead       - Toggle keep head mode (preserves wearer's head)
 *   /tiedup clothes layer <part>   - Toggle layer visibility
 *
 * All commands operate on the clothes item held in main hand.
 */
public class ClothesCommand {

    /**
     * Create the /tiedup clothes ... command tree.
     *
     * @return The command builder
     */
    public static LiteralArgumentBuilder<
        CommandSourceStack
    > createClothesCommand() {
        return Commands.literal("clothes")
            // /tiedup clothes url set <url>
            // /tiedup clothes url reset
            .then(
                Commands.literal("url")
                    .then(
                        Commands.literal("set").then(
                            Commands.argument(
                                "url",
                                StringArgumentType.greedyString()
                            ).executes(ClothesCommand::setUrl)
                        )
                    )
                    .then(
                        Commands.literal("reset").executes(
                            ClothesCommand::resetUrl
                        )
                    )
            )
            // /tiedup clothes fullskin
            .then(
                Commands.literal("fullskin").executes(
                    ClothesCommand::toggleFullSkin
                )
            )
            // /tiedup clothes smallarms
            .then(
                Commands.literal("smallarms").executes(
                    ClothesCommand::toggleSmallArms
                )
            )
            // /tiedup clothes keephead
            .then(
                Commands.literal("keephead").executes(
                    ClothesCommand::toggleKeepHead
                )
            )
            // /tiedup clothes layer <part>
            .then(
                Commands.literal("layer").then(
                    Commands.argument("part", StringArgumentType.word())
                        .suggests((ctx, builder) -> {
                            builder.suggest("head");
                            builder.suggest("body");
                            builder.suggest("leftarm");
                            builder.suggest("rightarm");
                            builder.suggest("leftleg");
                            builder.suggest("rightleg");
                            return builder.buildFuture();
                        })
                        .executes(ClothesCommand::toggleLayer)
                )
            );
    }

    /**
     * Get the clothes ItemStack from player's main hand.
     * Returns null and sends error message if not holding clothes.
     */
    private static ItemStack getHeldClothes(
        CommandContext<CommandSourceStack> ctx
    ) throws CommandSyntaxException {
        ServerPlayer player = ctx.getSource().getPlayerOrException();
        ItemStack held = player.getMainHandItem();

        if (held.isEmpty() || !(held.getItem() instanceof GenericClothes)) {
            ctx
                .getSource()
                .sendFailure(
                    Component.translatable("command.tiedup.clothes.not_holding")
                );
            return null;
        }
        return held;
    }

    /**
     * /tiedup clothes url set <url>
     */
    private static int setUrl(CommandContext<CommandSourceStack> ctx)
        throws CommandSyntaxException {
        ItemStack clothes = getHeldClothes(ctx);
        if (clothes == null) return 0;

        String url = StringArgumentType.getString(ctx, "url");

        // Basic URL validation
        if (!url.startsWith("https://")) {
            ctx
                .getSource()
                .sendFailure(
                    Component.translatable(
                        "command.tiedup.clothes.url_must_https"
                    )
                );
            return 0;
        }

        // Check URL length
        if (url.length() > 2048) {
            ctx
                .getSource()
                .sendFailure(
                    Component.translatable(
                        "command.tiedup.clothes.url_too_long"
                    )
                );
            return 0;
        }

        GenericClothes item = (GenericClothes) clothes.getItem();
        item.setDynamicTextureUrl(clothes, url);

        ctx
            .getSource()
            .sendSuccess(
                () -> Component.translatable("command.tiedup.clothes.url_set"),
                false
            );
        return 1;
    }

    /**
     * /tiedup clothes url reset
     */
    private static int resetUrl(CommandContext<CommandSourceStack> ctx)
        throws CommandSyntaxException {
        ItemStack clothes = getHeldClothes(ctx);
        if (clothes == null) return 0;

        GenericClothes item = (GenericClothes) clothes.getItem();
        item.removeDynamicTextureUrl(clothes);

        ctx
            .getSource()
            .sendSuccess(
                () ->
                    Component.translatable("command.tiedup.clothes.url_reset"),
                false
            );
        return 1;
    }

    /**
     * /tiedup clothes fullskin
     */
    private static int toggleFullSkin(CommandContext<CommandSourceStack> ctx)
        throws CommandSyntaxException {
        ItemStack clothes = getHeldClothes(ctx);
        if (clothes == null) return 0;

        GenericClothes item = (GenericClothes) clothes.getItem();
        boolean newState = !item.isFullSkinEnabled(clothes);
        item.setFullSkinEnabled(clothes, newState);

        String stateKey = newState ? "enabled" : "disabled";
        ctx
            .getSource()
            .sendSuccess(
                () ->
                    Component.translatable(
                        "command.tiedup.clothes.fullskin_" + stateKey
                    ),
                false
            );
        return 1;
    }

    /**
     * /tiedup clothes smallarms
     */
    private static int toggleSmallArms(CommandContext<CommandSourceStack> ctx)
        throws CommandSyntaxException {
        ItemStack clothes = getHeldClothes(ctx);
        if (clothes == null) return 0;

        GenericClothes item = (GenericClothes) clothes.getItem();
        boolean newState = !item.shouldForceSmallArms(clothes);
        item.setForceSmallArms(clothes, newState);

        String stateKey = newState ? "enabled" : "disabled";
        ctx
            .getSource()
            .sendSuccess(
                () ->
                    Component.translatable(
                        "command.tiedup.clothes.smallarms_" + stateKey
                    ),
                false
            );
        return 1;
    }

    /**
     * /tiedup clothes keephead
     * When enabled, the wearer's head/hat is preserved instead of being replaced by clothes.
     */
    private static int toggleKeepHead(CommandContext<CommandSourceStack> ctx)
        throws CommandSyntaxException {
        ItemStack clothes = getHeldClothes(ctx);
        if (clothes == null) return 0;

        GenericClothes item = (GenericClothes) clothes.getItem();
        boolean newState = !item.isKeepHeadEnabled(clothes);
        item.setKeepHeadEnabled(clothes, newState);

        String stateKey = newState ? "enabled" : "disabled";
        ctx
            .getSource()
            .sendSuccess(
                () ->
                    Component.translatable(
                        "command.tiedup.clothes.keephead_" + stateKey
                    ),
                false
            );
        return 1;
    }

    /**
     * /tiedup clothes layer <part>
     */
    private static int toggleLayer(CommandContext<CommandSourceStack> ctx)
        throws CommandSyntaxException {
        ItemStack clothes = getHeldClothes(ctx);
        if (clothes == null) return 0;

        String part = StringArgumentType.getString(ctx, "part").toLowerCase();
        String layerKey = mapPartToLayerKey(part);

        if (layerKey == null) {
            ctx
                .getSource()
                .sendFailure(
                    Component.translatable(
                        "command.tiedup.clothes.unknown_layer",
                        part
                    )
                );
            return 0;
        }

        GenericClothes item = (GenericClothes) clothes.getItem();
        boolean newState = !item.isLayerEnabled(clothes, layerKey);
        item.setLayerEnabled(clothes, layerKey, newState);

        String stateKey = newState ? "visible" : "hidden";
        ctx
            .getSource()
            .sendSuccess(
                () ->
                    Component.translatable(
                        "command.tiedup.clothes.layer_" + stateKey,
                        part
                    ),
                false
            );
        return 1;
    }

    /**
     * Map user-friendly part name to NBT layer key.
     */
    private static String mapPartToLayerKey(String part) {
        return switch (part) {
            case "head" -> GenericClothes.LAYER_HEAD;
            case "body" -> GenericClothes.LAYER_BODY;
            case "leftarm" -> GenericClothes.LAYER_LEFT_ARM;
            case "rightarm" -> GenericClothes.LAYER_RIGHT_ARM;
            case "leftleg" -> GenericClothes.LAYER_LEFT_LEG;
            case "rightleg" -> GenericClothes.LAYER_RIGHT_LEG;
            default -> null;
        };
    }
}
