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

import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.ClientTickEvent;
import dev.crucifixbeta.api.events.impl.TotemEvent;
import dev.crucifixbeta.api.utils.math.Timer;
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.settings.enums.Timing;
import dev.crucifixbeta.mod.modules.settings.impl.BindSetting;
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.BarrelBlock;
import net.minecraft.block.BlastFurnaceBlock;
import net.minecraft.block.Block;
import net.minecraft.block.ChestBlock;
import net.minecraft.block.CrafterBlock;
import net.minecraft.block.DispenserBlock;
import net.minecraft.block.DropperBlock;
import net.minecraft.block.EnderChestBlock;
import net.minecraft.block.FurnaceBlock;
import net.minecraft.block.HopperBlock;
import net.minecraft.block.ShulkerBoxBlock;
import net.minecraft.block.SmokerBlock;
import net.minecraft.entity.Entity;
import net.minecraft.entity.decoration.EndCrystalEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.PickaxeItem;
import net.minecraft.item.SwordItem;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class MainHand extends Module {
   public static MainHand INSTANCE;
   private final EnumSetting<MainHand.MainHandItem> item = this.add(new EnumSetting<>("Item", MainHand.MainHandItem.Totem));
   private final EnumSetting<MainHand.SwapMode> swapMode = this.add(new EnumSetting<>("SwapMode", MainHand.SwapMode.InventorySwap));
   private final SliderSetting slot = this.add(new SliderSetting("Slot", 1.0, 1.0, 9.0, 1.0));
   private final SliderSetting delay = this.add(new SliderSetting("Delay", 50.0, 0.0, 500.0, 1.0));
   private final EnumSetting<Timing> timing = this.add(new EnumSetting<>("Timing", Timing.All));
   private final BooleanSetting mainHandTotem = this.add(new BooleanSetting("MainHandTotem", false).setParent());
   private final BooleanSetting safe = this.add(new BooleanSetting("Safe", true, this.mainHandTotem::isOpen).setParent());
   private final SliderSetting safeHealth = this.add(new SliderSetting("Health", 16.0, 0.0, 36.0, 0.1, this.safe::isOpen));
   private final BooleanSetting lethalCrystal = this.add(new BooleanSetting("LethalCrystal", true, this.safe::isOpen));
   private final BooleanSetting forceUpdate = this.add(new BooleanSetting("ForceUpdate", false, this.mainHandTotem::isOpen));
   private final BooleanSetting withOffhand = this.add(new BooleanSetting("WithOffhand", false, this.mainHandTotem::isOpen));
   private final BooleanSetting onlyPlayer = this.add(new BooleanSetting("OnlyPlayer", false, this.mainHandTotem::isOpen));
   private final SliderSetting playerDistance = this.add(
      new SliderSetting("PlayerDistance", 10.0, 1.0, 30.0, 0.5, () -> this.mainHandTotem.isOpen() && this.onlyPlayer.getValue())
   );
   private final BooleanSetting gapSwitch = this.add(new BooleanSetting("GapSwitch", true).setParent());
   private final BooleanSetting always = this.add(new BooleanSetting("Always", false, this.gapSwitch::isOpen));
   private final BooleanSetting gapOnTotem = this.add(new BooleanSetting("Gap-Totem", true, this.gapSwitch::isOpen));
   private final BooleanSetting gapOnSword = this.add(new BooleanSetting("Gap-Sword", true, this.gapSwitch::isOpen));
   private final BooleanSetting gapOnPick = this.add(new BooleanSetting("Gap-Pickaxe", false, this.gapSwitch::isOpen));
   private final BooleanSetting containerFilter = this.add(new BooleanSetting("ContainerFilter", true, this.gapSwitch::isOpen));
   private final BooleanSetting restoreAfterGap = this.add(new BooleanSetting("RestoreGap", true, this.gapSwitch::isOpen));
   private final SliderSetting eatTicks = this.add(new SliderSetting("EatTicks", 34.0, 1.0, 60.0, 1.0, this.gapSwitch::isOpen));
   private final BooleanSetting chorusSwitch = this.add(new BooleanSetting("ChorusSwitch", true).setParent());
   private final BindSetting chorusKey = this.add(new BindSetting("ChorusKey", -1, this.chorusSwitch::isOpen));
   private final EnumSetting<MainHand.KeyMode> chorusKeyMode = this.add(
      new EnumSetting<>("ChorusKeyMode", MainHand.KeyMode.Hold, this.chorusSwitch::isOpen)
   );
   private final Timer timer = new Timer();
   private boolean eatingApple;
   private int eatTimer;
   private int previousSlot = -1;
   private int swappedContainerSlot = -1;
   private int swappedHotbarSlot = -1;
   private Item eatingItem = Items.AIR;
   private int eatingStartCount = -1;
   private boolean lastChorusKeyPressed;
   private boolean chorusToggled;
   private int previousMainHandSlot = -1;
   private boolean holdingTotemDueToDanger;

   public MainHand() {
      super("MainHand", Module.Category.Combat);
      this.setChinese("\u4e3b\u624b\u7269\u54c1");
      INSTANCE = this;
   }

   @Override
   public String getInfo() {
      return this.item.getValue().name();
   }

   @Override
   public void onDisable() {
      this.stopApple(true);
      this.lastChorusKeyPressed = false;
      this.chorusToggled = false;
      this.previousMainHandSlot = -1;
      this.holdingTotemDueToDanger = false;
   }

   public boolean isUsingItemPauseActive() {
      return this.isOn() && !this.isMultiTaskEnabled() && !nullCheck()
         ? this.eatingApple || this.isActiveFoodUse() || this.shouldGapSwitch() || this.shouldChorusSwitch()
         : false;
   }

   @EventListener
   public void totem(TotemEvent event) {
      if (event.getPlayer() == mc.player && mc.player.getMainHandStack().getItem() == Items.TOTEM_OF_UNDYING) {
         mc.player.getInventory().removeStack(mc.player.getInventory().selectedSlot);
      }
   }

   @EventListener
   public void onTick(ClientTickEvent event) {
      if (!nullCheck()) {
         if ((!this.timing.is(Timing.Pre) || !event.isPost()) && (!this.timing.is(Timing.Post) || !event.isPre())) {
            if (this.eatingApple) {
               if (this.shouldHoldMainHandTotem()) {
                  this.stopApple(true);
                  this.switchToMainHandTotem();
                  this.timer.reset();
               } else {
                  this.updateAppleUse();
               }
            } else if (this.timer.passed((long)this.delay.getValueInt()) && EntityUtil.inInventory()) {
               if (this.restoreMainHandSlot()) {
                  this.timer.reset();
               } else if (this.handleMainHandTotem()) {
                  this.timer.reset();
               } else if (this.shouldChorusSwitch()) {
                  if (this.startApple(Items.CHORUS_FRUIT)) {
                     this.timer.reset();
                  }
               } else if (this.shouldGapSwitch()) {
                  Item var3 = this.findBestApple();
                  if (var3 != null && this.startApple(var3)) {
                     this.timer.reset();
                  }
               } else {
                  Item var2 = this.getDesiredItem();
                  if (var2 != null && this.ensureHotbarSlot(var2, this.slot.getValueInt() - 1)) {
                     this.timer.reset();
                  }
               }
            }
         }
      }
   }

   private boolean handleMainHandTotem() {
      if (this.mainHandTotem.getValue() && this.hasNearbyPlayers()) {
         boolean var1 = this.ensureHotbarSlot(Items.TOTEM_OF_UNDYING, this.slot.getValueInt() - 1);
         if (this.shouldHoldMainHandTotem()) {
            var1 |= this.switchToMainHandTotem();
         }

         return var1;
      } else {
         return false;
      }
   }

   private boolean shouldHoldMainHandTotem() {
      if (this.mainHandTotem.getValue() && this.hasNearbyPlayers()) {
         return mc.player.getOffHandStack().getItem() == Items.TOTEM_OF_UNDYING && !this.withOffhand.getValue()
            ? false
            : !this.safe.getValue() || this.isUnsafe();
      } else {
         return false;
      }
   }

   private boolean switchToMainHandTotem() {
      int var1 = this.findHotbarItem(Items.TOTEM_OF_UNDYING, -1);
      if (var1 == -1) {
         return false;
      } else if (var1 == mc.player.getInventory().selectedSlot && !this.forceUpdate.getValue()) {
         return false;
      } else {
         if (!this.holdingTotemDueToDanger) {
            this.previousMainHandSlot = mc.player.getInventory().selectedSlot;
            this.holdingTotemDueToDanger = true;
         }

         InventoryUtil.switchToSlot(var1);
         return true;
      }
   }

   private boolean restoreMainHandSlot() {
      if (this.holdingTotemDueToDanger && !this.shouldHoldMainHandTotem()) {
         if (this.previousMainHandSlot >= 0 && this.previousMainHandSlot < 9) {
            InventoryUtil.switchToSlot(this.previousMainHandSlot);
         }

         this.previousMainHandSlot = -1;
         this.holdingTotemDueToDanger = false;
         return true;
      } else {
         return false;
      }
   }

   private boolean isUnsafe() {
      return (double)EntityUtil.getHealth(mc.player) < this.safeHealth.getValue() || this.lethalCrystal();
   }

   private boolean lethalCrystal() {
      if (this.lethalCrystal.getValue() && AutoCrystal.INSTANCE != null) {
         for (Entity var2 : CrucifixBeta.THREAD.getEntities()) {
            if (var2 instanceof EndCrystalEntity && mc.player.distanceTo(var2) <= 12.0F) {
               Vec3d var3 = new Vec3d(var2.getX(), var2.getY(), var2.getZ());
               if (AutoCrystal.INSTANCE.calculateDamage(var3, mc.player, mc.player) >= EntityUtil.getHealth(mc.player)) {
                  return true;
               }
            }
         }

         return false;
      } else {
         return false;
      }
   }

   private boolean hasNearbyPlayers() {
      if (!this.onlyPlayer.getValue()) {
         return true;
      } else {
         double var1 = this.playerDistance.getValue();

         for (PlayerEntity var4 : mc.world.getPlayers()) {
            if (var4 != mc.player && (double)mc.player.distanceTo(var4) <= var1) {
               return true;
            }
         }

         return false;
      }
   }

   private Item getDesiredItem() {
      return switch ((MainHand.MainHandItem)this.item.getValue()) {
         case None -> null;
         case Totem -> Items.TOTEM_OF_UNDYING;
         case Crystal -> Items.END_CRYSTAL;
         case Gapple -> this.findBestApple();
         case Shield -> Items.SHIELD;
         case Chorus -> Items.CHORUS_FRUIT;
      };
   }

   private boolean ensureHotbarSlot(Item item, int targetHotbarSlot) {
      targetHotbarSlot = this.clampHotbarSlot(targetHotbarSlot);
      if (mc.player.getInventory().getStack(targetHotbarSlot).getItem() == item) {
         return false;
      } else {
         int var3 = this.findHotbarItem(item, targetHotbarSlot);
         if (var3 != -1) {
            this.swapHotbarSlots(var3, targetHotbarSlot);
            return true;
         } else if (this.swapMode.is(MainHand.SwapMode.HotbarOnly)) {
            return false;
         } else {
            int var4 = this.findInventorySlot(item);
            if (var4 == -1) {
               return false;
            } else {
               mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, var4, targetHotbarSlot, SlotActionType.SWAP, mc.player);
               EntityUtil.syncInventory();
               return true;
            }
         }
      }
   }

   private boolean shouldGapSwitch() {
      if (this.gapSwitch.getValue() && mc.options.useKey.isPressed() && !this.isLookingAtContainer()) {
         Item var1 = mc.player.getMainHandStack().getItem();
         return this.always.getValue()
            || this.gapOnTotem.getValue() && var1 == Items.TOTEM_OF_UNDYING
            || this.gapOnSword.getValue() && var1 instanceof SwordItem
            || this.gapOnPick.getValue() && var1 instanceof PickaxeItem;
      } else {
         return false;
      }
   }

   private boolean shouldChorusSwitch() {
      return this.chorusSwitch.getValue() && this.isChorusKeyActive() && mc.options.useKey.isPressed() && !this.isLookingAtContainer();
   }

   private boolean isActiveFoodUse() {
      if (!mc.player.isUsingItem()) {
         return false;
      } else {
         Item var1 = mc.player.getActiveItem().getItem();
         return var1 == Items.GOLDEN_APPLE || var1 == Items.ENCHANTED_GOLDEN_APPLE || var1 == Items.CHORUS_FRUIT;
      }
   }

   private boolean isLookingAtContainer() {
      if (this.containerFilter.getValue() && !nullCheck() && mc.crosshairTarget instanceof BlockHitResult var2) {
         Block var3 = mc.world.getBlockState(var2.getBlockPos()).getBlock();
         return var3 instanceof ChestBlock
            || var3 instanceof EnderChestBlock
            || var3 instanceof BarrelBlock
            || var3 instanceof ShulkerBoxBlock
            || var3 instanceof HopperBlock
            || var3 instanceof FurnaceBlock
            || var3 instanceof BlastFurnaceBlock
            || var3 instanceof SmokerBlock
            || var3 instanceof DispenserBlock
            || var3 instanceof DropperBlock
            || var3 instanceof CrafterBlock;
      } else {
         return false;
      }
   }

   private boolean isChorusKeyActive() {
      if (this.chorusKey.getValue() == -1) {
         this.lastChorusKeyPressed = false;
         this.chorusToggled = false;
         return false;
      } else {
         boolean var1 = this.chorusKey.isPressed();
         if (this.chorusKeyMode.is(MainHand.KeyMode.Toggle) && var1 && !this.lastChorusKeyPressed) {
            this.chorusToggled = !this.chorusToggled;
         }

         this.lastChorusKeyPressed = var1;
         return this.chorusKeyMode.is(MainHand.KeyMode.Hold) ? var1 : this.chorusToggled;
      }
   }

   private boolean startApple(Item apple) {
      if (mc.player.getMainHandStack().getItem() == apple) {
         this.beginAppleUse(apple);
         return true;
      } else {
         int var2 = mc.player.getInventory().selectedSlot;
         if (this.previousSlot == -1) {
            this.previousSlot = var2;
         }

         int var3 = this.findHotbarItem(apple, -1);
         if (var3 != -1) {
            InventoryUtil.switchToSlot(var3);
         } else {
            if (this.swapMode.is(MainHand.SwapMode.HotbarOnly)) {
               this.previousSlot = -1;
               return false;
            }

            int var4 = this.findInventorySlot(apple);
            if (var4 == -1) {
               this.previousSlot = -1;
               return false;
            }

            mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, var4, var2, SlotActionType.SWAP, mc.player);
            EntityUtil.syncInventory();
            InventoryUtil.switchToSlot(var2);
            this.swappedContainerSlot = var4;
            this.swappedHotbarSlot = var2;
         }

         this.beginAppleUse(apple);
         return true;
      }
   }

   private void beginAppleUse(Item apple) {
      ItemStack var2 = mc.player.getMainHandStack();
      this.eatingApple = true;
      this.eatTimer = 0;
      this.eatingItem = apple;
      this.eatingStartCount = var2.getItem() == apple ? var2.getCount() : -1;
      this.holdUsingItemPause();
      mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND);
   }

   private void updateAppleUse() {
      this.eatTimer++;
      ItemStack var1 = mc.player.getMainHandStack();
      boolean var2 = var1.getItem() == this.eatingItem;
      if (!mc.options.useKey.isPressed()) {
         this.stopApple(this.restoreAfterGap.getValue());
      } else if (this.eatingItem == Items.CHORUS_FRUIT && !this.isChorusKeyActive()) {
         this.stopApple(this.restoreAfterGap.getValue());
      } else if (!var2) {
         this.stopApple(this.restoreAfterGap.getValue());
      } else {
         boolean var3 = var2 && this.eatingStartCount > 0 && var1.getCount() < this.eatingStartCount;
         if (var3) {
            this.eatTimer = 0;
            this.eatingStartCount = var1.getCount();
            mc.interactionManager.interactItem(mc.player, Hand.MAIN_HAND);
         }
      }
   }

   private void stopApple(boolean restore) {
      if (this.eatingApple || this.previousSlot != -1 || this.swappedContainerSlot != -1) {
         if (restore) {
            if (this.swappedContainerSlot != -1 && this.swappedHotbarSlot != -1 && EntityUtil.inInventory()) {
               mc.interactionManager
                  .clickSlot(mc.player.currentScreenHandler.syncId, this.swappedContainerSlot, this.swappedHotbarSlot, SlotActionType.SWAP, mc.player);
               EntityUtil.syncInventory();
            }

            if (this.previousSlot >= 0 && this.previousSlot < 9) {
               InventoryUtil.switchToSlot(this.previousSlot);
            }
         }

         this.eatingApple = false;
         this.eatTimer = 0;
         this.previousSlot = -1;
         this.swappedContainerSlot = -1;
         this.swappedHotbarSlot = -1;
         this.eatingItem = Items.AIR;
         this.eatingStartCount = -1;
         this.releaseUsingItemPause();
      }
   }

   private Item findBestApple() {
      if (InventoryUtil.findItem(Items.ENCHANTED_GOLDEN_APPLE) != -1 || this.findInventorySlot(Items.ENCHANTED_GOLDEN_APPLE) != -1) {
         return Items.ENCHANTED_GOLDEN_APPLE;
      } else {
         return InventoryUtil.findItem(Items.GOLDEN_APPLE) == -1 && this.findInventorySlot(Items.GOLDEN_APPLE) == -1 ? null : Items.GOLDEN_APPLE;
      }
   }

   private boolean isApple(Item item) {
      return item == Items.GOLDEN_APPLE || item == Items.ENCHANTED_GOLDEN_APPLE;
   }

   private int findHotbarItem(Item item, int excludedSlot) {
      for (int var3 = 0; var3 < 9; var3++) {
         if (var3 != excludedSlot && mc.player.getInventory().getStack(var3).getItem() == item) {
            return var3;
         }
      }

      return -1;
   }

   private int findInventorySlot(Item item) {
      ItemStack var2 = mc.player.getOffHandStack();
      if (var2.getItem() == item) {
         return -1;
      } else {
         for (int var3 = 35; var3 >= 9; var3--) {
            ItemStack var4 = mc.player.getInventory().getStack(var3);
            if (var4.getItem() == item) {
               return var3;
            }
         }

         return -1;
      }
   }

   private void swapHotbarSlots(int sourceHotbar, int targetHotbar) {
      if (sourceHotbar != targetHotbar) {
         int var3 = sourceHotbar + 36;
         int var4 = targetHotbar + 36;
         mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, var3, 0, SlotActionType.PICKUP, mc.player);
         mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, var4, 0, SlotActionType.PICKUP, mc.player);
         mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, var3, 0, SlotActionType.PICKUP, mc.player);
         EntityUtil.syncInventory();
      }
   }

   private int clampHotbarSlot(int slot) {
      return Math.max(0, Math.min(8, slot));
   }
@Environment(EnvType.CLIENT)
   public static enum KeyMode {
      Hold,
      Toggle;

}

   @Environment(EnvType.CLIENT)
   public static enum MainHandItem {
      None,
      Totem,
      Crystal,
      Gapple,
      Shield,
      Chorus;

}

   @Environment(EnvType.CLIENT)
   public static enum SwapMode {
      HotbarOnly,
      InventorySwap;

}
}
