import { world, system } from "@minecraft/server";
import * as utils from "./utils";

const CHUNK_SIZE = 16;
const HALF_CHUNK_SIZE = CHUNK_SIZE / 2;

const chunks = {};

// Quickly calculates chunk coordinates without object overhead
const getChunkCoordinates = (x, y, z) => [
    Math.floor(x / CHUNK_SIZE) * CHUNK_SIZE,
    Math.floor(y / CHUNK_SIZE) * CHUNK_SIZE,
    Math.floor(z / CHUNK_SIZE) * CHUNK_SIZE
];

// Quickly generates a unique chunk ID
const getChunkId = (x, y, z) => `${x}:${y}:${z}`;

export async function onTick() {
    const players = world.getAllPlayers();
    for (let i = 0; i < players.length; i++) {
        const player = players[i];
        if (!player.hasTag("goe_bos_in_dungeon") || player.dimension.id !== "minecraft:the_end") continue;

        try {
            loadChunksAroundPlayer(player);
        } catch {}
    }
    await removeUnusedChunks();
}

function loadChunksAroundPlayer(player) {
    const [cx, cy, cz] = getChunkCoordinates(player.location.x, player.location.y, player.location.z);

    const activeChunks = new Set();
    let dx, dy, dz, chunkId, chunkEntity, chunkLoc;

    for (dx = -1; dx <= 1; dx++) {
        for (dy = -1; dy <= 1; dy++) {
            for (dz = -1; dz <= 1; dz++) {
                const chunkX = cx + dx * CHUNK_SIZE;
                const chunkY = cy + dy * CHUNK_SIZE;
                const chunkZ = cz + dz * CHUNK_SIZE;
                chunkId = getChunkId(chunkX, chunkY, chunkZ);
                activeChunks.add(chunkId);

                if (!chunks[chunkId]) {
                    chunkLoc = { x: chunkX, y: chunkY, z: chunkZ };
                    loadChunk(player, chunkLoc, chunkId);
                }
            }
        }
    }

    // Mark unused chunks
    for (chunkId in chunks) {
        if (!activeChunks.has(chunkId)) chunks[chunkId].delete = true;
    }
}

function loadChunk(player, chunkLocation, chunkId) {
    const dungeonId = player.getDynamicProperty("goe_bos_dungeon_id");
    const chunkEntity = player.dimension.spawnEntity(
        `goe_bos:chunk_emitter<${dungeonId}>`,
        {
            x: chunkLocation.x + HALF_CHUNK_SIZE,
            y: chunkLocation.y + HALF_CHUNK_SIZE,
            z: chunkLocation.z + HALF_CHUNK_SIZE
        }
    );

    chunks[chunkId] = { id: chunkId, entity: chunkEntity, delete: false };
}

async function removeUnusedChunks() {
    const chunkEmitters = await utils.getEntities("goe_bos:chunk_emitter");

    // Remove unused emitters
    let i = 0, emitter, chunkId;
    while (i < chunkEmitters.length) {
        emitter = chunkEmitters[i++];
        if (!emitter.isValid()) continue;

        const [chunkX, chunkY, chunkZ] = getChunkCoordinates(emitter.location.x, emitter.location.y, emitter.location.z);
        chunkId = getChunkId(chunkX, chunkY, chunkZ);
        if (chunks[chunkId] === undefined) emitter.remove();
    }

    // Remove chunks marked for deletion
    for (chunkId in chunks) {
        const chunk = chunks[chunkId];
        if (chunk.delete) {
            try { chunk.entity.remove(); } catch {}
            delete chunks[chunkId];
        }
    }
}
