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

import asm.accessors.ILivingEntity;
import com.mojang.authlib.GameProfile;
import dev.crucifixbeta.CrucifixBeta;
import dev.crucifixbeta.api.events.eventbus.EventListener;
import dev.crucifixbeta.api.events.impl.PacketEvent;
import dev.crucifixbeta.api.events.impl.UpdateEvent;
import dev.crucifixbeta.api.utils.Wrapper;
import dev.crucifixbeta.api.utils.math.DamageUtils;
import dev.crucifixbeta.api.utils.world.BlockPosX;
import dev.crucifixbeta.api.utils.world.BlockUtil;
import dev.crucifixbeta.mod.modules.Module;
import dev.crucifixbeta.mod.modules.impl.combat.AutoAnchor;
import dev.crucifixbeta.mod.modules.impl.combat.AutoCrystal;
import dev.crucifixbeta.mod.modules.impl.combat.Criticals;
import dev.crucifixbeta.mod.modules.settings.impl.BooleanSetting;
import dev.crucifixbeta.mod.modules.settings.impl.StringSetting;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.block.Blocks;
import net.minecraft.client.network.OtherClientPlayerEntity;
import net.minecraft.entity.Entity.RemovalReason;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket;
import net.minecraft.network.packet.c2s.play.PlayerInteractEntityC2SPacket.InteractType;
import net.minecraft.network.packet.s2c.play.EntityStatusS2CPacket;
import net.minecraft.network.packet.s2c.play.ExplosionS2CPacket;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.Hand;
import net.minecraft.util.math.Vec3d;

@Environment(EnvType.CLIENT)
public class FakePlayer extends Module {
   public static FakePlayer INSTANCE;
   public static FakePlayer.FakePlayerEntity fakePlayer;
   final StringSetting name = this.add(new StringSetting("Name", "CrucifiX"));
   private final BooleanSetting damage = this.add(new BooleanSetting("Damage", true));
   private final BooleanSetting autoTotem = this.add(new BooleanSetting("AutoTotem", true));
   public final BooleanSetting record = this.add(new BooleanSetting("Record", false));
   public final BooleanSetting play = this.add(new BooleanSetting("Play", false));
   final List<FakePlayer.PlayerState> positions = new ArrayList<>();
   int movementTick;
   boolean lastRecordValue = false;

   public FakePlayer() {
      super("FakePlayer", Module.Category.Misc);
      this.setChinese("\u5047\u4eba");
      INSTANCE = this;
   }

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

   @Override
   public void onEnable() {
      if (nullCheck()) {
         this.disable();
      } else {
         try {
            fakePlayer = new FakePlayer.FakePlayerEntity(mc.player, this.name.getValue());
            mc.world.addEntity(fakePlayer);
         } catch (Throwable var2) {
            fakePlayer = null;
            var2.printStackTrace();
            this.sendMessage("\u00a7cFakePlayer failed to load. Restart Minecraft after updating the mod.");
            this.disable();
         }
      }
   }

   @EventListener
   public void onUpdate(UpdateEvent event) {
      if (fakePlayer != null && fakePlayer.clientWorld == mc.world) {
         if (this.autoTotem.getValue()) {
            if (fakePlayer.getOffHandStack().getItem() != Items.TOTEM_OF_UNDYING) {
               fakePlayer.setStackInHand(Hand.OFF_HAND, new ItemStack(Items.TOTEM_OF_UNDYING));
            }

            if (fakePlayer.getMainHandStack().getItem() != Items.TOTEM_OF_UNDYING) {
               fakePlayer.setStackInHand(Hand.MAIN_HAND, new ItemStack(Items.TOTEM_OF_UNDYING));
            }
         }

         if (this.record.getValue() != this.lastRecordValue && this.record.getValue()) {
            this.positions.clear();
         }

         this.lastRecordValue = this.record.getValue();
         if (this.record.getValue()) {
            this.positions.add(new FakePlayer.PlayerState(mc.player.getX(), mc.player.getY(), mc.player.getZ(), mc.player.getYaw(), mc.player.getPitch()));
         }

         if (this.play.getValue() && !this.positions.isEmpty()) {
            this.movementTick++;
            if (this.movementTick >= this.positions.size()) {
               this.movementTick = 0;
            }

            FakePlayer.PlayerState var2 = this.positions.get(this.movementTick);
            fakePlayer.setYaw(var2.yaw);
            fakePlayer.setPitch(var2.pitch);
            fakePlayer.setHeadYaw(var2.yaw);
            fakePlayer.updateTrackedPosition(var2.x, var2.y, var2.z);
            fakePlayer.updateTrackedPositionAndAngles(var2.x, var2.y, var2.z, var2.yaw, var2.pitch, 3);
         }
      } else {
         this.disable();
      }
   }

