import { world, system, MolangVariableMap } from "@minecraft/server";
import { skipDay } from "../LvZoMain.js";
import { toggleHordeEffects } from "./horde_effects.js"; // Import function
import { spawnZombieFeature, randomInt } from "./feature_population_handler.js"
/**
 * Subscribes to script events used for lv_zo_debug purposes
 * 
 * @example "lv_zo_debug:time": This will return current time of day
 * @example "lv_zo_debug:skipday": This will skip an entire day
 * @example "lv_zo_debug:settime 0-23999": This will set the current time of day to the named number: 13000 will start horde on horde night, 23000 > will end horde night.
 */
system.afterEvents.scriptEventReceive.subscribe((e) => {
    const { id, message, sourceEntity, sourceBlock } = e;

    /**
     * FEATURE EVENTS
     */
    if (id === "lv_zo:camp_feature") {
        sourceBlock.setType("minecraft:stone");

        // Call our zombie spawning function with default parameters
        spawnZombieFeature(sourceBlock, { count: randomInt(4, 8), radius: 10 });
    }

    if (id === "lv_zo:office_feature") {
        sourceBlock.setType("minecraft:stone");

        // Call our zombie spawning function with default parameters
        spawnZombieFeature(sourceBlock, { count: randomInt(4, 8), radius: 10, office: true });
    }

    if (id === "lv_zo:graveyard_feature") {
        sourceBlock.setType("minecraft:stone");

        // Call our zombie spawning function with default parameters
        spawnZombieFeature(sourceBlock, { count: randomInt(4, 8), radius: 10, office: true });
    }

    if (id === "lv_zo:suburban_feature") {
        sourceBlock.setType("minecraft:stone");

        // Call our zombie spawning function with default parameters
        spawnZombieFeature(sourceBlock, { count: randomInt(4, 8), radius: 10, office: true });
    }


    if (id === "lv_zo:restaurant_feature") {
        sourceBlock.setType("minecraft:stone");

        // Call our zombie spawning function with default parameters
        spawnZombieFeature(sourceBlock, { count: randomInt(6, 8), radius: 12 });
    }
    if (id === "lv_zo:pool_feature") {
        sourceBlock.setType("minecraft:stone");

        // Call our zombie spawning function with default parameters
        spawnZombieFeature(sourceBlock, { count: randomInt(6, 8), radius: 12, pool: true });
    }
    if (id === "lv_zo:farm_feature") {
        sourceBlock.setType("minecraft:stone");

        // Call our zombie spawning function with default parameters
        spawnZombieFeature(sourceBlock, { count: randomInt(6, 8), radius: 12, farm: true });
    }
    if (id === "lv_zo:mansion_feature") {
        sourceBlock.setType("minecraft:stone");

        // Call our zombie spawning function with default parameters
        spawnZombieFeature(sourceBlock, { count: randomInt(8, 12), radius: 15, medieval: true });
    }
    if (id === "lv_zo:zomboss_spawn") {
        sourceBlock.setType("minecraft:stone");

        // Call our zombie spawning function with default parameters
        spawnZombieFeature(sourceBlock, { count: randomInt(8, 12), radius: 6, medieval: true });
        system.runTimeout(() => {
            sourceBlock.dimension.spawnEntity("lv_zo:zomboss", { x: sourceBlock.location.x, y: sourceBlock.location.y + 3, z: sourceBlock.location.z })
            sourceBlock.dimension.getBlock({ x: sourceBlock.location.x, y: sourceBlock.location.y + 3, z: sourceBlock.location.z }).setType("minecraft:light_block_15");
        }, 20);
    }
    /**
     * lv_zo_debug CODE
     */
    if (id === "lv_zo_debug:time") {
        let currentTime = world.getTimeOfDay();
        world.sendMessage(`Current Time: ${currentTime} ticks`);
    }
    if (id === "lv_zo_debug:skipday") {
        skipDay();
        for (const player of world.getPlayers()) {
            player.removeTag("horde_night_active");
        }
    }
    if (e.id === "lv_zo_debug:togglehordeeffects") {
        if (message === "true") {
            toggleHordeEffects(true);
        }
        if (message === "false") {
            toggleHordeEffects(false);
        }
    }
    if (id === "lv_zo_debug:starthorde") {
        let newTime = 15950
        if (!isNaN(newTime)) {
            world.setTimeOfDay(newTime);
            world.sendMessage(`Time set to ${newTime} ticks.`);
        }
    }
    if (id === "lv_zo_debug:endhorde") {
        let newTime = 22950
        if (!isNaN(newTime)) {
            world.setTimeOfDay(newTime);
            world.sendMessage(`Time set to ${newTime} ticks.`);
        }
    }
    if (id === "lv_zo_debug:settime") {
        let newTime = parseInt(message);
        if (!isNaN(newTime)) {
            world.setTimeOfDay(newTime);
            world.sendMessage(`Time set to ${newTime} ticks.`);
        } else {
            world.sendMessage("Invalid time value. Use: /scriptevent lv_zo_debug:settime <ticks>");
        }
    }

    if (id === "lv_zo_debug:setskin") {
        const args = message.split(" ");
        if (args.length !== 2) {
            world.sendMessage("Usage: /scriptevent lv_zo_debug:setskin <ID> <Range>");
        } else {
            const propertyID = parseInt(args[0]);
            const range = parseFloat(args[1]);

            if (isNaN(propertyID) || isNaN(range)) {
                world.sendMessage("Invalid input. Use numbers like: /scriptevent lv_zo_debug:setskin 1 10");
            } else if (!sourceEntity) {
                world.sendMessage("This command must be triggered by an entity.");
            } else {
                const query = {
                    families: ["zombie"],
                    location: sourceEntity.location,
                    maxDistance: range
                };

                const entities = sourceEntity.dimension.getEntities(query);
                let count = 0;

                for (const entity of entities) {
                    try {
                        entity.setProperty("lv_zo:skin", propertyID);
                        count++;
                    } catch (e) {
                        sourceEntity.sendMessage?.(`Failed to set skin for entity ${entity.id}: ${e}`);
                    }
                }

                sourceEntity.sendMessage?.(`Set skin to ${propertyID} for ${count} zombies within ${range} blocks.`);
            }
        }
    }

    // ===== Corruption Core Script (Enhanced Version) =====

    const overworld = world.getDimension("overworld");
    let corruptionTask = null;
    const corruptionQueue = [];
    const blocksPerTick = 150; // Increased from 110
    const TICK_INTERVAL = 2; // Process every 2 ticks instead of every tick for better performance

    // Configuration moved to top level for easy editing
    const CONFIG = {
        tickInterval: TICK_INTERVAL,
        blocksPerTick: blocksPerTick,
        radiusInnerRatio: 0.43,
        radiusMiddleRatio: 0.86,
        caveRadiusInnerRatio: 0.35,
        caveRadiusMiddleRatio: 0.7,
        caveRadiusOuterRatio: 1.1,
        bushSpawnChance: 0.15,
        minZombies: 3,
        maxZombies: 8,
        maxCorruptionSubCenters: 3,
        maxCaveCorruptionSubCenters: 2,
        subCenterRadiusMinRatio: 0.6,
        subCenterRadiusVariance: 0.2,
        subCenterMaxOffsetSurface: 6,
        subCenterMaxOffsetCave: 4,
        subCenterYOffsetSurface: 2,
        subCenterYOffsetCave: 3
    };

    // Set up system intervals with more efficient timing
    system.runInterval(processCorruptionQueue, CONFIG.tickInterval);
    system.runInterval(updateCorruptionTask, CONFIG.tickInterval);

    // Startup the corruption process from coordinates
    function startCorruption(origin, baseRadius, config, type) {
        // If a corruption task is already running, queue it instead of replacing
        if (corruptionTask) {
            corruptionTask.queue = corruptionTask.queue || [];
            corruptionTask.queue.push({ origin, config, type, maxRadius: baseRadius });
            return;
        }

        corruptionTask = {
            origin,
            config,
            type,
            currentRadius: 0,
            maxRadius: baseRadius,
            queue: []
        };
    }

    // More efficient radius expansion approach
    function updateCorruptionTask() {
        if (!corruptionTask) return;

        const { origin, config, type, currentRadius, maxRadius } = corruptionTask;

        // Calculate points only on the shell of the sphere at current radius
        generateSphericalShell(origin, currentRadius, (pos) => {
            corruptBlockAt(pos, config, type);
        });

        // Increment radius for next update
        corruptionTask.currentRadius++;

        // If we've completed this corruption task, move to the next in queue if available
        if (corruptionTask.currentRadius > corruptionTask.maxRadius) {
            if (corruptionTask.queue && corruptionTask.queue.length > 0) {
                const nextTask = corruptionTask.queue.shift();
                corruptionTask = {
                    origin: nextTask.origin,
                    config: nextTask.config,
                    type: nextTask.type,
                    currentRadius: 0,
                    maxRadius: nextTask.maxRadius,
                    queue: corruptionTask.queue
                };
            } else {
                corruptionTask = null;
            }
        }
    }

    // More efficient sphere shell generation
    function generateSphericalShell(center, radius, callback) {
        if (radius <= 0) {
            callback(center);
            return;
        }

        // Use a more efficient algorithm for generating points on a sphere shell
        // This approach generates fewer points than a cube iteration but covers the sphere surface
        const base = {
            x: Math.floor(center.x),
            y: Math.floor(center.y),
            z: Math.floor(center.z)
        };

        // For larger radii, sample points instead of checking every possible position
        const stepSize = radius > 10 ? Math.ceil(radius / 10) : 1;

        // Only iterate over the outer shell rather than the entire cube
        for (let x = -radius; x <= radius; x += stepSize) {
            for (let y = -radius; y <= radius; y += stepSize) {
                for (let z = -radius; z <= radius; z += stepSize) {
                    const distSquared = x * x + y * y + z * z;
                    const radiusSquared = radius * radius;

                    // Only process blocks close to the sphere surface
                    if (Math.abs(distSquared - radiusSquared) <= radius) {
                        const pos = {
                            x: base.x + x,
                            y: base.y + y,
                            z: base.z + z
                        };

                        // Add some randomness to avoid perfect sphere shells
                        const noise = (Math.random() * 0.8) - 0.4;
                        const adjustedDist = Math.sqrt(distSquared) + noise;

                        // Only process blocks that are approximately at the current radius
                        if (Math.floor(adjustedDist) === radius) {
                            callback(pos);
                        }
                    }
                }
            }
        }
    }

    // Queue processing with error handling
    function processCorruptionQueue() {
        const batchSize = Math.min(blocksPerTick, corruptionQueue.length);

        for (let i = 0; i < batchSize; i++) {
            if (corruptionQueue.length === 0) return;

            try {
                const task = corruptionQueue.pop();
                if (!task) continue;
                corruptBlockAt(task.pos, task.config, task.type);
            } catch (error) {
                // Skip this block if an error occurs
                continue;
            }
        }
    }

    // Enhanced block corruption with better error handling
    function corruptBlockAt(pos, config, type) {
        try {
            const block = overworld.getBlock(pos);
            if (!block || block.isAir || isAlreadyCorrupted(block)) return;

            if (ignoredBlocks.includes(block.typeId)) {
                block.setType("minecraft:air");
                return;
            }

            if (block.typeId.includes("log")) {
                const oldAxis = block.permutation.getState("axis");

                block.setType("lv_zo:corrupted_log");

                if (oldAxis && typeof oldAxis === "string") {
                    block.setPermutation(block.permutation.withState("axis", oldAxis));
                }

                if (Math.random() < 0.20) { // Changed from 0.55 to your original 10% chance
                    const directions = [
                        { dx: -1, dy: 0, dz: 0, face: "east" },
                        { dx: 1, dy: 0, dz: 0, face: "west" },
                        { dx: 0, dy: 0, dz: -1, face: "south" },
                        { dx: 0, dy: 0, dz: 1, face: "north" }
                        // Removed up/down to ensure bush only places on sides
                    ];

                    const opposite = {
                        north: "south",
                        south: "north",
                        east: "west",
                        west: "east"
                    };

                    const choice = directions[Math.floor(Math.random() * directions.length)];
                    const bushPos = {
                        x: pos.x + choice.dx,
                        y: pos.y + choice.dy,
                        z: pos.z + choice.dz
                    };

                    const bushBlock = overworld.getBlock(bushPos); // fixed context: no sourceEntity
                    if (bushBlock && bushBlock.isAir) {
                        bushBlock.setType("lv_zo:corrupted_tree_bush");

                        const face = opposite[choice.face];
                        if (face) {
                            try {
                                bushBlock.setPermutation(
                                    bushBlock.permutation.withState("minecraft:block_face", face)
                                );
                            } catch (e) {
                                // Fallback if permutation fails
                            }
                        }
                    }
                }

                return;
            }


            if (block.typeId.includes("leaves")) {
                block.setType(config.leaveBlock);
                return;
            }

            let corruptionType = (type === "surface")
                ? getCorruptionBlockByDistanceSimple(pos, config)
                : getCorruptionBlockByDistanceCustomSimple(pos, config);

            if (corruptionType) {
                block.setType(corruptionType);
                decorateAround(pos, config);
            }
        } catch (error) {
            // Skip this block if there's an error
        }
    }

    // Improved decoration with better error handling
    function decorateAround(pos, config) {
        try {
            const above = overworld.getBlock({ x: pos.x, y: pos.y + 1, z: pos.z });
            const below = overworld.getBlock({ x: pos.x, y: pos.y - 1, z: pos.z });

            if (above && above.isAir && Math.random() < CONFIG.bushSpawnChance) {
                above.setType(pickWeightedBlock(config.decoBushes));
            }

            if (below && below.isAir) {
                below.setType(pickWeightedBlock(config.decoBelow));
            }
        } catch (error) {
            // Skip decoration if there's an error
        }
    }

    // More efficient block selection using config ratios
    function getCorruptionBlockByDistanceSimple(pos, config) {
        const center = corruptionTask.origin;
        const inner = corruptionTask.maxRadius * CONFIG.radiusInnerRatio;
        const middle = corruptionTask.maxRadius * CONFIG.radiusMiddleRatio;
        const distance = getDistance(pos, center);

        if (distance <= inner) return config.innerBlock;
        if (distance <= middle) return config.middleBlock;
        return config.outerBlock;
    }

    function getCorruptionBlockByDistanceCustomSimple(pos, config) {
        const center = corruptionTask.origin;
        const inner = corruptionTask.maxRadius * CONFIG.caveRadiusInnerRatio;
        const middle = corruptionTask.maxRadius * CONFIG.caveRadiusMiddleRatio;
        const outer = corruptionTask.maxRadius * CONFIG.caveRadiusOuterRatio;
        const distance = getDistance(pos, center);

        if (distance <= inner) return pickWeightedBlock(config.innerBlocks);
        if (distance <= middle) return config.middleBlock;
        if (distance <= outer) return pickWeightedBlock(config.outerBlocks);
        return config.veryOuterBlock;
    }

    // Optimized weighted selection
    function pickWeightedBlock(list) {
        const total = list.reduce((sum, i) => sum + i.weight, 0);
        let rand = Math.random() * total;

        for (const item of list) {
            if (rand < item.weight) return item.id;
            rand -= item.weight;
        }

        return list[0].id;
    }

    // Optimized distance calculation
    function getDistance(pos, center) {
        const dx = pos.x - center.x;
        const dy = pos.y - center.y;
        const dz = pos.z - center.z;
        return Math.sqrt(dx * dx + dy * dy + dz * dz);
    }

    // ====== Configuration ======

    const corruptionCaveConfig = {
        innerBlocks: [
            { id: "lv_zo:corrupted_cave_stone", weight: 90 },
            { id: "lv_zo:corrupted_cave_bone_ore", weight: 10 }
        ],
        middleBlock: "lv_zo:corrupted_cave_soil",
        outerBlocks: [
            { id: "lv_zo:corrupted_cave_block", weight: 96 },
            { id: "lv_zo:corrupted_cave_light", weight: 4 }
        ],
        veryOuterBlock: "lv_zo:corrupted_grass",
        decoBushes: [
            { id: "lv_zo:corrupted_mushroom", weight: 60 },
            { id: "lv_zo:skelly_remains_0", weight: 1 },
            { id: "lv_zo:skelly_remains_1", weight: 1 },
            { id: "lv_zo:skelly_remains_2", weight: 1 }
        ],
        leaveBlock: "lv_zo:corrupted_leaves",
        decoBelow: [
            { id: "minecraft:air", weight: 120 },
            { id: "lv_zo:corrupted_cave_hanging_root", weight: 30 },
            { id: "lv_zo:corrupted_cave_hanging_plant", weight: 30 }
        ]
    };

    const corruptionConfig = {
        innerBlock: "lv_zo:corrupted_soil",
        middleBlock: "lv_zo:corrupted_grass",
        outerBlock: "minecraft:coarse_dirt",
        decoBushes: [
            { id: "lv_zo:corrupted_bush", weight: 20 },
            { id: "lv_zo:corrupted_flower", weight: 10 },
            { id: "lv_zo:corrupted_small_grass", weight: 150 },
            { id: "lv_zo:skelly_remains_0", weight: 2 },
            { id: "lv_zo:skelly_remains_1", weight: 2 },
            { id: "lv_zo:skelly_remains_2", weight: 2 }
        ],
        leaveBlock: "lv_zo:corrupted_leaves",
        decoBelow: [
            { id: "minecraft:air", weight: 85 },
            { id: "minecraft:hanging_roots", weight: 15 },
            { id: "minecraft:pale_hanging_moss", weight: 15 }
        ]
    };

    const corruptionZombieSpawnList = [
        { id: "lv_zo:zomb", weight: 60 },
        { id: "lv_zo:small_zomb", weight: 40 },
        { id: "lv_zo:chubby", weight: 40 },
        { id: "lv_zo:zombomb", weight: 10 },
        { id: "lv_zo:slime_shooter", weight: 10 },
        { id: "lv_zo:slapstick", weight: 10 },
        { id: "lv_zo:tanky", weight: 2 }
    ];

    // Expanded list of ignored blocks for better coverage
    const ignoredBlocks = [
        "minecraft:tall_grass", "minecraft:short_grass", "minecraft:fern",
        "minecraft:large_fern", "minecraft:dandelion", "minecraft:poppy",
        "minecraft:blue_orchid", "minecraft:allium", "minecraft:azure_bluet",
        "minecraft:red_tulip", "minecraft:orange_tulip", "minecraft:white_tulip",
        "minecraft:pink_tulip", "minecraft:oxeye_daisy", "minecraft:cornflower",
        "minecraft:lily_of_the_valley", "minecraft:sunflower", "minecraft:rose_bush",
        "minecraft:peony", "minecraft:lilac", "minecraft:leaf_litter", "minecraft:wildflowers",
        "minecraft:short_dry_grass", "minecraft:tall_dry_grass", "minecraft:bush", "minecraft:vine",
        "minecraft:deadbush", "minecraft:cave_vines_body_with_berries", "minecraft:cave_vines_head_with_berries", "minecraft:cave_vines",
        "minecraft:pointed_dripstone", "minecraft:glow_lichen", "minecraft:waterlily", "minecraft:bamboo", "minecraft:sapling", "minecraft:sugar_cane",
        "minecraft:pale_moss_carpet", "minecraft:open_eyeblossom", "minecraft:closed_eyeblossom", "minecraft:snow_layer",
        // Additional blocks to ignore
        "minecraft:small_dripleaf", "minecraft:big_dripleaf", "minecraft:moss_carpet", "minecraft:kelp", "minecraft:seagrass",
        "minecraft:sea_pickle", "minecraft:torch", "minecraft:wall_torch", "minecraft:lantern", "minecraft:soul_lantern",
        "minecraft:flower_pot", "minecraft:potted_plant", "minecraft:brown_mushroom", "minecraft:red_mushroom", "minecraft:sweet_berry_bush"
    ];

    // Improved isAlreadyCorrupted function with Set for faster lookups
    const corruptedBlockTypes = new Set([
        corruptionConfig.innerBlock,
        corruptionConfig.middleBlock,
        corruptionCaveConfig.middleBlock,
        corruptionCaveConfig.veryOuterBlock,
        "lv_zo:corrupted_log",
        "lv_zo:corrupted_tree_bush",
        "minecraft:hanging_roots",
        "minecraft:water",
        "minecraft:flowing_water",
        "minecraft:lava",
        "minecraft:flowing_lava",
        "minecraft:magma_block",
        "lv_zo:meteor_block",
        "lv_zo:red_meteor_block",
        "lv_zo:scrap_ore",
        "minecraft:repeating_command_block"
    ]);

    // Add all config blocks to the corrupted blocks set
    corruptionConfig.decoBushes.forEach(item => corruptedBlockTypes.add(item.id));
    corruptionConfig.decoBelow.forEach(item => corruptedBlockTypes.add(item.id));
    corruptionCaveConfig.innerBlocks.forEach(item => corruptedBlockTypes.add(item.id));
    corruptionCaveConfig.outerBlocks.forEach(item => corruptedBlockTypes.add(item.id));
    corruptionCaveConfig.decoBushes.forEach(item => corruptedBlockTypes.add(item.id));
    corruptionCaveConfig.decoBelow.forEach(item => corruptedBlockTypes.add(item.id));
    corruptedBlockTypes.add(corruptionConfig.leaveBlock);
    corruptedBlockTypes.add(corruptionCaveConfig.leaveBlock);

    function isAlreadyCorrupted(block) {
        if (!block) return true;
        return corruptedBlockTypes.has(block.typeId);
    }

    // Enhanced zombie spawning with better positioning logic and corruption type awareness
    function spawnCorruptionZombies(origin, baseRadius, type) {
        const numZombies = Math.floor(Math.random() * (CONFIG.maxZombies - CONFIG.minZombies + 1)) + CONFIG.minZombies;
        let spawnedCount = 0;

        // Maximum attempts to find valid spawn positions
        const maxAttempts = numZombies * 3;
        let attempts = 0;

        // Select the event based on corruption type
        const corruptionEvent = type === "cave" ? "lv_zo:cave_corrupted" : "lv_zo:corrupted";

        while (spawnedCount < numZombies && attempts < maxAttempts) {
            attempts++;

            // Generate spawn position with randomized offset
            const offsetX = Math.floor(Math.random() * baseRadius * 2) - baseRadius;
            const offsetZ = Math.floor(Math.random() * baseRadius * 2) - baseRadius;
            const spawnPos = {
                x: origin.x + offsetX,
                y: origin.y,
                z: origin.z + offsetZ
            };

            // Check for a valid spawn location
            try {
                // Find ground level by checking downward
                let validY = findValidSpawnY(spawnPos);
                if (validY === null) continue;

                spawnPos.y = validY;
                const mobId = pickWeightedBlock(corruptionZombieSpawnList);

                // Spawn the entity and trigger the appropriate corruption event
                let entitySpawned = overworld.spawnEntity(mobId, spawnPos);
                entitySpawned.triggerEvent(corruptionEvent);
                spawnedCount++;
            } catch (error) {
                continue; // Skip this spawn attempt if there's an error
            }
        }

        return spawnedCount;
    }

    // Find a valid Y coordinate for spawning
    function findValidSpawnY(pos) {
        // Check current position and a few blocks below
        for (let yOffset = 0; yOffset >= -5; yOffset--) {
            const checkPos = { x: pos.x, y: pos.y + yOffset, z: pos.z };
            const groundBlock = overworld.getBlock({ x: checkPos.x, y: checkPos.y - 1, z: checkPos.z });
            const spawnBlock = overworld.getBlock(checkPos);
            const headBlock = overworld.getBlock({ x: checkPos.x, y: checkPos.y + 1, z: checkPos.z });

            if (groundBlock && !groundBlock.isAir && spawnBlock && spawnBlock.isAir &&
                headBlock && headBlock.isAir) {
                return checkPos.y;
            }
        }

        // Check a few blocks above
        for (let yOffset = 1; yOffset <= 5; yOffset++) {
            const checkPos = { x: pos.x, y: pos.y + yOffset, z: pos.z };
            const groundBlock = overworld.getBlock({ x: checkPos.x, y: checkPos.y - 1, z: checkPos.z });
            const spawnBlock = overworld.getBlock(checkPos);
            const headBlock = overworld.getBlock({ x: checkPos.x, y: checkPos.y + 1, z: checkPos.z });

            if (groundBlock && !groundBlock.isAir && spawnBlock && spawnBlock.isAir &&
                headBlock && headBlock.isAir) {
                return checkPos.y;
            }
        }

        return null; // No valid position found
    }

    if (id === "lv_zo_debug:corrupttest") {
        handleCorruptTest(message, false);

        if (Math.random() < 0.4) {
            const parts = message.split(" ");
            const x = parseInt(parts[1]);
            const y = parseInt(parts[2]);
            const z = parseInt(parts[3]);
            const meteorTypes = ["lv_zo:meteor_s", "lv_zo:meteor_m", "lv_zo:meteor_l"];
            const selectedMeteor = meteorTypes[Math.floor(Math.random() * meteorTypes.length)];

            const baseX = x - 2;
            const baseY = y - randomInt(2, 3);
            const baseZ = z - 2;

            overworld.runCommandAsync(`structure load ${selectedMeteor} ${baseX} ${baseY} ${baseZ}`)

            if (Math.random() < 0.8) {
                const offset = randomInt(4, 6); // 3–5 blocks
                const angle = Math.random() * Math.PI * 2;
                const offsetX = Math.round(Math.cos(angle) * offset);
                const offsetZ = Math.round(Math.sin(angle) * offset);

                const miniX = baseX + offsetX;
                const miniY = baseY - 1;
                const miniZ = baseZ + offsetZ;

                overworld.runCommandAsync(`structure load lv_zo:meteor_mini ${miniX} ${miniY} ${miniZ}`);
            }
        }
    }

    if (id === "lv_zo_debug:corruptcavetest") {
        handleCorruptTest(message, true);
    }

    function handleCorruptTest(message, isCave) {
        try {
            const parts = message.split(" ");
            const baseRadius = parseInt(parts[0]);
            const x = parseInt(parts[1]);
            const y = parseInt(parts[2]);
            const z = parseInt(parts[3]);

            if (isNaN(baseRadius) || isNaN(x) || isNaN(y) || isNaN(z)) {
                return; // Invalid parameters
            }

            const origin = { x, y, z };
            const config = isCave ? corruptionCaveConfig : corruptionConfig;
            const type = isCave ? "cave" : "surface";
            const maxSubCenters = isCave ? CONFIG.maxCaveCorruptionSubCenters : CONFIG.maxCorruptionSubCenters;
            const maxOffset = isCave ? CONFIG.subCenterMaxOffsetCave : CONFIG.subCenterMaxOffsetSurface;
            const yOffset = isCave ? CONFIG.subCenterYOffsetCave : CONFIG.subCenterYOffsetSurface;

            // Start the main corruption
            startCorruption(origin, baseRadius, config, type);

            // Create additional corruption centers
            for (let i = 0; i < maxSubCenters; i++) {
                const offset = {
                    x: Math.floor(Math.random() * (maxOffset * 2 + 1)) - maxOffset,
                    y: Math.floor(Math.random() * (yOffset * 2 + 1)) - yOffset,
                    z: Math.floor(Math.random() * (maxOffset * 2 + 1)) - maxOffset
                };

                const newCenter = {
                    x: origin.x + offset.x,
                    y: origin.y + offset.y,
                    z: origin.z + offset.z
                };

                const subRadius = Math.floor(baseRadius *
                    (CONFIG.subCenterRadiusMinRatio + Math.random() * CONFIG.subCenterRadiusVariance));

                startCorruption(newCenter, subRadius, config, type);
            }

            // Spawn zombies at the main corruption center with the correct corruption type
            spawnCorruptionZombies(origin, baseRadius, type);
        } catch (error) {
            // Handle any errors during corruption test
        }
    }

});