package zenith.zov.client.modules.impl.crystal;

import com.darkmagician6.eventapi.EventTarget;
import java.util.HashSet;
import java.util.Set;
import net.minecraft.block.Blocks;
import net.minecraft.block.RespawnAnchorBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.ActionResult;
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;
import zenith.zov.Zenith;
import zenith.zov.base.events.impl.player.EventUpdate;
import zenith.zov.base.rotation.AimManager;
import zenith.zov.base.rotation.RotationManager;
import zenith.zov.base.rotation.RotationTarget;
import zenith.zov.base.rotation.mods.config.api.RotationConfig;
import zenith.zov.client.modules.api.Category;
import zenith.zov.client.modules.api.Module;
import zenith.zov.client.modules.api.ModuleAnnotation;
import zenith.zov.client.modules.api.setting.impl.BooleanSetting;
import zenith.zov.client.modules.api.setting.impl.ModeSetting;
import zenith.zov.client.modules.api.setting.impl.NumberSetting;
import zenith.zov.utility.game.player.rotation.Rotation;
import zenith.zov.utility.game.player.rotation.RotationUtil;
import zenith.zov.utility.interfaces.IMinecraft;

@ModuleAnnotation(
    name = "SafeAnchor",
    category = Category.CRYSTAL,
    description = "Places a block in front of you before popping a respawn anchor"
)
public final class SafeAnchor extends Module implements IMinecraft {
    public static final SafeAnchor INSTANCE = new SafeAnchor();
    private final ModeSetting mode = new ModeSetting("Mode", "Blatant", "Blatant", "Legit");
    private final BooleanSetting silentRotation = new BooleanSetting("Silent Rotation", true);
    private final NumberSetting explosionSlot = new NumberSetting("Explosion Slot", 1.0F, 0.0F, 8.0F, 1.0F);
    private final ModeSetting protectionBlock = new ModeSetting("Protection Block", "Obsidian", "Glowstone", "Obsidian", "Cobblestone");
    private final NumberSetting fov = new NumberSetting("FOV", 180.0F, 30.0F, 360.0F, 1.0F);
    private final NumberSetting protectionDelayMs = new NumberSetting("Protection Delay (ms)", 200.0F, 50.0F, 1000.0F, 10.0F);
    private final NumberSetting blatantProtectionDelayMs = new NumberSetting("Blatant Protection Delay (ms)", 50.0F, 10.0F, 200.0F, 5.0F);
    private BlockPos lastPlacedAnchor = null;
    private long lastPlaceTime = 0L;
    private BlockPos currentlyCharging = null;
    private long chargeStartTime = 0L;
    private boolean protectionPlaced = false;
    private Set<BlockPos> myAnchors = new HashSet<>();
    private long lastProtectionAttempt = 0L;
    private int protectionRetryCount = 0;
    private static final long PROTECTION_COOLDOWN = 50L;
    private static final long BLATANT_PROTECTION_COOLDOWN = 10L;
    private static final int MAX_PROTECTION_RETRIES = 5;

    private SafeAnchor() {
    }

