package com.livingworld.platform.neoforge;

import com.livingworld.core.LivingWorldConstants;
import com.livingworld.debug.DiagnosticCategory;
import com.livingworld.debug.LivingWorldLogger;
import com.livingworld.modules.worldeffects.WorldEffectConsumer;
import com.livingworld.modules.worldeffects.WorldEffectRequest;
import java.util.Random;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.Heightmap;

/**
 * Translates {@link WorldEffectRequest}s from the ecosystem simulation into
 * concrete Minecraft block operations and particle effects.
 *
 * <p>Called on the server tick thread. Block writes are guarded by a
 * {@link ServerLevel#isLoaded} check to avoid force-loading chunks.</p>
 */
public final class NeoForgeWorldEffectExecutor implements WorldEffectConsumer {

    private static final int REGION_BLOCKS =
            LivingWorldConstants.DEFAULT_REGION_SIZE_CHUNKS * 16;
    private static final int BLOCK_ATTEMPTS = 20;
    private static final int MIN_GRASS_LIGHT = 9;

    private final Supplier<MinecraftServer> serverSupplier;
    private final Random random = new Random();

    public NeoForgeWorldEffectExecutor(Supplier<MinecraftServer> serverSupplier) {
        this.serverSupplier = serverSupplier;
    }

    @Override
    public void consume(WorldEffectRequest request) {
        MinecraftServer server = serverSupplier.get();
        if (server == null) {
            return;
        }
        ResourceKey<Level> dimensionKey = ResourceKey.create(
                Registries.DIMENSION,
                ResourceLocation.parse(request.region().dimensionId()));
        ServerLevel level = server.getLevel(dimensionKey);
        if (level == null) {
            return;
        }
        int baseX = request.region().x() * REGION_BLOCKS;
        int baseZ = request.region().z() * REGION_BLOCKS;

        switch (request.type()) {
            case GRASS_DEGRADES_TO_DIRT ->
                    degradeGrass(level, baseX, baseZ, request.intensity());
            case VEGETATION_SPREADS ->
                    spreadVegetation(level, baseX, baseZ, request.intensity());
            case POLLUTION_VISUAL_INDICATOR ->
                    spawnPollutionParticles(level, baseX, baseZ, request.intensity());
            case SAPLING_GROWTH_BOOSTED ->
                    placeSaplings(level, baseX, baseZ, request.intensity());
            case SAPLING_GROWTH_SLOWED -> {} // requires mixin; deferred
            case WILDFIRE ->
                    igniteVegetation(level, baseX, baseZ, request.intensity());
            case VEGETATION_DIES ->
                    killVegetation(level, baseX, baseZ, request.intensity());
            case WATER_POOL_FORMS ->
                    formWaterPool(level, baseX, baseZ, request.intensity());
            case RIVER_CARVE ->
                    carveRiverChannel(level, baseX, baseZ, request.intensity());
            case GROUND_SPRING_EMERGES ->
                    groundSpringEmerges(level, baseX, baseZ, request.intensity());
            case HYDRAULIC_EROSION ->
                    hydraulicErosion(level, baseX, baseZ, request.intensity());
            case LAVA_FLOW ->
                    lavaFlow(level, baseX, baseZ, request.intensity());
            case ASH_DEPOSIT ->
                    ashDeposit(level, baseX, baseZ, request.intensity());
            case COBBLESTONE_FORMS ->
                    cobblestoneFromLava(level, baseX, baseZ, request.intensity());
        }
    }

