package dev.crucifixbeta.mod.modules.impl.exploit;

import asm.accessors.IPlayerMoveC2SPacket;
import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.ClientTickEvent;
import dev.crucifixbeta.api.events.impl.PacketEvent;
import dev.crucifixbeta.api.utils.player.EntityUtil;
import dev.crucifixbeta.api.utils.player.InventoryUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.combat.Criticals;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.EnumSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import io.netty.buffer.Unpooled;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.entity.Entity;
import net.minecraft.entity.decoration.EndCrystalEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Items;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket.PositionAndOnGround;

@Environment(EnvType.CLIENT)
public class MaceSpoof extends Module {
   public static MaceSpoof INSTANCE;
   private final BooleanSetting antiNoFall = this.add(new BooleanSetting("AntiNoFall", false));
   private final EnumSetting<MaceSpoof.Mode> mode = this.add(new EnumSetting<>("Mode", MaceSpoof.Mode.Bounce));
   private final SliderSetting minFallDistance = this.add(
      new SliderSetting("MinFallDist", 1.5, 0.5, 6.0, 0.1, () -> this.mode.is(MaceSpoof.Mode.Bounce))
   );
   private final SliderSetting bounceInterval = this.add(
      new SliderSetting("BounceInterval", 2.0, 1.0, 5.0, 1.0, () -> this.mode.is(MaceSpoof.Mode.Bounce))
   );
   private final SliderSetting timeout = this.add(
      new SliderSetting("Timeout", 200.0, 40.0, 400.0, 5.0, () -> this.mode.is(MaceSpoof.Mode.Bounce))
   );
   private final BooleanSetting fakeHeight = this.add(new BooleanSetting("FakeHeight", false, () -> this.mode.is(MaceSpoof.Mode.Bounce)));
   private final SliderSetting fakeHeightValue = this.add(
      new SliderSetting("FakeHeightValue", 25.0, 0.0, 2000.0, 0.1, () -> this.mode.is(MaceSpoof.Mode.Bounce) && this.fakeHeight.getValue())
   );
   private final BooleanSetting rubberband = this.add(new BooleanSetting("Rubberband", false, () -> this.mode.is(MaceSpoof.Mode.Bounce)));
   private final SliderSetting rubberbandHeight = this.add(
      new SliderSetting("RBHeight", 5.0, 1.0, 50.0, 0.5, () -> this.mode.is(MaceSpoof.Mode.Bounce) && this.rubberband.getValue())
   );
   private final BooleanSetting onlyGround = this.add(
      new BooleanSetting("OnlyGround", true, () -> this.mode.is(MaceSpoof.Mode.Vanilla) || this.mode.is(MaceSpoof.Mode.SwapVanilla))
   );
   private final SliderSetting height = this.add(
      new SliderSetting(
         "Height", 25.0, 0.0, 2000.0, 0.1, () -> this.mode.is(MaceSpoof.Mode.Vanilla) || this.mode.is(MaceSpoof.Mode.SwapVanilla)
      )
   );
   private final BooleanSetting noCrystal = this.add(
      new BooleanSetting("NoCrystal", true, () -> this.mode.is(MaceSpoof.Mode.Swap) || this.mode.is(MaceSpoof.Mode.SwapVanilla))
   );
   private final BooleanSetting inventorySwap = this.add(
      new BooleanSetting(
         "InventorySwap",
         false,
         () -> this.mode.is(MaceSpoof.Mode.Swap) || this.mode.is(MaceSpoof.Mode.Bounce) || this.mode.is(MaceSpoof.Mode.SwapVanilla)
      )
   );
   private final BooleanSetting vanillaOnlyAir = this.onlyAirSetting("OnlyAirVanilla", MaceSpoof.Mode.Vanilla);
   private final BooleanSetting ncpOnlyAir = this.onlyAirSetting("OnlyAirNCP", MaceSpoof.Mode.NCP);
   private final BooleanSetting swapOnlyAir = this.onlyAirSetting("OnlyAirSwap", MaceSpoof.Mode.Swap);
   private final BooleanSetting bounceOnlyAir = this.onlyAirSetting("OnlyAirBounce", MaceSpoof.Mode.Bounce);
   private final BooleanSetting swapVanillaOnlyAir = this.onlyAirSetting("OnlyAirSwapVanilla", MaceSpoof.Mode.SwapVanilla);
   private boolean shouldBounce;
   private double bounceStartY;
   private int lastAttackTick;
   private int lastBounceTick;
   private boolean bounceIgnore;
   private boolean ignore;
   private PlayerInteractEntityC2SPacket lastPacket;
   private boolean ignoreSwapVanilla;
   private boolean ignoreVanilla;

