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

import com.darkmagician6.eventapi.EventTarget;
import java.util.ArrayList;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraft.block.RespawnAnchorBlock;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.SwordItem;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult.Type;
import net.minecraft.util.math.BlockPos;
import org.lwjgl.glfw.GLFW;
import zenith.zov.Zenith;
import zenith.zov.base.events.impl.player.EventUpdate;
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.NumberSetting;
import zenith.zov.client.modules.api.setting.impl.ToolSelectSetting;
import zenith.zov.utility.interfaces.IMinecraft;

@ModuleAnnotation(
    name = "AutoHitCrystal",
    category = Category.CRYSTAL,
    description = "Automatically hit-crystals for you"
)
public final class AutoHitCrystal extends Module implements IMinecraft {
    public static final AutoHitCrystal INSTANCE = new AutoHitCrystal();
    private final BooleanSetting checkPlace = new BooleanSetting("Check Place", false);
    private final NumberSetting switchDelay = new NumberSetting("Switch Delay", 0.0F, 0.0F, 20.0F, 1.0F);
    private final NumberSetting placeDelay = new NumberSetting("Place Delay", 0.0F, 0.0F, 20.0F, 1.0F);
    private final ToolSelectSetting workWith = new ToolSelectSetting("Work With", new ArrayList<>());
    private final BooleanSetting swordSwap = new BooleanSetting("Sword Swap", true);
    private int placeClock = 0;
    private int switchClock = 0;
    private boolean active;
    private boolean crystalling;
    private boolean crystalSelected;

    private AutoHitCrystal() {
        this.workWith.add(Items.DIAMOND_SWORD);
        this.workWith.add(Items.NETHERITE_SWORD);
        this.workWith.add(Items.IRON_SWORD);
        this.workWith.add(Items.DIAMOND_AXE);
        this.workWith.add(Items.NETHERITE_AXE);
        this.workWith.add(Items.MACE);
        this.workWith.add(Items.TOTEM_OF_UNDYING);
        this.workWith.add(Items.GOLDEN_APPLE);
        this.workWith.add(Items.ENCHANTED_GOLDEN_APPLE);
        this.workWith.add(Items.ENDER_PEARL);
        this.workWith.add(Items.SHIELD);
        this.workWith.add(Items.END_CRYSTAL);
        this.workWith.add(Items.AMETHYST_SHARD);
        this.workWith.add(Items.ECHO_SHARD);
        this.workWith.add(Items.QUARTZ);
    }

    @Override
    public void onEnable() {
        this.reset();
        super.onEnable();
    }

    @Override
    public void onDisable() {
        super.onDisable();
    }

    @EventTarget
    public void onUpdate(EventUpdate event) {
        if (mc.currentScreen == null) {
            if (GLFW.glfwGetMouseButton(mc.getWindow().getHandle(), 1) == 1) {
                if (mc.crosshairTarget instanceof BlockHitResult hitResult
                    && mc.crosshairTarget.getType() == Type.BLOCK
                    && !this.active
                    && !this.canPlaceBlockClient(hitResult.getBlockPos())
                    && this.checkPlace.isEnabled()) {
                    return;
                }

                ItemStack mainHandStack = mc.player.getMainHandStack();
                if (!this.isValidItem(mainHandStack) && !this.active) {
                    return;
                }

                if (mc.crosshairTarget instanceof BlockHitResult hitResult
                    && !this.active
                    && this.swordSwap.isEnabled()
                    && mc.crosshairTarget.getType() == Type.BLOCK) {
                    Block block = mc.world.getBlockState(hitResult.getBlockPos()).getBlock();
                    this.crystalling = block == Blocks.OBSIDIAN || block == Blocks.BEDROCK;
                }

                this.active = true;
                if (!this.crystalling) {
                    this.handleObsidianPlacement();
                }

                if (this.crystalling) {
                    this.handleCrystalBreaking();
                }
            } else {
                this.reset();
            }
        }
    }

    private boolean isValidItem(ItemStack stack) {
        return stack.getItem() instanceof SwordItem || this.workWith.contains(stack.getItem());
    }

    private void handleObsidianPlacement() {
        if (mc.crosshairTarget instanceof BlockHitResult hit) {
            if (hit.getType() == Type.MISS) {
                return;
            }

            if (!this.isObsidian(hit.getBlockPos())) {
                if (this.isChargedAnchor(hit.getBlockPos())) {
                    return;
                }

                mc.options.useKey.setPressed(false);
                if (!mc.player.isHolding(Items.OBSIDIAN)) {
                    if (this.switchClock > 0) {
                        this.switchClock--;
                        return;
                    }

                    this.switchClock = (int)this.switchDelay.getCurrent();
                    this.selectItemFromHotbar(Items.OBSIDIAN);
                }

                if (mc.player.isHolding(Items.OBSIDIAN)) {
                    if (this.placeClock > 0) {
                        this.placeClock--;
                        return;
                    }

                    this.placeBlock(hit);
                    this.placeClock = (int)this.placeDelay.getCurrent();
                    this.crystalling = true;
                }
            }
        }
    }

    private void handleCrystalBreaking() {
        if (!mc.player.isHolding(Items.END_CRYSTAL) && !this.crystalSelected) {
            if (this.switchClock > 0) {
                this.switchClock--;
                return;
            }

            this.crystalSelected = this.selectItemFromHotbar(Items.END_CRYSTAL);
            this.switchClock = (int)this.switchDelay.getCurrent();
        }

        if (mc.player.isHolding(Items.END_CRYSTAL)) {
            AutoCrystal autoCrystal = (AutoCrystal)Zenith.getInstance().getModuleManager().getModule("AutoCrystal");
            if (autoCrystal != null && !autoCrystal.isEnabled()) {
                autoCrystal.onUpdate(new EventUpdate());
            }
        }
    }

    private boolean canPlaceBlockClient(BlockPos pos) {
        return mc.world != null && mc.player != null ? mc.world.getBlockState(pos).isReplaceable() : false;
    }

    private boolean isObsidian(BlockPos pos) {
        return mc.world == null ? false : mc.world.getBlockState(pos).getBlock() == Blocks.OBSIDIAN;
    }

    private boolean isChargedAnchor(BlockPos pos) {
        if (mc.world == null) {
            return false;
        } else {
            return mc.world.getBlockState(pos).getBlock() == Blocks.RESPAWN_ANCHOR
                ? (Integer)mc.world.getBlockState(pos).get(RespawnAnchorBlock.CHARGES) > 0
                : false;
        }
    }

    private boolean selectItemFromHotbar(Item item) {
        for (int i = 0; i < 9; i++) {
            if (mc.player.getInventory().getStack(i).isOf(item)) {
                mc.player.getInventory().selectedSlot = i;
                return true;
            }
        }

        return false;
    }

    private void placeBlock(BlockHitResult hit) {
        if (mc.interactionManager != null) {
            mc.interactionManager.interactBlock(mc.player, mc.player.getActiveHand(), hit);
        }
    }

    public void reset() {
        this.placeClock = (int)this.placeDelay.getCurrent();
        this.switchClock = (int)this.switchDelay.getCurrent();
        this.active = false;
        this.crystalling = false;
        this.crystalSelected = false;
    }
}
