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

import dev.crucifixbeta.CrucifixBeta;
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.PredictUtil;
import dev.crucifixbeta.api.utils.math.Timer;
import dev.crucifixbeta.api.utils.player.EntityUtil;
import dev.crucifixbeta.api.utils.player.InventoryUtil;
import dev.crucifixbeta.api.utils.world.BlockUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.exploit.Blink;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.SliderSetting;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Blocks;
import net.minecraft.util.math.BlockPos;

@Environment(EnvType.CLIENT)
public class HoleFiller extends Module {
   public static HoleFiller INSTANCE;
   public final SliderSetting placeDelay = this.add(new SliderSetting("PlaceDelay", 50, 0, 500).setSuffix("ms"));
   public final BooleanSetting inAirPause = this.add(new BooleanSetting("InAirPause", true));
   private final Timer timer = new Timer();
   private final SliderSetting blocksPer = this.add(new SliderSetting("BlocksPer", 1, 1, 8));
   private final SliderSetting placeRange = this.add(new SliderSetting("PlaceRange", 5.0, 0.0, 8.0).setSuffix("m"));
   private final SliderSetting enemyRange = this.add(new SliderSetting("EnemyRange", 6.0, 0.0, 8.0).setSuffix("m"));
   private final SliderSetting holeRange = this.add(new SliderSetting("HoleRange", 2.0, 0.0, 8.0).setSuffix("m"));
   private final SliderSetting selfRange = this.add(new SliderSetting("SelfRange", 2.0, 0.0, 8.0).setSuffix("m"));
   private final SliderSetting predictTicks = this.add(new SliderSetting("Predict", 1, 1, 8).setSuffix("tick"));
   private final BooleanSetting detectMining = this.add(new BooleanSetting("DetectMining", false));
   private final BooleanSetting rotate = this.add(new BooleanSetting("Rotate", true));
   private final BooleanSetting packetPlace = this.add(new BooleanSetting("PacketPlace", true));
   private final BooleanSetting breakCrystal = this.add(new BooleanSetting("Break", true).setParent());
   private final BooleanSetting eatPause = this.add(new BooleanSetting("EatingPause", true, this.breakCrystal::isOpen));
   private final BooleanSetting usingPause = this.add(new BooleanSetting("UsingPause", true));
   private final BooleanSetting inventory = this.add(new BooleanSetting("InventorySwap", true));
   private final BooleanSetting web = this.add(new BooleanSetting("Web", true));
   int progress = 0;

   public HoleFiller() {
      super("HoleFiller", Module.Category.Combat);
      this.setChinese("\u81ea\u52a8\u586b\u5751");
      INSTANCE = this;
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (this.timer.passed((long)this.placeDelay.getValue())) {
         if (!this.inventory.getValue() || EntityUtil.inInventory()) {
            this.progress = 0;
            if (this.getBlock() != -1) {
               if (!Blink.INSTANCE.isOn() || !Blink.INSTANCE.pauseModule.getValue()) {
                  if (!this.usingPause.getValue() || !this.shouldPauseForUsingItem()) {
                     if (!this.inAirPause.getValue() || mc.player.isOnGround()) {
                        CombatUtil.getEnemies(this.enemyRange.getValue())
                           .stream()
                           .flatMap(
                              enemy -> BlockUtil.getSphere(this.holeRange.getValueFloat(), PredictUtil.getPos(enemy, this.predictTicks.getValueInt())).stream()
                           )
                           .filter(
                              pos -> pos.toCenterPos().distanceTo(mc.player.getPos()) > this.selfRange.getValue()
                                    && (CrucifixBeta.HOLE.isHole(pos, true, true, false) || CrucifixBeta.HOLE.isDoubleHole(pos))
                           )
                           .distinct()
                           .forEach(this::tryPlaceBlock);
                     }
                  }
               }
            }
         }
      }
   }

   private void tryPlaceBlock(BlockPos pos) {
      if (pos != null) {
         if (!this.detectMining.getValue() || !CrucifixBeta.BREAK.isMining(pos)) {
            if ((double)this.progress < this.blocksPer.getValue()) {
               int var2 = this.getBlock();
               if (var2 != -1) {
                  if (BlockUtil.canPlace(pos, this.placeRange.getValue(), true)) {
                     if (this.tryUseCombatTask()) {
                        if (this.breakCrystal.getValue()) {
                           CombatUtil.attackCrystal(pos, this.rotate.getValue(), this.eatPause.getValue());
                        } else if (BlockUtil.hasEntity(pos, false)) {
                           return;
                        }

                        int var3 = mc.player.getInventory().selectedSlot;
                        this.doSwap(var2);
                        BlockUtil.placeBlock(pos, this.rotate.getValue(), this.packetPlace.getValue());
                        if (this.inventory.getValue()) {
                           this.doSwap(var2);
                           EntityUtil.syncInventory();
                        } else {
                           this.doSwap(var3);
                        }

                        this.progress++;
                        this.timer.reset();
                     }
                  }
               }
            }
         }
      }
   }

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

   private int getBlock() {
      if (this.inventory.getValue()) {
         return this.web.getValue() && InventoryUtil.findBlockInventorySlot(Blocks.COBWEB) != -1
            ? InventoryUtil.findBlockInventorySlot(Blocks.COBWEB)
            : InventoryUtil.findBlockInventorySlot(Blocks.OBSIDIAN);
      } else {
         return this.web.getValue() && InventoryUtil.findBlock(Blocks.COBWEB) != -1
            ? InventoryUtil.findBlock(Blocks.COBWEB)
            : InventoryUtil.findBlock(Blocks.OBSIDIAN);
      }
   }
}
