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

import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.UpdateEvent;
import dev.crucifixbeta.api.utils.combat.CombatUtil;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.api.utils.move.MoveUtils;
import dev.crucifixbeta.api.utils.player.EntityUtil;
import dev.crucifixbeta.api.utils.player.InventoryUtil;
import dev.crucifixbeta.api.utils.rotation.Rotation;
import dev.crucifixbeta.api.utils.rotation.RotationUtils;
import dev.crucifixbeta.api.utils.world.BlockUtil;
import dev.crucifixbeta.mod.modules.Module;
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 net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.BlockState;
import net.minecraft.block.ButtonBlock;
import net.minecraft.block.FenceGateBlock;
import net.minecraft.entity.Entity;
import net.minecraft.entity.ExperienceOrbEntity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.ArrowEntity;
import net.minecraft.entity.projectile.thrown.ExperienceBottleEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class AntiStand extends Module {
   public static AntiStand INSTANCE;
   private final EnumSetting<AntiStand.Mode> mode = this.add(new EnumSetting<>("Mode", AntiStand.Mode.Button));
   private final SliderSetting delay = this.add(new SliderSetting("Delay", 100, 0, 500).setSuffix("ms"));
   private final SliderSetting range = this.add(new SliderSetting("Range", 5.0, 0.0, 8.0).setSuffix("m"));
   private final SliderSetting placeRange = this.add(new SliderSetting("PlaceRange", 4.5, 0.0, 6.0).setSuffix("m"));
   private final SliderSetting predictTicks = this.add(
      new SliderSetting("PredictTicks", 2.0, 0.0, 50.0, 1.0, () -> this.mode.is(AntiStand.Mode.FenceGate))
   );
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotate", true));
   private final BooleanSetting silent = this.add(new BooleanSetting("Silent", false, () -> this.mode.is(AntiStand.Mode.FenceGate)));
   private final BooleanSetting inventorySwap = this.add(new BooleanSetting("InventorySwap", true));
   private final BooleanSetting ghostHand = this.add(new BooleanSetting("GhostHand", true));
   private final BooleanSetting movementFix = this.add(new BooleanSetting("MovementFix", true, () -> this.mode.is(AntiStand.Mode.Button)));
   private final BooleanSetting autoDisable = this.add(new BooleanSetting("AutoDisable", false, () -> this.mode.is(AntiStand.Mode.FenceGate)));
   private final Timer timer = new Timer();

   public AntiStand() {
      super("AntiStand", Module.Category.Combat);
      this.setChinese("\u53cd\u654c\u4eba\u7ad9\u7acb");
      INSTANCE = this;
   }

   @Override
   public void onEnable() {
      this.timer.reset();
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (!nullCheck() && this.timer.passedMs(this.delay.getValue())) {
         if (!this.inventorySwap.getValue() || EntityUtil.inInventory()) {
            if (this.mode.is(AntiStand.Mode.Button)) {
               this.runButtonMode();
            } else {
               this.runFenceGateMode();
            }
         }
      }
   }

   private void runButtonMode() {
      PlayerEntity var1 = CombatUtil.getClosestEnemy(this.range.getValue());
      if (var1 != null) {
         BlockPos var2 = EntityUtil.getEntityPos(var1, true);
         if (var2.getY() < 320 && !this.shouldSkipButtonPlace(var2)) {
            int var3 = this.getButtonSlot();
            if (var3 != -1) {
               int var4 = mc.player.getInventory().selectedSlot;
               this.doSwap(var3);
               if (this.movementFix.getValue()) {
                  Direction var5 = BlockUtil.allowAirPlace() ? BlockUtil.getClickSide(var2) : BlockUtil.getPlaceSide(var2, this.placeRange.getValue());
                  if (var5 != null) {
                     Vec3d var6 = var2.toCenterPos()
                        .add((double)var5.getVector().getX() * 0.5, (double)var5.getVector().getY() * 0.5, (double)var5.getVector().getZ() * 0.5);
                     Rotation var7 = RotationUtils.calculate(var6);
                     MoveUtils.fixMovement(var7.getYaw());
                  }
               }

               BlockUtil.placeBlock(var2, this.rotate.getValue());
               this.restoreSwap(var4, var3);
               this.timer.reset();
            }
         }
      }
   }

   private void runFenceGateMode() {
      PlayerEntity var1 = this.findTarget();
      if (var1 == null) {
         if (this.autoDisable.getValue()) {
            this.disable();
         }
      } else {
         int var2 = this.findFenceGateSlot();
         if (var2 != -1) {
            this.placeAround(var1, var2);
            this.timer.reset();
         }
      }
   }

   private PlayerEntity findTarget() {
      PlayerEntity var1 = null;
      double var2 = this.range.getValue();

      for (PlayerEntity var5 : mc.world.getPlayers()) {
         if (var5 != mc.player && !var5.isDead() && var5.isAlive()) {
            double var6 = (double)mc.player.distanceTo(var5);
            if (var6 < var2) {
               var2 = var6;
               var1 = var5;
            }
         }
      }

      return var1;
   }

   private void placeAround(PlayerEntity target, int slot) {
      BlockPos var3 = target.getBlockPos();
      int var4 = 0;
      int var5 = Math.max(1, this.predictTicks.getValueInt() > 0 ? 1 : 1);

      for (int var6 = -var5; var6 <= var5; var6++) {
         for (int var7 = -var5; var7 <= var5; var7++) {
            if (var6 != 0 || var7 != 0) {
               BlockPos var8 = var3.add(var6, 0, var7);
               if (!(mc.player.getEyePos().distanceTo(var8.toCenterPos()) > this.placeRange.getValue())) {
                  if (!mc.world.isAir(var8)) {
                     this.toggleGate(var8);
                  } else if (mc.world.getBlockState(var8.down()).isSolidBlock(mc.world, var8.down())) {
                     this.placeGate(var8, slot);
                     if (++var4 >= 8) {
                        return;
                     }
                  }
               }
            }
         }
      }
   }

   private void placeGate(BlockPos pos, int slot) {
      int var3 = mc.player.getInventory().selectedSlot;
      this.doSwap(slot);
      BlockUtil.placeBlock(pos, this.rotate.getValue());
      this.toggleGate(pos);
      this.restoreSwap(var3, slot);
   }

   private void toggleGate(BlockPos pos) {
      BlockState var2 = mc.world.getBlockState(pos);
      if (var2.getBlock() instanceof FenceGateBlock) {
         if (!(mc.player.getEyePos().distanceTo(pos.toCenterPos()) > this.placeRange.getValue())) {
            Direction var3 = BlockUtil.getClickSide(pos);
            if (var3 != null) {
               BlockUtil.clickBlock(pos, var3, this.rotate.getValue());
            }
         }
      }
   }

   private boolean shouldSkipButtonPlace(BlockPos pos) {
      if (!BlockUtil.canReplace(pos)) {
         return true;
      } else {
         if (this.ghostHand.getValue()) {
            if (this.hasEntityIgnoringPlayers(pos)) {
               return true;
            }
         } else if (BlockUtil.hasEntity(pos, false)) {
            return true;
         }

         return this.placeRange.getValue() > 0.0 && mc.player.getEyePos().distanceTo(pos.toCenterPos()) > this.placeRange.getValue()
            ? true
            : BlockUtil.getPlaceSide(pos, this.placeRange.getValue()) == null && !BlockUtil.allowAirPlace();
      }
   }

   private boolean hasEntityIgnoringPlayers(BlockPos pos) {
      for (Entity var3 : BlockUtil.getEntities(new Box(pos))) {
         if (var3 != null
            && var3.isAlive()
            && !(var3 instanceof ItemEntity)
            && !(var3 instanceof ExperienceOrbEntity)
            && !(var3 instanceof ExperienceBottleEntity)
            && !(var3 instanceof ArrowEntity)
            && !(var3 instanceof PlayerEntity)) {
            return true;
         }
      }

      return false;
   }

   private int getButtonSlot() {
      return this.inventorySwap.getValue() ? InventoryUtil.findClassInventorySlot(ButtonBlock.class) : InventoryUtil.findClass(ButtonBlock.class);
   }

   private int findFenceGateSlot() {
      for (int var1 = 0; var1 < 45; var1++) {
         ItemStack var2 = mc.player.getInventory().getStack(var1);
         if (var2.getItem() instanceof BlockItem var4 && var4.getBlock() instanceof FenceGateBlock) {
            return var1;
         }
      }

      return -1;
   }

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

   private void restoreSwap(int oldSlot, int swappedSlot) {
      if (!this.inventorySwap.getValue() && !this.ghostHand.getValue()) {
         InventoryUtil.switchToSlot(oldSlot);
      } else {
         InventoryUtil.inventorySwap(swappedSlot, mc.player.getInventory().selectedSlot);
         EntityUtil.syncInventory();
      }
   }
@Environment(EnvType.CLIENT)
   private static enum Mode {
      Button,
      FenceGate;

}
}