   public MaceSpoof() {
      super("MaceSpoof", Module.Category.Exploit);
      this.setChinese("\u98ce\u9524\u52a0\u4f24");
      INSTANCE = this;
   }

   public static boolean shouldDisableNoFall() {
      return INSTANCE != null && INSTANCE.isOn() && INSTANCE.antiNoFall.getValue();
   }

   public static boolean willHandleAttack(PlayerInteractEntityC2SPacket packet) {
      return INSTANCE != null && INSTANCE.isOn() && INSTANCE.canHandleAttack(packet);
   }

   public static Entity getEntity(PlayerInteractEntityC2SPacket packet) {
      PacketByteBuf var1 = new PacketByteBuf(Unpooled.buffer());
      packet.write(var1);
      return mc.world == null ? null : mc.world.getEntityById(var1.readVarInt());
   }

   public static MaceSpoof.InteractType getInteractType(PlayerInteractEntityC2SPacket packet) {
      PacketByteBuf var1 = new PacketByteBuf(Unpooled.buffer());
      packet.write(var1);
      var1.readVarInt();
      return var1.readEnumConstant(MaceSpoof.InteractType.class);
   }

   @Override
   public void onEnable() {
      if (!nullCheck() && this.mode.is(MaceSpoof.Mode.Bounce)) {
         this.resetState();
      }
   }

   @Override
   public void onDisable() {
      this.resetState();
   }

   @Override
   public String getInfo() {
      if (this.mode.is(MaceSpoof.Mode.Bounce)) {
         if (this.rubberband.getValue()) {
            return "Rubberband";
         }

         if (this.shouldBounce) {
            return "Bounce";
         }

         if (this.fakeHeight.getValue()) {
            return "FakeHeight";
         }
      }

      return this.mode.is(MaceSpoof.Mode.SwapVanilla) ? "Swap+Vanilla" : this.mode.getValue().name();
   }

   @EventListener(
      priority = 200
   )
   public void onPacketSend(PacketEvent.Send event) {
      if (!nullCheck()) {
         if (this.mode.is(MaceSpoof.Mode.Vanilla)) {
            this.handleVanilla(event);
         } else if (this.mode.is(MaceSpoof.Mode.NCP)) {
            this.handleNCP(event);
         } else if (this.mode.is(MaceSpoof.Mode.Swap)) {
            this.handleSwap(event);
         } else if (this.mode.is(MaceSpoof.Mode.Bounce)) {
            this.handleBounce(event);
         } else if (this.mode.is(MaceSpoof.Mode.SwapVanilla)) {
            this.handleSwapVanilla(event);
         }
      }
   }

   @EventListener
   public void onTick(ClientTickEvent event) {
      if (!nullCheck() && !event.isPre() && this.mode.is(MaceSpoof.Mode.Bounce) && !this.rubberband.getValue() && this.shouldBounce) {
         if (mc.player.age - this.lastAttackTick > this.timeout.getValueInt()) {
            this.resetState();
         } else if (mc.player.getY() >= this.bounceStartY - 0.1) {
            this.resetState();
         } else if (mc.player.age - this.lastBounceTick >= this.bounceInterval.getValueInt()) {
            this.performBouncePacket();
            this.lastBounceTick = mc.player.age;
         }
      }
   }