   @Override
   public void onDisable() {
      if (fakePlayer != null) {
         fakePlayer.kill();
         fakePlayer.setRemoved(RemovalReason.KILLED);
         fakePlayer.onRemoved();
         fakePlayer = null;
      }
   }

   @EventListener
   public void onAttack(PacketEvent.Send event) {
      Packet var2 = event.getPacket();
      PlayerInteractEntityC2SPacket var3;
      if (var2 instanceof PlayerInteractEntityC2SPacket
         && Criticals.getInteractType(var3 = (PlayerInteractEntityC2SPacket)var2) == InteractType.ATTACK
         && Criticals.getEntity(var3) == fakePlayer) {
         mc.world
            .playSound(mc.player, fakePlayer.getX(), fakePlayer.getY(), fakePlayer.getZ(), SoundEvents.ENTITY_PLAYER_HURT, SoundCategory.PLAYERS, 1.0F, 1.0F);
         float var4 = DamageUtils.getAttackDamage(mc.player, fakePlayer);
         if ((
               mc.player.fallDistance > 0.0F
                  || Criticals.INSTANCE.isOn()
                     && !Criticals.INSTANCE.mode.is(Criticals.Mode.Ground)
                     && (mc.player.isOnGround() || !Criticals.INSTANCE.onlyGround.getValue())
            )
            && (!mc.player.isOnGround() || Criticals.INSTANCE.isOn() && !Criticals.INSTANCE.mode.is(Criticals.Mode.Ground))
            && !mc.player.isClimbing()
            && !mc.player.isTouchingWater()
            && !mc.player.hasStatusEffect(StatusEffects.BLINDNESS)
            && !mc.player.hasVehicle()) {
            mc.world
               .playSound(
                  mc.player, fakePlayer.getX(), fakePlayer.getY(), fakePlayer.getZ(), SoundEvents.ENTITY_PLAYER_ATTACK_CRIT, SoundCategory.PLAYERS, 1.0F, 1.0F
               );
            mc.player.addCritParticles(fakePlayer);
         }

         if (fakePlayer.hurtTime <= 0) {
            fakePlayer.onDamaged(mc.world.getDamageSources().generic());
            if (fakePlayer.getAbsorptionAmount() >= var4) {
               fakePlayer.setAbsorptionAmount(fakePlayer.getAbsorptionAmount() - var4);
            } else {
               float var5 = var4 - fakePlayer.getAbsorptionAmount();
               fakePlayer.setAbsorptionAmount(0.0F);
               fakePlayer.setHealth(fakePlayer.getHealth() - var5);
            }

            if (fakePlayer.isDead()) {
               CrucifixBeta.POP.onTotemPop(fakePlayer);
               if (fakePlayer.tryUseTotem(mc.world.getDamageSources().generic())) {
                  fakePlayer.setHealth(10.0F);
                  new EntityStatusS2CPacket(fakePlayer, (byte)35).apply((ClientPlayPacketListener)mc.getNetworkHandler());
               }
            }
         }
      }
   }