    @EventTarget
    private void onUpdate(EventUpdate event) {
        if (mc.player != null && mc.world != null) {
            this.checkForAnchorsToExplode();
            if (mc.crosshairTarget instanceof BlockHitResult blockHit) {
                if (mc.player.getMainHandStack().isOf(Items.RESPAWN_ANCHOR)) {
                    BlockPos targetPos = blockHit.getBlockPos();
                    Direction side = blockHit.getSide();
                    if (!mc.world.getBlockState(targetPos).isReplaceable()) {
                        switch (side) {
                            case UP:
                                this.myAnchors.add(targetPos.up());
                                break;
                            case DOWN:
                                this.myAnchors.add(targetPos.down());
                                break;
                            case EAST:
                                this.myAnchors.add(targetPos.east());
                                break;
                            case WEST:
                                this.myAnchors.add(targetPos.west());
                                break;
                            case NORTH:
                                this.myAnchors.add(targetPos.north());
                                break;
                            case SOUTH:
                                this.myAnchors.add(targetPos.south());
                        }
                    } else {
                        this.myAnchors.add(targetPos);
                    }
                }

                if (mc.world.getBlockState(blockHit.getBlockPos()).getBlock() == Blocks.RESPAWN_ANCHOR) {
                    BlockPos anchorPos = blockHit.getBlockPos();
                    if (this.myAnchors.contains(anchorPos)) {
                        BlockPos posInFront = mc.player.getBlockPos().offset(mc.player.getHorizontalFacing());
                        if (mc.world.isAir(posInFront)) {
                            double distance = mc.player.getPos().distanceTo(Vec3d.ofCenter(posInFront));
                            if (distance <= 4.5 && this.isWithinFOV(posInFront)) {
                                int slot = this.findBlockSlot();
                                if (slot != -1) {
                                    mc.player.getInventory().selectedSlot = slot;
                                    mc.interactionManager
                                        .interactBlock(
                                            mc.player,
                                            Hand.MAIN_HAND,
                                            new BlockHitResult(Vec3d.ofCenter(posInFront), Direction.UP, posInFront.down(), false)
                                        );
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void checkForAnchorsToExplode() {
        if (this.currentlyCharging != null) {
            if (System.currentTimeMillis() - this.chargeStartTime > 2000L) {
                this.currentlyCharging = null;
                this.chargeStartTime = 0L;
                this.protectionPlaced = false;
                this.protectionRetryCount = 0;
            } else if (mc.world.getBlockState(this.currentlyCharging).getBlock() == Blocks.RESPAWN_ANCHOR) {
                int charges = (Integer)mc.world.getBlockState(this.currentlyCharging).get(RespawnAnchorBlock.CHARGES);
                if (charges > 0) {
                    if (!this.protectionPlaced) {
                        long currentTime = System.currentTimeMillis();
                        long currentCooldown = this.mode.get().equals("Blatant") ? 10L : 50L;
                        if (currentTime - this.lastProtectionAttempt > currentCooldown && this.protectionRetryCount < 5) {
                            if (this.placeProtectionBlocks()) {
                                this.protectionPlaced = true;
                                this.protectionRetryCount = 0;
                            } else {
                                this.protectionRetryCount++;
                            }

                            this.lastProtectionAttempt = currentTime;
                        }
                    }

                    long currentDelay = this.mode.get().equals("Blatant")
                        ? (long)this.blatantProtectionDelayMs.getCurrent()
                        : (long)this.protectionDelayMs.getCurrent();
                    if (System.currentTimeMillis() - this.chargeStartTime > currentDelay) {
                        mc.player.getInventory().selectedSlot = (int)this.explosionSlot.getCurrent();
                        this.explodeAnchor(this.currentlyCharging);
                        this.myAnchors.remove(this.currentlyCharging);
                        this.currentlyCharging = null;
                        this.chargeStartTime = 0L;
                        this.protectionPlaced = false;
                        this.protectionRetryCount = 0;
                        return;
                    }
                }
            } else {
                this.currentlyCharging = null;
                this.chargeStartTime = 0L;
                this.protectionPlaced = false;
                this.protectionRetryCount = 0;
            }
        }

        if (this.currentlyCharging == null) {
            BlockPos playerPos = mc.player.getBlockPos();

            for (int x = -5; x <= 5; x++) {
                for (int y = -3; y <= 3; y++) {
                    for (int z = -5; z <= 5; z++) {
                        BlockPos pos = playerPos.add(x, y, z);
                        if (mc.world.getBlockState(pos).getBlock() == Blocks.RESPAWN_ANCHOR && this.myAnchors.contains(pos)) {
                            int charges = (Integer)mc.world.getBlockState(pos).get(RespawnAnchorBlock.CHARGES);
                            if (charges == 0
                                && (
                                    this.lastPlacedAnchor == null
                                        || !this.lastPlacedAnchor.equals(pos)
                                        || System.currentTimeMillis() - this.lastPlaceTime > 2000L
                                )
                                && this.isWithinFOV(pos)) {
                                this.chargeAnchor(pos);
                                this.currentlyCharging = pos;
                                this.chargeStartTime = System.currentTimeMillis();
                                this.lastPlacedAnchor = pos;
                                this.lastPlaceTime = System.currentTimeMillis();
                                this.protectionRetryCount = 0;
                                return;
                            }
                        }
                    }
                }
            }
        }
    }

    private void chargeAnchor(BlockPos anchorPos) {
        int glowstoneSlot = this.findGlowstoneSlot();
        if (glowstoneSlot != -1) {
            double distance = mc.player.getPos().distanceTo(Vec3d.ofCenter(anchorPos));
            if (!(distance > 4.5)) {
                if (this.silentRotation.isEnabled()) {
                    Vec3d targetPos = Vec3d.ofCenter(anchorPos);
                    Rotation targetRotation = RotationUtil.calculateAngle(targetPos);
                    RotationManager rotationManager = Zenith.getInstance().getRotationManager();
                    AimManager aimManager = rotationManager.getAimManager();
                    RotationConfig rotationConfig = this.mode.get().equals("Blatant") ? aimManager.getInstantSetup() : aimManager.getAiSetup();
                    int priority = this.mode.get().equals("Blatant") ? 1 : 2;
                    rotationManager.setRotation(
                        new RotationTarget(targetRotation, () -> aimManager.rotate(rotationConfig, targetRotation), rotationConfig), priority, this
                    );
                }

                mc.player.getInventory().selectedSlot = glowstoneSlot;
                BlockHitResult hitResult = new BlockHitResult(Vec3d.ofCenter(anchorPos), Direction.UP, anchorPos, false);
                mc.interactionManager.interactBlock(mc.player, Hand.MAIN_HAND, hitResult);
            }
        }
    }

    private boolean placeProtectionBlocks() {
        if (mc.player != null && mc.world != null) {
            int blockSlot = this.findBlockSlot();
            if (blockSlot == -1) {
                return false;
            } else {
                BlockPos playerPos = mc.player.getBlockPos();
                BlockPos[] protectionPositions = new BlockPos[]{
                    playerPos.offset(mc.player.getHorizontalFacing()),
                    playerPos.offset(mc.player.getHorizontalFacing()).down(),
                    playerPos.offset(mc.player.getHorizontalFacing()).up(),
                    playerPos.up(),
                    playerPos.offset(mc.player.getHorizontalFacing().rotateYClockwise()),
                    playerPos.offset(mc.player.getHorizontalFacing().rotateYCounterclockwise())
                };

                for (BlockPos targetPos : protectionPositions) {
                    double distance = mc.player.getPos().distanceTo(Vec3d.ofCenter(targetPos));
                    if (!(distance > 4.5)
                        && this.isWithinFOV(targetPos)
                        && mc.world.isAir(targetPos)
                        && mc.world.getWorldBorder().contains(targetPos)) {
                        Direction bestDirection = null;
                        BlockPos bestNeighborPos = null;

                        for (Direction dir : Direction.values()) {
                            BlockPos neighborPos = targetPos.offset(dir);
                            if (!mc.world.getBlockState(neighborPos).isAir() && !mc.world.getBlockState(neighborPos).isReplaceable()) {
                                if (dir == Direction.DOWN) {
                                    bestDirection = dir;
                                    bestNeighborPos = neighborPos;
                                    break;
                                }

                                if (bestDirection == null) {
                                    bestDirection = dir;
                                    bestNeighborPos = neighborPos;
                                }
                            }
                        }

                        if (bestDirection != null && bestNeighborPos != null) {
                            int originalSlot = mc.player.getInventory().selectedSlot;
                            mc.player.getInventory().selectedSlot = blockSlot;
                            BlockHitResult hitResult = new BlockHitResult(Vec3d.ofCenter(targetPos), bestDirection, bestNeighborPos, false);
                            ActionResult result = mc.interactionManager.interactBlock(mc.player, Hand.MAIN_HAND, hitResult);
                            boolean success = result.isAccepted();
                            mc.player.getInventory().selectedSlot = originalSlot;
                            if (success) {
                                return true;
                            }
                        }
                    }
                }

                return false;
            }
        } else {
            return false;
        }
    }

    private void explodeAnchor(BlockPos anchorPos) {
        double distance = mc.player.getPos().distanceTo(Vec3d.ofCenter(anchorPos));
        if (!(distance > 4.5)) {
            if (this.silentRotation.isEnabled()) {
                Vec3d targetPos = Vec3d.ofCenter(anchorPos);
                Rotation targetRotation = RotationUtil.calculateAngle(targetPos);
                RotationManager rotationManager = Zenith.getInstance().getRotationManager();
                AimManager aimManager = rotationManager.getAimManager();
                RotationConfig rotationConfig = this.mode.get().equals("Blatant") ? aimManager.getInstantSetup() : aimManager.getAiSetup();
                int priority = this.mode.get().equals("Blatant") ? 1 : 2;
                rotationManager.setRotation(
                    new RotationTarget(targetRotation, () -> aimManager.rotate(rotationConfig, targetRotation), rotationConfig), priority, this
                );
            }

            BlockHitResult hitResult = new BlockHitResult(Vec3d.ofCenter(anchorPos), Direction.UP, anchorPos, false);
            mc.interactionManager.interactBlock(mc.player, Hand.MAIN_HAND, hitResult);
        }
    }

    private int findBlockSlot() {
        if (mc.player == null) {
            return -1;
        } else {
            String selectedBlock = this.protectionBlock.get();

            for (int i = 0; i < 9; i++) {
                ItemStack stack = mc.player.getInventory().getStack(i);
                if (!stack.isEmpty()) {
                    boolean isSelectedBlock = false;
                    switch (selectedBlock) {
                        case "Glowstone":
                            isSelectedBlock = stack.getItem() == Items.GLOWSTONE;
                            break;
                        case "Obsidian":
                            isSelectedBlock = stack.getItem() == Items.OBSIDIAN;
                            break;
                        case "Cobblestone":
                            isSelectedBlock = stack.getItem() == Items.COBBLESTONE;
                    }

                    if (isSelectedBlock) {
                        return i;
                    }
                }
            }

            return -1;
        }
    }

    private int findGlowstoneSlot() {
        if (mc.player == null) {
            return -1;
        } else {
            for (int i = 0; i < 9; i++) {
                ItemStack stack = mc.player.getInventory().getStack(i);
                if (stack.getItem() == Items.GLOWSTONE && !stack.isEmpty()) {
                    return i;
                }
            }

            return -1;
        }
    }

    private boolean isWithinFOV(BlockPos pos) {
        if (mc.player == null) {
            return false;
        } else {
            Vec3d playerPos = mc.player.getPos();
            Vec3d targetPos = Vec3d.ofCenter(pos);
            Vec3d direction = targetPos.subtract(playerPos).normalize();
            Vec3d lookDirection = mc.player.getRotationVector();
            double dotProduct = lookDirection.dotProduct(direction);
            double angle = Math.acos(Math.max(-1.0, Math.min(1.0, dotProduct)));
            double angleDegrees = Math.toDegrees(angle);
            double halfFOV = (double)this.fov.getCurrent() / 2.0;
            return angleDegrees <= halfFOV;
        }
    }
}