   private void handleVanilla(PacketEvent.Send event) {
      if (!this.ignoreVanilla) {
         if (mc.player.getMainHandStack().getItem() == Items.MACE) {
            if (event.getPacket() instanceof PlayerInteractEntityC2SPacket var3) {
               if (getInteractType(var3) == MaceSpoof.InteractType.ATTACK) {
                  Entity var8 = getEntity(var3);
                  if (!(var8 instanceof EndCrystalEntity)) {
                     if (!this.onlyAirBlocksCurrentMode()) {
                        if (this.onlyAirEnabledCurrentMode() || !this.onlyGround.getValue() || mc.player.isOnGround() || mc.player.getAbilities().flying) {
                           if (!mc.player.isInLava() && !mc.player.isSubmergedInWater() && var8 != null) {
                              event.cancel();
                              this.ignoreVanilla = true;

                              try {
                                 for (int var4 = 0; var4 < 4; var4++) {
                                    this.sendFakeY(0.0);
                                 }

                                 this.sendFakeY(this.height.getValue());
                                 this.sendFakeY(0.0);
                                 mc.getNetworkHandler().sendPacket(var3);
                              } finally {
                                 this.ignoreVanilla = false;
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }

   private boolean canHandleAttack(PlayerInteractEntityC2SPacket packet) {
      if (!nullCheck() && getInteractType(packet) == MaceSpoof.InteractType.ATTACK && !this.onlyAirBlocksCurrentMode()) {
         Entity var2 = getEntity(packet);
         if (var2 != null && var2 != mc.player) {
            if (var2 instanceof PlayerEntity var3 && CrucifixBeta.FRIEND.isFriend(var3)) {
               return false;
            }

            if (this.mode.is(MaceSpoof.Mode.Vanilla)) {
               return mc.player.getMainHandStack().getItem() == Items.MACE
                  && !(var2 instanceof EndCrystalEntity)
                  && (this.onlyAirEnabledCurrentMode() || !this.onlyGround.getValue() || mc.player.isOnGround() || mc.player.getAbilities().flying)
                  && !mc.player.isInLava()
                  && !mc.player.isSubmergedInWater();
            } else if (this.mode.is(MaceSpoof.Mode.Swap)) {
               return this.getMaceSlot() != -1 && (!this.noCrystal.getValue() || !(var2 instanceof EndCrystalEntity));
            } else if (this.mode.is(MaceSpoof.Mode.Bounce)) {
               return this.getMaceSlot() != -1;
            } else {
               return !this.mode.is(MaceSpoof.Mode.SwapVanilla)
                  ? false
                  : this.getMaceSlot() != -1
                     && (!this.noCrystal.getValue() || !(var2 instanceof EndCrystalEntity))
                     && (this.onlyAirEnabledCurrentMode() || !this.onlyGround.getValue() || mc.player.isOnGround() || mc.player.getAbilities().flying)
                     && !mc.player.isInLava()
                     && !mc.player.isSubmergedInWater();
            }
         } else {
            return false;
         }
      } else {
         return false;
      }
   }

   private void handleNCP(PacketEvent.Send event) {
      if (!this.onlyAirBlocksCurrentMode()) {
         if (event.getPacket() instanceof PlayerMoveC2SPacket) {
            ((IPlayerMoveC2SPacket)event.getPacket()).setOnGround(false);
         }
      }
   }

   private void handleSwap(PacketEvent.Send event) {
      if (!this.onlyAirBlocksCurrentMode()) {
         if (!event.isCancelled() && !this.ignore && this.getMaceSlot() != -1) {
            if (event.getPacket() instanceof PlayerInteractEntityC2SPacket var3) {
               if (Criticals.getInteractType(var3) == PlayerInteractEntityC2SPacket.InteractType.ATTACK) {
                  if (!this.noCrystal.getValue() || !(Criticals.getEntity(var3) instanceof EndCrystalEntity)) {
                     this.lastPacket = var3;
                     this.ignore = true;

                     try {
                        this.doSwapAttack();
                     } finally {
                        this.ignore = false;
                     }

                     event.cancel();
                  }
               }
            }
         }
      }
   }

   private void handleBounce(PacketEvent.Send event) {
      if (!this.onlyAirBlocksCurrentMode()) {
         if (!this.bounceIgnore && event.getPacket() instanceof PlayerInteractEntityC2SPacket var3) {
            if (getInteractType(var3) == MaceSpoof.InteractType.ATTACK) {
               Entity var10 = getEntity(var3);
               if (var10 != null && var10 != mc.player) {
                  if (var10 instanceof PlayerEntity var4 && CrucifixBeta.FRIEND.isFriend(var4)) {
                     return;
                  }

                  int var11 = this.getMaceSlot();
                  if (var11 != -1) {
                     event.cancel();
                     int var5 = mc.player.getInventory().selectedSlot;
                     this.bounceIgnore = true;

                     try {
                        this.doSwap(var11);
                        if (this.fakeHeight.getValue()) {
                           for (int var6 = 0; var6 < 4; var6++) {
                              this.sendFakeY(0.0);
                           }

                           this.sendFakeY(this.fakeHeightValue.getValue());
                           this.sendFakeY(0.0);
                        }

                        mc.getNetworkHandler().sendPacket(var3);
                        if (!this.rubberband.getValue()) {
                           if (!this.fakeHeight.getValue() && !this.isValidAttackWindow()) {
                              if (this.shouldBounce) {
                                 this.bounceStartY = mc.player.getY();
                                 this.lastAttackTick = mc.player.age;
                              }

                              return;
                           }

                           this.shouldBounce = true;
                           this.bounceStartY = mc.player.getY();
                           this.lastAttackTick = mc.player.age;
                           this.lastBounceTick = 0;
                           return;
                        }

                        this.performRubberband();
                        this.resetState();
                     } finally {
                        this.swapBack(var11, var5);
                        this.bounceIgnore = false;
                     }
                  }
               }
            }
         }
      }
   }

   private void handleSwapVanilla(PacketEvent.Send event) {
      if (!event.isCancelled() && !this.ignoreSwapVanilla) {
         if (event.getPacket() instanceof PlayerInteractEntityC2SPacket var3) {
            if (getInteractType(var3) == MaceSpoof.InteractType.ATTACK) {
               Entity var10 = getEntity(var3);
               if (var10 != null && (!this.noCrystal.getValue() || !(var10 instanceof EndCrystalEntity))) {
                  if (!this.onlyAirBlocksCurrentMode()) {
                     if (this.onlyAirEnabledCurrentMode() || !this.onlyGround.getValue() || mc.player.isOnGround() || mc.player.getAbilities().flying) {
                        if (!mc.player.isInLava() && !mc.player.isSubmergedInWater()) {
                           int var4 = this.getMaceSlot();
                           if (var4 != -1) {
                              event.cancel();
                              int var5 = mc.player.getInventory().selectedSlot;
                              this.ignoreSwapVanilla = true;

                              try {
                                 this.doSwap(var4);

                                 for (int var6 = 0; var6 < 4; var6++) {
                                    this.sendFakeY(0.0);
                                 }

                                 this.sendFakeY(this.height.getValue());
                                 this.sendFakeY(0.0);
                                 mc.getNetworkHandler().sendPacket(var3);
                              } finally {
                                 this.swapBack(var4, var5);
                                 this.ignoreSwapVanilla = false;
                              }
                           }
                        }
                     }
                  }
               }
            }
         }
      }
   }

   private boolean isValidAttackWindow() {
      return mc.player != null
         && mc.player.fallDistance >= this.minFallDistance.getValueFloat()
         && !mc.player.isOnGround()
         && !mc.player.isClimbing()
         && !mc.player.isFallFlying();
   }

   private void performBouncePacket() {
      if (mc.player != null && mc.getNetworkHandler() != null) {
         mc.getNetworkHandler().sendPacket(new PositionAndOnGround(mc.player.getX(), mc.player.getY() + 1.0E-9, mc.player.getZ(), false));
         mc.player.onLanding();
      }
   }

   private void performRubberband() {
      double var1 = mc.player.getX();
      double var3 = mc.player.getZ();
      double var5 = mc.player.getY();
      double var7 = var5 + this.rubberbandHeight.getValue();
      mc.getNetworkHandler().sendPacket(new PositionAndOnGround(var1, var7, var3, false));
      mc.getNetworkHandler().sendPacket(new PositionAndOnGround(var1, var5, var3, false));

      for (int var9 = 0; var9 < 4; var9++) {
         mc.getNetworkHandler().sendPacket(new PositionAndOnGround(var1, var7, var3, false));
      }
   }

   private void doSwapAttack() {
      if (this.lastPacket != null) {
         int var1 = this.getMaceSlot();
         if (var1 != -1) {
            int var2 = mc.player.getInventory().selectedSlot;
            this.doSwap(var1);
            mc.getNetworkHandler().sendPacket(this.lastPacket);
            this.swapBack(var1, var2);
         }
      }
   }

   private void swapBack(int maceSlot, int oldSlot) {
      if (this.inventorySwap.getValue()) {
         this.doSwap(maceSlot);
         EntityUtil.syncInventory();
      } else {
         this.doSwap(oldSlot);
      }
   }

   private void doSwap(int slot) {
      if (this.inventorySwap.getValue()) {
         InventoryUtil.inventorySwap(slot, mc.player.getInventory().selectedSlot);
      } else {
         InventoryUtil.switchToSlot(slot);
      }
   }

   private int getMaceSlot() {
      return this.inventorySwap.getValue() ? InventoryUtil.findItemInventorySlot(Items.MACE) : InventoryUtil.findItem(Items.MACE);
   }

   private BooleanSetting onlyAirSetting(String name, MaceSpoof.Mode visibleMode) {
      BooleanSetting var3 = this.add(new BooleanSetting(name, false, () -> this.mode.is(visibleMode)));
      var3.setChinese("\u4ec5\u7a7a\u4e2d");
      return var3;
   }

   private boolean onlyAirBlocksCurrentMode() {
      return this.onlyAirEnabledCurrentMode() && mc.player.isOnGround() && !mc.player.getAbilities().flying;
   }

   private boolean onlyAirEnabledCurrentMode() {
      return switch ((MaceSpoof.Mode)this.mode.getValue()) {
         case Vanilla -> this.vanillaOnlyAir.getValue();
         case NCP -> this.ncpOnlyAir.getValue();
         case Swap -> this.swapOnlyAir.getValue();
         case Bounce -> this.bounceOnlyAir.getValue();
         case SwapVanilla -> this.swapVanillaOnlyAir.getValue();
      };
   }

   private void sendFakeY(double offset) {
      mc.getNetworkHandler().sendPacket(new PositionAndOnGround(mc.player.getX(), mc.player.getY() + offset, mc.player.getZ(), false));
   }

   private void resetState() {
      this.shouldBounce = false;
      this.bounceStartY = 0.0;
      this.lastAttackTick = 0;
      this.lastBounceTick = 0;
   }
@Environment(EnvType.CLIENT)
   public static enum InteractType {
      INTERACT,
      ATTACK,
      INTERACT_AT;

}

   @Environment(EnvType.CLIENT)
   public static enum Mode {
      Vanilla,
      NCP,
      Swap,
      Bounce,
      SwapVanilla;

}
}