   @EventListener
   public void onPacketReceive(PacketEvent.Receive event) {
      Packet var2;
      if (this.damage.getValue() && fakePlayer != null && fakePlayer.hurtTime <= 0 && (var2 = event.getPacket()) instanceof ExplosionS2CPacket) {
         ExplosionS2CPacket var3 = (ExplosionS2CPacket)var2;
         Vec3d var4 = new Vec3d(var3.getX(), var3.getY(), var3.getZ());
         if (Math.sqrt(var4.squaredDistanceTo(fakePlayer.getPos())) > 10.0) {
            return;
         }

         float var5 = BlockUtil.getBlock(new BlockPosX(var3.getX(), var3.getY(), var3.getZ())) == Blocks.RESPAWN_ANCHOR
            ? (float)AutoAnchor.INSTANCE.getAnchorDamage(new BlockPosX(var3.getX(), var3.getY(), var3.getZ()), fakePlayer, fakePlayer)
            : AutoCrystal.INSTANCE.calculateDamage(new Vec3d(var3.getX(), var3.getY(), var3.getZ()), fakePlayer, fakePlayer);
         fakePlayer.onDamaged(mc.world.getDamageSources().generic());
         if (fakePlayer.getAbsorptionAmount() >= var5) {
            fakePlayer.setAbsorptionAmount(fakePlayer.getAbsorptionAmount() - var5);
         } else {
            float var6 = var5 - fakePlayer.getAbsorptionAmount();
            fakePlayer.setAbsorptionAmount(0.0F);
            fakePlayer.setHealth(fakePlayer.getHealth() - var6);
         }

         if (fakePlayer.isDead()) {
            CrucifixBeta.POP.onTotemPop(fakePlayer);
            if (fakePlayer.tryUseTotem(mc.world.getDamageSources().generic())) {
               fakePlayer.setHealth(10.0F);
               new EntityStatusS2CPacket(fakePlayer, (byte)35).apply((ClientPlayPacketListener)mc.getNetworkHandler());
            }
         }
      }
   }
@Environment(EnvType.CLIENT)
   public static class FakePlayerEntity extends OtherClientPlayerEntity {
      private final boolean onGround;

      public FakePlayerEntity(PlayerEntity player, String name) {
         super(Wrapper.mc.world, new GameProfile(UUID.fromString("66666666-6666-6666-6666-666666666666"), name));
         this.copyPositionAndRotation(player);
         this.prevX = player.prevX;
         this.prevZ = player.prevZ;
         this.prevY = player.prevY;
         this.bodyYaw = player.bodyYaw;
         this.headYaw = player.headYaw;
         this.handSwingProgress = player.handSwingProgress;
         this.handSwingTicks = player.handSwingTicks;
         this.limbAnimator.setSpeed(player.limbAnimator.getSpeed());
         this.limbAnimator.pos = player.limbAnimator.getPos();
         ((ILivingEntity)this).setLeaningPitch(((ILivingEntity)player).getLeaningPitch());
         ((ILivingEntity)this).setLastLeaningPitch(((ILivingEntity)player).getLeaningPitch());
         this.touchingWater = player.isTouchingWater();
         this.setSneaking(player.isSneaking());
         this.setPose(player.getPose());
         this.setFlag(7, player.isFallFlying());
         this.onGround = player.isOnGround();
         this.setOnGround(this.onGround);
         this.getInventory().clone(player.getInventory());
         this.setAbsorptionAmountUnclamped(player.getAbsorptionAmount());
         this.setHealth(player.getHealth());
         this.setBoundingBox(player.getBoundingBox());
      }

      @Override
      public boolean isOnGround() {
         return this.onGround;
      }

      @Override
      public boolean isSpectator() {
         return false;
      }

      @Override
      public boolean isCreative() {
         return false;
      }
}

   @Environment(EnvType.CLIENT)
   private static record PlayerState(double x, double y, double z, float yaw, float pitch) {
   }
}
