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

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.KeyboardInputEvent;
import dev.crucifixbeta.api.events.impl.MoveEvent;
import dev.crucifixbeta.api.events.impl.RotationEvent;
import dev.crucifixbeta.api.events.impl.UpdateEvent;
import dev.crucifixbeta.api.events.impl.UpdateRotateEvent;
import dev.crucifixbeta.api.utils.math.MathUtil;
import dev.crucifixbeta.mod.features.SpearPort;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.client.AntiCheat;
import dev.crucifixbeta.mod.modules.settings.enums.Placement;
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.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Items;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class TargetFollow extends Module {
   public static TargetFollow INSTANCE;
   private final SliderSetting range = this.add(new SliderSetting("Range", 50.0, 1.0, 250.0, 1.0));
   private final BooleanSetting lockTarget = this.add(new BooleanSetting("LockTarget", true));
   private final EnumSetting<TargetFollow.TargetPriority> priority = this.add(
      new EnumSetting<>("Priority", TargetFollow.TargetPriority.ShortDistance)
   );
   private final BooleanSetting maceMode = this.add(new BooleanSetting("MaceMode", false).setParent());
   private final SliderSetting maceRange = this.add(new SliderSetting("MaceRange", 5.0, 1.0, 12.0, 0.5, this.maceMode::isOpen));
   private final BooleanSetting spearMode = this.add(new BooleanSetting("SpearMode", false).setParent());
   private final SliderSetting spearMinRange = this.add(new SliderSetting("SpearMinRange", 4.0, 0.0, 32.0, 0.5, this.spearMode::isOpen));
   private final SliderSetting spearMaxRange = this.add(new SliderSetting("SpearMaxRange", 32.0, 1.0, 80.0, 0.5, this.spearMode::isOpen));
   private final BooleanSetting onlyElytra = this.add(new BooleanSetting("OnlyElytra", true));
   private final BooleanSetting autoMove = this.add(new BooleanSetting("AutoMove", true));
   private final BooleanSetting autoMovementSync = this.add(new BooleanSetting("AutoMovementSync", true));
   private PlayerEntity currentTarget;
   private Float elytraFollowYaw;
   private Float elytraFollowPitch;
   private boolean wasMovementSyncEnabled;
   private boolean wasForwardKeyPressed;
   private float wasMovementForward;
   private boolean wasPressingForward;
   private float wasMovementSideways;

   public TargetFollow() {
      super("TargetFollow", Module.Category.Movement);
      this.setChinese("\u81ea\u52a8\u8ffd\u4eba");
      INSTANCE = this;
   }

   @Override
   public void onEnable() {
      if (!nullCheck()) {
         if (this.autoMovementSync.getValue()) {
            this.wasMovementSyncEnabled = MovementSync.INSTANCE.isOn();
            if (!this.wasMovementSyncEnabled) {
               MovementSync.INSTANCE.enable();
            }
         }

         this.wasForwardKeyPressed = mc.options.forwardKey.isPressed();
         this.wasMovementForward = mc.player.input.movementForward;
         this.wasPressingForward = mc.player.input.pressingForward;
         this.wasMovementSideways = mc.player.input.movementSideways;
      }
   }

   @Override
   public void onDisable() {
      if (this.autoMovementSync.getValue() && !this.wasMovementSyncEnabled && MovementSync.INSTANCE != null) {
         MovementSync.INSTANCE.disable();
      }

      this.wasMovementSyncEnabled = false;
      this.currentTarget = null;
      this.elytraFollowYaw = null;
      this.elytraFollowPitch = null;
      if (mc.player != null) {
         mc.options.forwardKey.setPressed(this.wasForwardKeyPressed);
         mc.player.input.movementForward = this.wasMovementForward;
         mc.player.input.pressingForward = this.wasPressingForward;
         mc.player.input.movementSideways = this.wasMovementSideways;
      }
   }

   @Override
   public String getInfo() {
      return this.currentTarget == null ? null : this.currentTarget.getName().getString();
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (!nullCheck()) {
         this.currentTarget = this.getTarget(this.range.getValue());
         if (this.currentTarget != null && this.autoMove.getValue() && this.shouldTrack()) {
            this.forceForward();
            this.updateElytraRotation();
         } else {
            this.clearElytraRotation();
         }
      }
   }

   @EventListener(
      priority = -999
   )
   public void onRotate(RotationEvent event) {
      if (!nullCheck() && this.currentTarget != null && this.shouldTrack()) {
         Vec3d var2 = MathUtil.getClosestPointToBox(mc.player.getEyePos(), this.currentTarget.getBoundingBox());
         event.setTarget(var2, 1.0F, 1.0F);
         if (this.autoMove.getValue()) {
            this.forceForward();
         }
      }
   }

   @EventListener(
      priority = 10000
   )
   public void onUpdateRotate(UpdateRotateEvent event) {
      if (!nullCheck() && this.currentTarget != null && this.autoMove.getValue() && this.shouldTrack() && this.isElytraFlying()) {
         this.updateElytraRotation();
         if (this.elytraFollowYaw != null) {
            event.setYaw(this.elytraFollowYaw);
         }

         if (this.elytraFollowPitch != null) {
            event.setPitch(this.elytraFollowPitch);
         }
      }
   }

   @EventListener
   public void onMove(MoveEvent event) {
      if (!nullCheck() && this.currentTarget != null && this.autoMove.getValue() && !this.isGrim() && this.shouldTrack() && mc.player.isOnGround()) {
         float var2 = mc.player.getYaw();
         double var3 = 0.2873;
         event.setX((double)(-MathHelper.sin(var2 * (float) (Math.PI / 180.0))) * var3);
         event.setZ((double)MathHelper.cos(var2 * (float) (Math.PI / 180.0)) * var3);
      }
   }

   @EventListener(
      priority = -999
   )
   public void onKeyInput(KeyboardInputEvent event) {
      if (!nullCheck() && this.currentTarget != null && this.autoMove.getValue() && this.isGrim() && this.shouldTrack()) {
         mc.player.input.movementSideways = 0.0F;
         mc.player.input.movementForward = 1.0F;
      }
   }

   private void forceForward() {
      mc.options.forwardKey.setPressed(true);
      mc.player.input.movementForward = 1.0F;
      mc.player.input.pressingForward = true;
   }

   public Float getElytraFollowYaw() {
      if (this.isOn() && this.currentTarget != null && this.autoMove.getValue() && this.shouldTrack() && this.isElytraFlying()) {
         this.updateElytraRotation();
         return this.elytraFollowYaw;
      } else {
         return null;
      }
   }

   public Float getElytraFollowPitch() {
      if (this.isOn() && this.currentTarget != null && this.autoMove.getValue() && this.shouldTrack() && this.isElytraFlying()) {
         this.updateElytraRotation();
         return this.elytraFollowPitch;
      } else {
         return null;
      }
   }

   private void updateElytraRotation() {
      if (this.currentTarget == null) {
         this.clearElytraRotation();
      } else {
         Vec3d var1 = mc.player.getEyePos();
         Vec3d var2 = MathUtil.getClosestPointToBox(var1, this.currentTarget.getBoundingBox());
         double var3 = var2.x - var1.x;
         double var5 = var2.y - var1.y;
         double var7 = var2.z - var1.z;
         double var9 = Math.sqrt(var3 * var3 + var7 * var7);
         if (var9 < 1.0E-4) {
            this.elytraFollowYaw = mc.player.getYaw();
            this.elytraFollowPitch = (float)MathHelper.clamp(-Math.toDegrees(Math.atan2(var5, 1.0E-4)), -89.0, 89.0);
         } else {
            this.elytraFollowYaw = (float)MathHelper.wrapDegrees(Math.toDegrees(Math.atan2(var7, var3)) - 90.0);
            this.elytraFollowPitch = (float)MathHelper.clamp(-Math.toDegrees(Math.atan2(var5, var9)), -89.0, 89.0);
         }
      }
   }

   private void clearElytraRotation() {
      this.elytraFollowYaw = null;
      this.elytraFollowPitch = null;
   }

   private boolean isElytraFlying() {
      return ElytraFly.INSTANCE != null && ElytraFly.INSTANCE.isOn() && ElytraFly.INSTANCE.isFallFlying();
   }

   private boolean shouldTrack() {
      return !this.onlyElytra.getValue() || ElytraFly.INSTANCE != null && ElytraFly.INSTANCE.isFallFlying();
   }

   private boolean isGrim() {
      return AntiCheat.INSTANCE != null && AntiCheat.INSTANCE.placement.is(Placement.Grim);
   }

   private PlayerEntity getTarget(double range) {
      if (this.lockTarget.getValue() && this.isValidTarget(this.currentTarget, range, false)) {
         return this.currentTarget;
      } else {
         PlayerEntity var3 = this.getBestPlayer(range, true);
         return var3 != null ? var3 : this.getBestPlayer(range, false);
      }
   }

   private PlayerEntity getBestPlayer(double range, boolean weaponFilter) {
      PlayerEntity var4 = null;

      for (Entity var6 : CrucifixBeta.THREAD.getEntities()) {
         if (var6 instanceof PlayerEntity) {
            PlayerEntity var7 = (PlayerEntity)var6;
            if (this.isValidTarget(var7, range, weaponFilter) && (var4 == null || this.isBetterTarget(var7, var4))) {
               var4 = var7;
            }
         }
      }

      return var4;
   }

   private boolean isValidTarget(PlayerEntity player, double range, boolean weaponFilter) {
      if (player != null && player != mc.player && player.isAlive() && !player.isSpectator()) {
         if (CrucifixBeta.FRIEND.isFriend(player.getName().getString())) {
            return false;
         } else {
            double var5 = (double)mc.player.distanceTo(player);
            return var5 > range ? false : !weaponFilter || this.matchesWeaponMode(var5);
         }
      } else {
         return false;
      }
   }

   private boolean matchesWeaponMode(double distance) {
      boolean var3 = this.maceMode.getValue() || this.spearMode.getValue();
      if (!var3) {
         return true;
      } else {
         boolean var4 = this.maceMode.getValue() && this.hasMace() && distance <= this.maceRange.getValue();
         boolean var5 = this.spearMode.getValue() && this.hasSpear() && distance >= this.spearMinRange.getValue() && distance <= this.spearMaxRange.getValue();
         return var4 || var5;
      }
   }

   private boolean isBetterTarget(PlayerEntity candidate, PlayerEntity current) {
      return switch ((TargetFollow.TargetPriority)this.priority.getValue()) {
         case ShortDistance -> mc.player.distanceTo(candidate) < mc.player.distanceTo(current);
         case LongDistance -> mc.player.distanceTo(candidate) > mc.player.distanceTo(current);
         case LowHealth -> this.getHealth(candidate) < this.getHealth(current);
         case HighHealth -> this.getHealth(candidate) > this.getHealth(current);
         case TotemPops -> CrucifixBeta.POP.getPop(candidate) > CrucifixBeta.POP.getPop(current);
      };
   }

   private float getHealth(PlayerEntity player) {
      return player.getHealth() + player.getAbsorptionAmount();
   }

   private boolean hasMace() {
      if (!mc.player.getMainHandStack().isOf(Items.MACE) && !mc.player.getOffHandStack().isOf(Items.MACE)) {
         for (int var1 = 0; var1 < 9; var1++) {
            if (mc.player.getInventory().getStack(var1).isOf(Items.MACE)) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }

   private boolean hasSpear() {
      return SpearPort.isSpear(mc.player.getMainHandStack())
         || SpearPort.isSpear(mc.player.getOffHandStack())
         || SpearPort.findSpearHotbarSlot() != -1
         || this.hasTrident();
   }

   private boolean hasTrident() {
      if (!mc.player.getMainHandStack().isOf(Items.TRIDENT) && !mc.player.getOffHandStack().isOf(Items.TRIDENT)) {
         for (int var1 = 0; var1 < 9; var1++) {
            if (mc.player.getInventory().getStack(var1).isOf(Items.TRIDENT)) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
@Environment(EnvType.CLIENT)
   private static enum TargetPriority {
      ShortDistance,
      LongDistance,
      LowHealth,
      HighHealth,
      TotemPops;

}
}