    private void degradeGrass(ServerLevel level, int baseX, int baseZ, double intensity) {
        int attempts = Math.max(1, (int) (intensity * BLOCK_ATTEMPTS));
        for (int i = 0; i < attempts; i++) {
            BlockPos pos = surfaceAt(level, baseX + random.nextInt(REGION_BLOCKS),
                    baseZ + random.nextInt(REGION_BLOCKS));
            if (pos == null) continue;
            BlockPos above = pos.above();
            // Clear plants sitting on top of the block first
            if (level.isLoaded(above)) {
                var aboveState = level.getBlockState(above);
                if (aboveState.is(Blocks.SHORT_GRASS) || aboveState.is(Blocks.TALL_GRASS)
                        || aboveState.is(Blocks.DANDELION) || aboveState.is(Blocks.POPPY)
                        || aboveState.is(Blocks.OAK_SAPLING)) {
                    level.setBlock(above, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
                }
            }
            var state = level.getBlockState(pos);
            if (state.is(Blocks.GRASS_BLOCK)) {
                level.setBlock(pos, Blocks.DIRT.defaultBlockState(), Block.UPDATE_ALL);
                LivingWorldLogger.info(DiagnosticCategory.SIMULATION,
                        "WorldEffect GRASS_DEGRADES_TO_DIRT at " + pos);
            } else if (state.is(Blocks.DIRT) && intensity > 0.5) {
                level.setBlock(pos, Blocks.COARSE_DIRT.defaultBlockState(), Block.UPDATE_ALL);
            }
        }
    }

    private void spreadVegetation(ServerLevel level, int baseX, int baseZ, double intensity) {
        int attempts = Math.max(1, (int) (intensity * BLOCK_ATTEMPTS));
        for (int i = 0; i < attempts; i++) {
            BlockPos pos = surfaceAt(level, baseX + random.nextInt(REGION_BLOCKS),
                    baseZ + random.nextInt(REGION_BLOCKS));
            if (pos == null) continue;
            BlockPos above = pos.above();
            boolean aboveAir = level.isLoaded(above) && level.getBlockState(above).isAir();
            boolean brightEnough = aboveAir && level.getRawBrightness(above, 0) >= MIN_GRASS_LIGHT;

            var state = level.getBlockState(pos);
            if (state.is(Blocks.DIRT) && brightEnough) {
                level.setBlock(pos, Blocks.GRASS_BLOCK.defaultBlockState(), Block.UPDATE_ALL);
                LivingWorldLogger.info(DiagnosticCategory.SIMULATION,
                        "WorldEffect VEGETATION_SPREADS at " + pos);
            } else if (state.is(Blocks.GRASS_BLOCK) && brightEnough) {
                // Add surface plants — flowers 1-in-5 chance, short grass otherwise
                Block plant = random.nextInt(5) == 0
                        ? (random.nextBoolean() ? Blocks.DANDELION : Blocks.POPPY)
                        : Blocks.SHORT_GRASS;
                level.setBlock(above, plant.defaultBlockState(), Block.UPDATE_ALL);
            }
        }
    }

    private void placeSaplings(ServerLevel level, int baseX, int baseZ, double intensity) {
        int attempts = Math.max(1, (int) (intensity * BLOCK_ATTEMPTS));
        for (int i = 0; i < attempts; i++) {
            BlockPos pos = surfaceAt(level, baseX + random.nextInt(REGION_BLOCKS),
                    baseZ + random.nextInt(REGION_BLOCKS));
            if (pos == null) continue;
            var state = level.getBlockState(pos);
            if (!state.is(Blocks.GRASS_BLOCK) && !state.is(Blocks.DIRT)) continue;
            BlockPos above = pos.above();
            if (!level.isLoaded(above) || !level.getBlockState(above).isAir()) continue;
            Block sapling = pickSapling(intensity);
            level.setBlock(above, sapling.defaultBlockState(), Block.UPDATE_ALL);
            LivingWorldLogger.info(DiagnosticCategory.SIMULATION,
                    "WorldEffect SAPLING_GROWTH_BOOSTED (" + sapling + ") at " + pos);
        }
    }

    /**
     * Selects a sapling species weighted by succession intensity.
     * Low intensity (young woodland, ~0.5): pioneer species — oak and birch dominate.
     * High intensity (mature forest, ~1.0): diverse canopy — spruce, dark oak, cherry join in.
     */
    private Block pickSapling(double intensity) {
        int r = random.nextInt(100);
        if (intensity < 0.7) {
            // Young woodland: pioneer species only
            return r < 55 ? Blocks.OAK_SAPLING : Blocks.BIRCH_SAPLING;
        } else if (intensity < 0.9) {
            // Developing forest: conifers begin to establish
            if (r < 35) return Blocks.OAK_SAPLING;
            if (r < 65) return Blocks.BIRCH_SAPLING;
            if (r < 85) return Blocks.SPRUCE_SAPLING;
            return Blocks.DARK_OAK_SAPLING;
        } else {
            // Mature forest: full species diversity
            if (r < 25) return Blocks.OAK_SAPLING;
            if (r < 45) return Blocks.BIRCH_SAPLING;
            if (r < 62) return Blocks.SPRUCE_SAPLING;
            if (r < 77) return Blocks.DARK_OAK_SAPLING;
            if (r < 90) return Blocks.CHERRY_SAPLING;
            return Blocks.JUNGLE_SAPLING;
        }
    }

    private void igniteVegetation(ServerLevel level, int baseX, int baseZ, double intensity) {
        int attempts = Math.max(1, (int) (intensity * 5));
        for (int i = 0; i < attempts; i++) {
            BlockPos pos = surfaceAt(level, baseX + random.nextInt(REGION_BLOCKS),
                    baseZ + random.nextInt(REGION_BLOCKS));
            if (pos == null) continue;
            var state = level.getBlockState(pos);
            if (!state.is(Blocks.GRASS_BLOCK) && !state.is(BlockTags.LEAVES)
                    && !state.is(BlockTags.LOGS)) continue;
            BlockPos above = pos.above();
            if (!level.isLoaded(above) || !level.getBlockState(above).isAir()) continue;
            level.setBlock(above, Blocks.FIRE.defaultBlockState(), Block.UPDATE_ALL);
            LivingWorldLogger.info(DiagnosticCategory.SIMULATION,
                    "WorldEffect WILDFIRE ignited at " + above);
        }
    }

    /**
     * Strips leaves and upper trunk sections from trees, and removes surface plants.
     * Uses MOTION_BLOCKING (includes canopy) vs MOTION_BLOCKING_NO_LEAVES (ground) to
     * detect trees — when the two heights differ, a canopy is present.
     * At low intensity: leaves only. At high intensity: leaves + upper log sections.
     */
    private void killVegetation(ServerLevel level, int baseX, int baseZ, double intensity) {
        int attempts = Math.max(2, (int)(intensity * BLOCK_ATTEMPTS * 2));
        for (int i = 0; i < attempts; i++) {
            int x = baseX + random.nextInt(REGION_BLOCKS);
            int z = baseZ + random.nextInt(REGION_BLOCKS);

            int canopyY = level.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z) - 1;
            int groundY = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z) - 1;

            if (canopyY > groundY) {
                // A tree canopy is present — strip downward from canopy.
                for (int y = canopyY; y > groundY; y--) {
                    BlockPos pos = new BlockPos(x, y, z);
                    if (!level.isLoaded(pos)) break;
                    var state = level.getBlockState(pos);
                    if (state.is(BlockTags.LEAVES)) {
                        level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
                    } else if (state.is(BlockTags.LOGS) && y > groundY + 1 && intensity > 0.3) {
                        // Remove upper trunk, leave bottom two blocks as a stump.
                        level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
                    }
                }
            } else {
                // No canopy — clear surface plants (grass, flowers, saplings).
                if (groundY < level.getMinBuildHeight()) continue;
                BlockPos surfacePos = new BlockPos(x, groundY, z);
                if (!level.isLoaded(surfacePos)) continue;
                BlockPos abovePos = surfacePos.above();
                if (!level.isLoaded(abovePos)) continue;
                var above = level.getBlockState(abovePos);
                if (above.is(Blocks.SHORT_GRASS) || above.is(Blocks.FERN)
                        || above.is(Blocks.TALL_GRASS) || above.is(Blocks.LARGE_FERN)
                        || above.is(BlockTags.SAPLINGS)
                        || above.is(Blocks.DANDELION) || above.is(Blocks.POPPY)) {
                    level.setBlock(abovePos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
                }
            }
        }
        LivingWorldLogger.info(DiagnosticCategory.SIMULATION,
                "WorldEffect VEGETATION_DIES intensity=" + String.format("%.2f", intensity)
                        + " at (" + baseX + "," + baseZ + ")");
    }

