package land.chipmunk.chipmunkmod.mixin;

import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.mojang.brigadier.CommandDispatcher;
import land.chipmunk.chipmunkmod.ChipmunkMod;
import land.chipmunk.chipmunkmod.command.CommandManager;
import land.chipmunk.chipmunkmod.listeners.Listener;
import land.chipmunk.chipmunkmod.listeners.ListenerManager;
import land.chipmunk.chipmunkmod.modules.*;
import land.chipmunk.chipmunkmod.modules.custom_chat.CustomChat;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.hud.InGameHud;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.command.CommandSource;
import net.minecraft.network.packet.s2c.play.*;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.resource.featuretoggle.FeatureSet;
import net.minecraft.text.PlainTextContent;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableTextContent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.Vec3d;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(value = ClientPlayNetworkHandler.class, priority = 1001)
public abstract class ClientPlayNetworkHandlerMixin {
    @Final
    @Shadow
    private FeatureSet enabledFeatures;
    @Final
    @Shadow
    private DynamicRegistryManager.Immutable combinedDynamicRegistries;
    @Shadow
    private CommandDispatcher<CommandSource> commandDispatcher;

    @Shadow public abstract ClientWorld getWorld ();

    @Inject(method = "onGameJoin", at = @At("TAIL"))
    private void onGameJoin (final GameJoinS2CPacket packet, final CallbackInfo ci) {
        final CommandRegistryAccess commandRegistryAccess = CommandRegistryAccess.of(this.combinedDynamicRegistries, this.enabledFeatures);

        KaboomCheck.INSTANCE.onJoin();
        CommandManager.INSTANCE = new CommandManager(ChipmunkMod.CONFIG.commands.prefix, commandRegistryAccess);
        SelfCare.INSTANCE.onJoin();
        CommandCore.INSTANCE.init();
        SongPlayer.INSTANCE.coreReady();
        RainbowName.INSTANCE.init();
        ChomeNSBotCommandSuggestions.INSTANCE.init();
    }

    @Inject(method = "onCommandTree", at = @At("TAIL"))
    private void onCommandTree (final CommandTreeS2CPacket packet, final CallbackInfo ci) {
        KaboomCheck.INSTANCE.onCommandTree(this.commandDispatcher);
    }

    @Inject(
            method = "onGameMessage",
            at = @At(
                    value = "INVOKE",
                    target = "Lnet/minecraft/client/network/message/MessageHandler;onGameMessage(Lnet/minecraft/text/Text;Z)V"
            ),
            cancellable = true
    )
    private void onGameMessage (final GameMessageS2CPacket packet, final CallbackInfo ci) {
        final Text message = packet.content();

        if (
                (
                        RainbowName.INSTANCE.enabled &&
                                (
                                        message.getString().startsWith("Your nickname is now ") ||
                                                message.getString().startsWith("Nickname changed.")
                                )
                ) ||
                        (
                                message.getContent() instanceof final TranslatableTextContent translatableTextContent &&
                                        (
                                                translatableTextContent.getKey().equals("advMode.setCommand.success")
                                                        || translatableTextContent.getKey().equals("قيادة المجموعة: %s")
                                        )
                        )
        ) {
            ci.cancel();
            return;
        }

        for (final Listener listener : ListenerManager.listeners) {
            listener.chatMessageReceived(message);
        }

        if (
                message.getSiblings().size() > 1
                        && message.getSiblings().getFirst().getContent() instanceof final PlainTextContent textContent
                        && textContent.string().equals(ChomeNSBotCommandSuggestions.REQUEST_SUGGESTIONS_ID)
        ) ci.cancel();
    }

    @WrapOperation(
            method = "onOverlayMessage",
            at = @At(
                    value = "INVOKE",
                    target = "Lnet/minecraft/client/gui/hud/InGameHud;setOverlayMessage(Lnet/minecraft/text/Text;Z)V"
            )
    )
    private void onOverlayMessage (final InGameHud instance, final Text message, final boolean tinted, final Operation<Void> original) {
        for (final Listener listener : ListenerManager.listeners) {
            listener.overlayMessageReceived(message);
        }

        original.call(instance, message, tinted);

        // checking for the chomens bot selector message doesn't really
        // do much here since the message is just an empty string
        // that gets sent only when you join (or the bot restarts),
        // so I do not ignore them
    }

    @Inject(method = "sendChatMessage", at = @At("HEAD"), cancellable = true)
    private void sendChatMessage (final String content, final CallbackInfo ci) {
        final CommandManager commandManager = CommandManager.INSTANCE;
        if (content.startsWith(commandManager.prefix)) {
            commandManager.executeCommand(content.substring(commandManager.prefix.length()));
            ci.cancel();
            return;
        }

        if (Chat.NEXT_CHAT_PLAYER.get()) {
            Chat.NEXT_CHAT_PLAYER.set(false);
            return;
        }

        CustomChat.INSTANCE.chat(content);
        ci.cancel();
    }

    @Inject(method = "onPlayerPositionLook", at = @At("TAIL"))
    private void setPosition (final PlayerPositionLookS2CPacket packet, final CallbackInfo ci) {
        final Vec3d position = packet.change().position();
        
        final BlockPos origin = CommandCore.INSTANCE.origin;
        if (origin == null) {
            CommandCore.INSTANCE.move(position);
            return;
        }
        
        final MinecraftClient client = MinecraftClient.getInstance();
        final ClientPlayerEntity player = client.player;
        
        if (player == null) return;
        
        final int distanceSquared = player.getChunkPos().getSquaredDistance(new ChunkPos(origin));
        final int distance = (int) Math.sqrt(distanceSquared);

        if (distance > getWorld().getSimulationDistance()) {
            CommandCore.INSTANCE.alreadyFilled = true;
            CommandCore.INSTANCE.move(position);
        }
    }
}
