package net.mcreator.phayriosisreborn.meteor;

import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.common.world.ForgeChunkManager;

/** Temporary ticking-chunk lease for the expanded live meteor impact. */
public final class PhayriosisMeteorChunkLoader {
    private static final String MOD_ID = "phayriosisreborn";
    private static final int PATH_PADDING_CHUNKS = 1;
    private static final int IMPACT_RADIUS_CHUNKS = 4;
    private static final double SAMPLE_SPACING_BLOCKS = 10.0D;
    private static final int POST_IMPACT_RELEASE_DELAY = 120;

    private PhayriosisMeteorChunkLoader() {}

    public static void forceFlight(ServerLevel level, Entity owner,
                                   double startX, double startZ,
                                   double targetX, double targetZ) {
        if (owner == null) return;
        setFlight(level, owner.m_20148_(), startX, startZ, targetX, targetZ, true);
    }

    public static void releaseFlight(ServerLevel level, Entity owner,
                                     double startX, double startZ,
                                     double targetX, double targetZ) {
        if (owner == null) return;
        setFlight(level, owner.m_20148_(), startX, startZ, targetX, targetZ, false);
    }

    public static void releaseFlightLater(ServerLevel level, Entity owner,
                                          double startX, double startZ,
                                          double targetX, double targetZ) {
        if (level == null || owner == null) return;
        final UUID uuid = owner.m_20148_();
        if (uuid == null) return;
        net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(POST_IMPACT_RELEASE_DELAY,
                () -> setFlight(level, uuid, startX, startZ, targetX, targetZ, false));
    }

    private static void setFlight(ServerLevel level, UUID uuid,
                                  double startX, double startZ,
                                  double targetX, double targetZ,
                                  boolean add) {
        if (level == null || uuid == null) return;
        Set<Long> chunks = computeChunks(startX, startZ, targetX, targetZ);
        int changed = 0;
        for (long packed : chunks) {
            int cx = (int) (packed >> 32);
            int cz = (int) packed;
            if (ForgeChunkManager.forceChunk(level, MOD_ID, uuid, cx, cz, add, true)) changed++;
        }
        System.out.println("[Phayriosis Meteor] " + (add ? "forced " : "released ")
                + changed + "/" + chunks.size() + " ticking chunks for meteor flight/expanded impact");
    }

    private static Set<Long> computeChunks(double startX, double startZ,
                                           double targetX, double targetZ) {
        HashSet<Long> chunks = new HashSet<>();
        int targetChunkX = floorToInt(targetX) >> 4;
        int targetChunkZ = floorToInt(targetZ) >> 4;
        for (int dx = -IMPACT_RADIUS_CHUNKS; dx <= IMPACT_RADIUS_CHUNKS; dx++) {
            for (int dz = -IMPACT_RADIUS_CHUNKS; dz <= IMPACT_RADIUS_CHUNKS; dz++) {
                chunks.add(pack(targetChunkX + dx, targetChunkZ + dz));
            }
        }
        double dx = targetX - startX;
        double dz = targetZ - startZ;
        double distance = Math.sqrt(dx * dx + dz * dz);
        int samples = Math.max(1, (int) Math.ceil(distance / SAMPLE_SPACING_BLOCKS));
        for (int i = 0; i <= samples; i++) {
            double t = (double) i / (double) samples;
            int cx = floorToInt(startX + dx * t) >> 4;
            int cz = floorToInt(startZ + dz * t) >> 4;
            for (int px = -PATH_PADDING_CHUNKS; px <= PATH_PADDING_CHUNKS; px++) {
                for (int pz = -PATH_PADDING_CHUNKS; pz <= PATH_PADDING_CHUNKS; pz++) {
                    chunks.add(pack(cx + px, cz + pz));
                }
            }
        }
        return chunks;
    }

    private static long pack(int x, int z) { return ((long) x << 32) ^ (z & 0xffffffffL); }
    private static int floorToInt(double value) { int i=(int)value; return value < (double)i ? i-1 : i; }
}