    /**
     * Places water source blocks in the lowest depression found among random surface samples.
     * Fires ~5% of times it is called so pools build gradually rather than flooding the region.
     * Once the water body scan detects these blocks the region gains a hydration boost that
     * drives succession — the core mechanism behind desert→grassland transformation.
     */
    private void formWaterPool(ServerLevel level, int baseX, int baseZ, double intensity) {
        if (random.nextDouble() > 0.05) return; // throttle: ~1 pool placement per 20 calls

        // Find the lowest surface point among a set of random samples (depression heuristic).
        int samples = Math.max(8, (int) (intensity * 20));
        BlockPos lowestPos = null;
        for (int i = 0; i < samples; i++) {
            BlockPos candidate = surfaceAt(level,
                    baseX + random.nextInt(REGION_BLOCKS),
                    baseZ + random.nextInt(REGION_BLOCKS));
            if (candidate != null && (lowestPos == null || candidate.getY() < lowestPos.getY())) {
                lowestPos = candidate;
            }
        }
        if (lowestPos == null) return;

        // Only pool on permeable/soft surfaces — not stone floors.
        var surface = level.getBlockState(lowestPos);
        if (!surface.is(Blocks.SAND) && !surface.is(Blocks.RED_SAND)
                && !surface.is(Blocks.GRAVEL) && !surface.is(Blocks.DIRT)
                && !surface.is(Blocks.COARSE_DIRT) && !surface.is(Blocks.GRASS_BLOCK)) return;

        BlockPos poolPos = lowestPos.above();
        if (!level.isLoaded(poolPos)) return;
        var poolState = level.getBlockState(poolPos);
        if (!poolState.isAir() && !poolState.is(Blocks.WATER)) return;

        level.setBlock(poolPos, Blocks.WATER.defaultBlockState(), Block.UPDATE_ALL);
        LivingWorldLogger.info(DiagnosticCategory.SIMULATION, "WorldEffect WATER_POOL_FORMS at " + poolPos);

        // At high intensity the pool widens into adjacent depressions at the same height.
        if (intensity > 0.6) {
            for (Direction dir : Direction.Plane.HORIZONTAL) {
                BlockPos adj = poolPos.relative(dir);
                if (level.isLoaded(adj) && level.getBlockState(adj).isAir()
                        && level.getBlockState(adj.below()).isSolid()
                        && adj.getY() <= poolPos.getY()) {
                    level.setBlock(adj, Blocks.WATER.defaultBlockState(), Block.UPDATE_ALL);
                }
            }
        }
    }

