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.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.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.network.packet.c2s.play.PickFromInventoryC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket.Action;
import net.minecraft.screen.slot.SlotActionType;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class Offhand extends Module {
   public static Offhand INSTANCE;
   private final EnumSetting<Offhand.OffhandItem> item = this.add(new EnumSetting<>("Item", Offhand.OffhandItem.Totem));
   private final BooleanSetting safe = this.add(new BooleanSetting("Safe", true).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 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", false, 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 chorusSwitch = this.add(new BooleanSetting("ChorusSwitch", true).setParent());
   private final BindSetting chorusKey = this.add(new BindSetting("ChorusKey", -1, this.chorusSwitch::isOpen));
   private final EnumSetting<Offhand.KeyMode> chorusKeyMode = this.add(
      new EnumSetting<>("ChorusKeyMode", Offhand.KeyMode.Hold, this.chorusSwitch::isOpen)
   );
   private final EnumSetting<Offhand.SwapMode> swapMode = this.add(new EnumSetting<>("SwapMode", Offhand.SwapMode.OffhandSwap));
   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 Timer timer = new Timer();
   private boolean lastChorusKeyPressed;
   private boolean chorusToggled;

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

   @Override
   public void onDisable() {
      this.lastChorusKeyPressed = false;
      this.chorusToggled = false;
      this.releaseUsingItemPause();
   }

   @EventListener
   public void totem(TotemEvent event) {
      if (event.getPlayer() == mc.player) {
         if (mc.player.getMainHandStack().getItem() == Items.TOTEM_OF_UNDYING) {
            mc.player.getInventory().removeStack(mc.player.getInventory().selectedSlot);
         } else if (mc.player.getOffHandStack().getItem() == Items.TOTEM_OF_UNDYING) {
            mc.player.getInventory().offHand.set(0, ItemStack.EMPTY);
         }
      }
   }

   @EventListener
   public void onTick(ClientTickEvent event) {
      if (!nullCheck()) {
         this.updateUsingItemPause();
         if ((!this.timing.is(Timing.Pre) || !event.isPost()) && (!this.timing.is(Timing.Post) || !event.isPre())) {
            if (this.timer.passed((long)this.delay.getValueInt()) && EntityUtil.inInventory()) {
               if (this.shouldChorusSwitch()) {
                  this.swap(Items.CHORUS_FRUIT);
                  this.timer.reset();
               } else if (this.safe.getValue() && this.isUnsafe()) {
                  this.swap(Items.TOTEM_OF_UNDYING);
                  this.timer.reset();
               } else if (this.shouldGapSwitch()) {
                  this.swap(Items.GOLDEN_APPLE);
                  this.timer.reset();
               } else {
                  Item var2 = this.getDesiredItem();
                  if (var2 != null) {
                     this.swap(var2);
                     this.timer.reset();
                  }
               }
            }
         }
      }
   }

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

   private void updateUsingItemPause() {
      if (this.isUsingItemPauseActive()) {
         this.holdUsingItemPause();
      } else {
         this.releaseUsingItemPause();
      }
   }

   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 shouldGapSwitch() {
      if (this.gapSwitch.getValue() && mc.options.useKey.isPressed() && !this.isLookingAtContainer()) {
         Item var1 = mc.player.getMainHandStack().getItem();
         return this.always.getValue() && var1 != Items.GOLDEN_APPLE && var1 != Items.ENCHANTED_GOLDEN_APPLE
            || this.gapOnSword.getValue() && var1 instanceof SwordItem
            || this.gapOnPick.getValue() && var1 instanceof PickaxeItem
            || this.gapOnTotem.getValue() && var1 == Items.TOTEM_OF_UNDYING;
      } else {
         return false;
      }
   }

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

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

   private boolean isActiveOffhandFoodUse() {
      if (mc.player.isUsingItem() && mc.player.getActiveHand() == Hand.OFF_HAND) {
         Item var1 = mc.player.getActiveItem().getItem();
         return var1 == Items.GOLDEN_APPLE || var1 == Items.ENCHANTED_GOLDEN_APPLE || var1 == Items.CHORUS_FRUIT;
      } else {
         return false;
      }
   }

   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(Offhand.KeyMode.Toggle) && var1 && !this.lastChorusKeyPressed) {
            this.chorusToggled = !this.chorusToggled;
         }

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

   private void swap(Item item) {
      int var2 = item == Items.GOLDEN_APPLE ? this.getGAppleSlot() : this.findItemInventorySlot(item);
      if (var2 != -1) {
         switch ((Offhand.SwapMode)this.swapMode.getValue()) {
            case ClickSlot:
               mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, var2, 0, SlotActionType.PICKUP, mc.player);
               mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, 45, 0, SlotActionType.PICKUP, mc.player);
               mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, var2, 0, SlotActionType.PICKUP, mc.player);
               EntityUtil.syncInventory();
               break;
            case OffhandSwap:
               mc.interactionManager.clickSlot(mc.player.currentScreenHandler.syncId, var2, 40, SlotActionType.SWAP, mc.player);
               EntityUtil.syncInventory();
               break;
            case Pick:
               mc.getNetworkHandler().sendPacket(new PickFromInventoryC2SPacket(var2));
               mc.getNetworkHandler().sendPacket(new PlayerActionC2SPacket(Action.SWAP_ITEM_WITH_OFFHAND, new BlockPos(0, 0, 0), Direction.DOWN, 0));
               mc.getNetworkHandler().sendPacket(new PickFromInventoryC2SPacket(var2));
         }
      }
   }

   private int getGAppleSlot() {
      int var1 = this.findItemInventorySlot(Items.ENCHANTED_GOLDEN_APPLE);
      return var1 != -1 ? var1 : this.findItemInventorySlot(Items.GOLDEN_APPLE);
   }

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

   public int findItemInventorySlot(Item item) {
      if (mc.player.getOffHandStack().getItem() == Items.GOLDEN_APPLE && item == Items.GOLDEN_APPLE) {
         return -1;
      } else if (mc.player.getOffHandStack().getItem() != Items.ENCHANTED_GOLDEN_APPLE || item != Items.GOLDEN_APPLE && item != Items.ENCHANTED_GOLDEN_APPLE) {
         if (item == mc.player.getOffHandStack().getItem()) {
            return -1;
         } else {
            switch ((Offhand.SwapMode)this.swapMode.getValue()) {
               case ClickSlot:
               case OffhandSwap:
                  for (int var5 = 44; var5 >= 0; var5--) {
                     ItemStack var7 = mc.player.getInventory().getStack(var5);
                     if (var7.getItem() == item) {
                        return var5 < 9 ? var5 + 36 : var5;
                     }
                  }
                  break;
               case Pick:
                  for (int var2 = 9; var2 < mc.player.getInventory().size() + 1; var2++) {
                     ItemStack var3 = mc.player.getInventory().getStack(var2);
                     if (var3.getItem() == item) {
                        return var2;
                     }
                  }

                  for (int var4 = 0; var4 < 9; var4++) {
                     ItemStack var6 = mc.player.getInventory().getStack(var4);
                     if (var6.getItem() == item) {
                        return var4;
                     }
                  }
            }

            return -1;
         }
      } else {
         return -1;
      }
   }
@Environment(EnvType.CLIENT)
   public static enum KeyMode {
      Hold,
      Toggle;

}

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

}

   @Environment(EnvType.CLIENT)
   public static enum SwapMode {
      ClickSlot,
      OffhandSwap,
      Pick;

}
}