    private void spawnPollutionParticles(
            ServerLevel level, int baseX, int baseZ, double intensity) {
        int count = Math.max(1, (int) (intensity * 8));
        for (int i = 0; i < count; i++) {
            double x = baseX + random.nextDouble() * REGION_BLOCKS;
            double z = baseZ + random.nextDouble() * REGION_BLOCKS;
            int y = level.getHeight(Heightmap.Types.WORLD_SURFACE, (int) x, (int) z);
            level.sendParticles(ParticleTypes.SMOKE, x, y + 1.5, z, 3, 0.5, 0.5, 0.5, 0.02);
        }
    }

    /**
     * Gradually carves a river channel by replacing natural surface blocks with water
     * and optionally exposing the layer beneath (deepening the channel over time).
     * Only fires ~10% of the time per call so rivers form over many sim cycles.
     */
    private void carveRiverChannel(ServerLevel level, int baseX, int baseZ, double intensity) {
        if (random.nextDouble() > 0.10) return; // throttle: gradual formation
        int attempts = Math.max(1, (int) (intensity * 4));
        for (int i = 0; i < attempts; i++) {
            int x = baseX + random.nextInt(REGION_BLOCKS);
            int z = baseZ + random.nextInt(REGION_BLOCKS);
            int surfaceY = level.getHeight(Heightmap.Types.WORLD_SURFACE, x, z) - 1;
            if (surfaceY < level.getMinBuildHeight() + 4) continue;

            BlockPos surfacePos = new BlockPos(x, surfaceY, z);
            if (!level.isLoaded(surfacePos)) continue;

            var state = level.getBlockState(surfacePos);
            // Only carve natural soft terrain — never touch player-placed blocks
            boolean softNatural = state.is(Blocks.GRASS_BLOCK) || state.is(Blocks.DIRT)
                    || state.is(Blocks.COARSE_DIRT) || state.is(Blocks.SAND)
                    || state.is(Blocks.GRAVEL);
            if (!softNatural) continue;

            // Replace surface with a water source
            level.setBlock(surfacePos, Blocks.WATER.defaultBlockState(), Block.UPDATE_ALL);

            // At high intensity also expose the layer below (deepens channel slowly)
            if (intensity > 0.6) {
                BlockPos below = surfacePos.below();
                if (level.isLoaded(below)) {
                    var belowState = level.getBlockState(below);
                    if (belowState.is(Blocks.STONE) || belowState.is(Blocks.GRAVEL)
                            || belowState.is(Blocks.DIRT)) {
                        level.setBlock(below, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
                    }
                }
            }
            LivingWorldLogger.info(DiagnosticCategory.SIMULATION,
                    "WorldEffect RIVER_CARVE at " + surfacePos);
        }
    }

    /**
     * Places a permanent water source at the lowest natural terrain point found in the
     * region. The source then flows downhill under vanilla water physics, forming a river
     * without any further simulation involvement.
     */
    private void groundSpringEmerges(ServerLevel level, int baseX, int baseZ, double intensity) {
        if (random.nextDouble() > 0.30) return;
        int samples = 20;
        BlockPos lowestPos = null;
        for (int i = 0; i < samples; i++) {
            BlockPos candidate = surfaceAt(level,
                    baseX + random.nextInt(REGION_BLOCKS),
                    baseZ + random.nextInt(REGION_BLOCKS));
            if (candidate != null && (lowestPos == null || candidate.getY() < lowestPos.getY())) {
                lowestPos = candidate;
            }
        }
        if (lowestPos == null) return;
        var surface = level.getBlockState(lowestPos);
        if (!surface.is(Blocks.DIRT) && !surface.is(Blocks.GRASS_BLOCK)
                && !surface.is(Blocks.SAND) && !surface.is(Blocks.GRAVEL)
                && !surface.is(Blocks.COARSE_DIRT)) return;
        BlockPos springPos = lowestPos.above();
        if (!level.isLoaded(springPos)) return;
        if (!level.getBlockState(springPos).isAir()) return;
        level.setBlock(springPos, Blocks.WATER.defaultBlockState(), Block.UPDATE_ALL);
        LivingWorldLogger.info(DiagnosticCategory.SIMULATION, "WorldEffect GROUND_SPRING_EMERGES at " + springPos);
    }

    /**
     * Flowing water (level 1–7) adjacent to soft rock gradually softens the terrain:
     * stone → gravel → sand, sand/dirt → air. Widens river valleys over time without
     * requiring rainfall; only fires ~15% of the time per call.
     */
    private void hydraulicErosion(ServerLevel level, int baseX, int baseZ, double intensity) {
        if (random.nextDouble() > 0.15) return;
        int attempts = Math.max(1, (int) (intensity * 6));
        for (int i = 0; i < attempts; i++) {
            int x = baseX + random.nextInt(REGION_BLOCKS);
            int z = baseZ + random.nextInt(REGION_BLOCKS);
            int surfaceY = level.getHeight(Heightmap.Types.WORLD_SURFACE, x, z) - 1;
            if (surfaceY < level.getMinBuildHeight()) continue;
            // Scan downward for flowing water
            for (int y = surfaceY; y > surfaceY - 6; y--) {
                BlockPos pos = new BlockPos(x, y, z);
                if (!level.isLoaded(pos)) break;
                var fluidState = level.getFluidState(pos);
                if (!fluidState.is(FluidTags.WATER) || fluidState.isSource()) continue;
                // Found flowing water — soften adjacent horizontal blocks
                for (Direction dir : Direction.Plane.HORIZONTAL) {
                    BlockPos adj = pos.relative(dir);
                    if (!level.isLoaded(adj)) continue;
                    var adjState = level.getBlockState(adj);
                    if (adjState.is(Blocks.STONE) || adjState.is(Blocks.DEEPSLATE)) {
                        if (intensity > 0.65) level.setBlock(adj, Blocks.GRAVEL.defaultBlockState(), Block.UPDATE_ALL);
                    } else if (adjState.is(Blocks.GRAVEL) && intensity > 0.45) {
                        level.setBlock(adj, Blocks.SAND.defaultBlockState(), Block.UPDATE_ALL);
                    } else if ((adjState.is(Blocks.SAND) || adjState.is(Blocks.DIRT)
                            || adjState.is(Blocks.COARSE_DIRT)) && intensity > 0.75) {
                        level.setBlock(adj, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
                    }
                }
                break;
            }
        }
    }

    /**
     * Active volcanic eruption: places lava source blocks at random high-surface positions.
     * Lava flows downhill under vanilla physics, permanently reshaping terrain.
     * Fires ~20% of the time per call so flows build gradually.
     */
    private void lavaFlow(ServerLevel level, int baseX, int baseZ, double intensity) {
        if (random.nextDouble() > 0.20) return;
        int attempts = Math.max(1, (int) (intensity * 3));
        for (int i = 0; i < attempts; i++) {
            int x = baseX + random.nextInt(REGION_BLOCKS);
            int z = baseZ + random.nextInt(REGION_BLOCKS);
            int y = level.getHeight(Heightmap.Types.WORLD_SURFACE, x, z) - 1;
            if (y < level.getMinBuildHeight()) continue;
            BlockPos pos = new BlockPos(x, y, z);
            if (!level.isLoaded(pos)) continue;
            var state = level.getBlockState(pos);
            boolean naturalRock = state.is(Blocks.STONE) || state.is(Blocks.DEEPSLATE)
                    || state.is(Blocks.ANDESITE) || state.is(Blocks.DIORITE)
                    || state.is(Blocks.GRANITE) || state.is(Blocks.GRAVEL)
                    || state.is(Blocks.GRASS_BLOCK) || state.is(Blocks.DIRT)
                    || state.is(Blocks.TUFF);
            if (!naturalRock) continue;
            BlockPos above = pos.above();
            if (!level.isLoaded(above) || !level.getBlockState(above).isAir()) continue;
            level.setBlock(above, Blocks.LAVA.defaultBlockState(), Block.UPDATE_ALL);
            LivingWorldLogger.info(DiagnosticCategory.SIMULATION, "WorldEffect LAVA_FLOW at " + above);
        }
    }

    /**
     * Deposits volcanic ash: grass → tuff, loose soil → gravel; surface plants are killed.
     * Fires ~25% of the time; applied over wide areas during eruptions.
     */
    private void ashDeposit(ServerLevel level, int baseX, int baseZ, double intensity) {
        if (random.nextDouble() > 0.25) return;
        int attempts = Math.max(1, (int) (intensity * BLOCK_ATTEMPTS));
        for (int i = 0; i < attempts; i++) {
            BlockPos pos = surfaceAt(level,
                    baseX + random.nextInt(REGION_BLOCKS),
                    baseZ + random.nextInt(REGION_BLOCKS));
            if (pos == null) continue;
            var state = level.getBlockState(pos);
            if (state.is(Blocks.GRASS_BLOCK)) {
                level.setBlock(pos, Blocks.TUFF.defaultBlockState(), Block.UPDATE_ALL);
            } else if (state.is(Blocks.DIRT) || state.is(Blocks.COARSE_DIRT)) {
                level.setBlock(pos, Blocks.GRAVEL.defaultBlockState(), Block.UPDATE_ALL);
            }
            BlockPos above = pos.above();
            if (!level.isLoaded(above)) continue;
            var aboveState = level.getBlockState(above);
            if (aboveState.is(Blocks.SHORT_GRASS) || aboveState.is(Blocks.TALL_GRASS)
                    || aboveState.is(Blocks.FERN) || aboveState.is(BlockTags.SAPLINGS)
                    || aboveState.is(Blocks.DANDELION) || aboveState.is(Blocks.POPPY)) {
                level.setBlock(above, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
            }
        }
    }

    /**
     * Solidifies cooling lava into new permanent land: lava adjacent to water → cobblestone;
     * lava exposed to air → basalt. This creates terrain that did not exist before the eruption.
     * Fires ~35% of the time per call.
     */
    private void cobblestoneFromLava(ServerLevel level, int baseX, int baseZ, double intensity) {
        if (random.nextDouble() > 0.35) return;
        int attempts = Math.max(2, (int) (intensity * 8));
        for (int i = 0; i < attempts; i++) {
            int x = baseX + random.nextInt(REGION_BLOCKS);
            int z = baseZ + random.nextInt(REGION_BLOCKS);
            int surfY = level.getHeight(Heightmap.Types.WORLD_SURFACE, x, z);
            for (int y = surfY + 2; y >= Math.max(level.getMinBuildHeight(), surfY - 4); y--) {
                BlockPos pos = new BlockPos(x, y, z);
                if (!level.isLoaded(pos)) continue;
                if (!level.getBlockState(pos).is(Blocks.LAVA)) continue;
                boolean adjacentWater = false;
                for (Direction dir : new Direction[]{
                        Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST, Direction.UP}) {
                    BlockPos adj = pos.relative(dir);
                    if (level.isLoaded(adj) && level.getFluidState(adj).is(FluidTags.WATER)) {
                        adjacentWater = true;
                        break;
                    }
                }
                if (adjacentWater) {
                    level.setBlock(pos, Blocks.COBBLESTONE.defaultBlockState(), Block.UPDATE_ALL);
                    LivingWorldLogger.info(DiagnosticCategory.SIMULATION, "WorldEffect COBBLESTONE_FORMS at " + pos);
                } else if (random.nextDouble() < 0.40) {
                    level.setBlock(pos, Blocks.BASALT.defaultBlockState(), Block.UPDATE_ALL);
                    LivingWorldLogger.info(DiagnosticCategory.SIMULATION, "WorldEffect COBBLESTONE_FORMS basalt at " + pos);
                }
                break;
            }
        }
    }

    private BlockPos surfaceAt(ServerLevel level, int x, int z) {
        int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z) - 1;
        if (y < level.getMinBuildHeight()) {
            return null;
        }
        BlockPos pos = new BlockPos(x, y, z);
        return level.isLoaded(pos) ? pos : null;
    }
}
