import { world, system, BlockPermutation, ItemStack } from "@minecraft/server";
import { fo } from './utilities/fo_constants.js';


export function initRandomTick() {


    const LEAVES_TYPES = new Map([
        ["minecraft:oak_leaves", "oak_sapling"],
        ["minecraft:spruce_leaves", "spruce_sapling"],
        ["minecraft:birch_leaves", "birch_sapling"],
        ["minecraft:jungle_leaves", "jungle_sapling"],
        ["minecraft:acacia_leaves", "acacia_sapling"],
        ["minecraft:dark_oak_leaves", "dark_oak_sapling"],
        ["minecraft:cherry_leaves", "cherry_sapling"],
        ["minecraft:pale_oak_leaves", "pale_oak_sapling"]
    ]);

    const DIRT_DECAY_BLOCKS = new Set([
        "minecraft:grass_block",
        "minecraft:mycelium"
    ]);

    const COPPER_BLOCKS = new Map();
    [
        ["minecraft:copper_block", "minecraft:exposed_copper", "minecraft:weathered_copper", "minecraft:oxidized_copper"],
        ["minecraft:chiseled_copper", "minecraft:exposed_chiseled_copper", "minecraft:weathered_chiseled_copper", "minecraft:oxidized_chiseled_copper"],
        ["minecraft:copper_bulb", "minecraft:exposed_copper_bulb", "minecraft:weathered_copper_bulb", "minecraft:oxidized_copper_bulb"],
        ["minecraft:copper_grate", "minecraft:exposed_copper_grate", "minecraft:weathered_copper_grate", "minecraft:oxidized_copper_grate"],
        ["minecraft:copper_trapdoor", "minecraft:exposed_copper_trapdoor", "minecraft:weathered_copper_trapdoor", "minecraft:oxidized_copper_trapdoor"],
        ["minecraft:cut_copper", "minecraft:exposed_cut_copper", "minecraft:weathered_cut_copper", "minecraft:oxidized_copper"],
        ["minecraft:cut_copper_slab", "minecraft:exposed_cut_copper_slab", "minecraft:weathered_cut_copper_slab", "minecraft:oxidized_cut_copper_slab"],
        ["minecraft:cut_copper_stairs", "minecraft:exposed_cut_copper_stairs", "minecraft:weathered_cut_copper_stairs", "minecraft:oxidized_cut_copper_stairs"],
        ["minecraft:lightning_rod", "minecraft:exposed_lightning_rod", "minecraft:weathered_lightning_rod", "minecraft:oxidized_lightning_rod"],
        ["minecraft:copper_bars", "minecraft:exposed_copper_bars", "minecraft:weathered_copper_bars", "minecraft:oxidized_copper_bars"],
        ["minecraft:copper_chain", "minecraft:exposed_copper_chain", "minecraft:weathered_copper_chain", "minecraft:oxidized_copper_chain"],
        ["minecraft:copper_lantern", "minecraft:exposed_copper_lantern", "minecraft:weathered_copper_lantern", "minecraft:oxidized_copper_lantern"],
    ].forEach(group => {
        group.forEach(block => COPPER_BLOCKS.set(block, group));
    });

    const AMETHYST_CLUSTERS = [
        "minecraft:small_amethyst_bud",
        "minecraft:medium_amethyst_bud",
        "minecraft:large_amethyst_bud",
        "minecraft:amethyst_cluster"
    ];

    
    let cachedForebodingScore = 0;
    let cachedRandomTick = true;

    
    const SUBCHUNK_OFFSETS = [];
    for (let dx = 0; dx <= 32; dx += 16) {
        for (let dy = 0; dy <= 32; dy += 16) {
            for (let dz = 0; dz <= 32; dz += 16) {
                SUBCHUNK_OFFSETS.push({ dx, dy, dz });
            }
        }
    }

    
    const FOREBODING_CHECK_1 = fo.scoreChapter3;
    const FOREBODING_CHECK_7 = fo.scoreChapter7;

    let tickInterval = 2 * fo.performanceModifier;


    const overworld = fo.overworld;

    
    system.runInterval(() => {
        cachedForebodingScore = fo.forebodingScore;
        cachedRandomTick = world.gameRules.randomTickSpeed > 0;
        tickInterval = 2 * fo.performanceModifier;
    }, 600);

    
    system.runInterval(() => {
        world.gameRules.doFireTick = false;
        system.runTimeout(() => { world.gameRules.doFireTick = true; }, 6);
        if (world.gameRules.randomTickSpeed === 0) world.gameRules.randomTickSpeed = 1;
    }, 100);

    
    system.runInterval(() => {
        system.runJob(doRandomTick(overworld));
    }, tickInterval);



    
    function* doRandomTick(overworld) {
        
        if (cachedForebodingScore > FOREBODING_CHECK_7 && cachedForebodingScore < FOREBODING_CHECK_1 || cachedRandomTick) return;

        const players = overworld.getPlayers();
        if (players.length === 0) return;

        const processedSubchunks = new Set();

        
        for (const player of players) {
            const { x: px, y: py, z: pz } = player.location;

            
            const baseChunkX = Math.floor(px / 16) * 16;
            const baseChunkY = Math.floor(py / 16) * 16;
            const baseChunkZ = Math.floor(pz / 16) * 16;

            
            for (const offset of SUBCHUNK_OFFSETS) {
                const subchunkX = baseChunkX + 16 - offset.dx; 
                const subchunkY = baseChunkY + 16 - offset.dy;
                const subchunkZ = baseChunkZ + 16 - offset.dz;

                
                const key = `${subchunkX},${subchunkY},${subchunkZ}`;
                if (processedSubchunks.has(key)) continue;

                processedSubchunks.add(key);
                processSubchunk(overworld, subchunkX, subchunkY, subchunkZ);
                yield; 
            }
        }
    }

    function processSubchunk(dimension, baseX, baseY, baseZ) {
        const randomIndex = Math.floor(Math.random() * 4096);
        const localX = randomIndex % 16;
        const localY = Math.floor(randomIndex / 256);
        const localZ = Math.floor((randomIndex % 256) / 16);

        const x = baseX + localX;
        const y = baseY + localY;
        const z = baseZ + localZ;

        
        if (y > dimension.heightRange.max || y < dimension.heightRange.min) return;

        const block = dimension.getBlock({ x, y, z });
        if (!block) return;

        const blockTypeId = block.typeId;

        
        if (blockTypeId.includes("leaves")) {
            system.runJob(leaves(block));
            return;
        }

        if (DIRT_DECAY_BLOCKS.has(blockTypeId)) {
            dirtDecay(block);
            return;
        }

        if (blockTypeId === "minecraft:farmland") {
            system.runJob(farmland(block));
            return;
        }

        if (COPPER_BLOCKS.has(blockTypeId)) {
            system.runJob(copperOxidation(block));
            return;
        }

        if (blockTypeId === "minecraft:lit_redstone_ore" || blockTypeId === "minecraft:lit_deepslate_redstone_ore") {
            redstoneOreTurnsOff(block);
            return;
        }

        if (blockTypeId === "minecraft:mud") {
            mudToClay(block);
            return;
        }

        if (blockTypeId === "minecraft:cauldron") {
            system.runJob(cauldronFill(block));
            return;
        }

        if (blockTypeId === "minecraft:portal") {
            netherPortalSpawn(block);
            return;
        }

        if (blockTypeId === "minecraft:budding_amethyst") {
            amethystGrow(block);
            return;
        }

        if (blockTypeId === "minecraft:water" && cachedForebodingScore < FOREBODING_CHECK_7) {
            waterToIce(block);
            return;
        }
    }

    function* leaves(block) {
        const persistent = block.permutation.getState("persistent_bit");
        const update = block.permutation.getState("update_bit");

        if (persistent || !update) return;

        const blockType = block.typeId;
        const { x, y, z } = block.location;
        const dimension = block.dimension;

        const maxDistance = 4;

        try {
            
            for (let dx = -maxDistance; dx <= maxDistance; dx++) {
                for (let dy = -maxDistance; dy <= maxDistance; dy++) {
                    for (let dz = -maxDistance; dz <= maxDistance; dz++) {
                        const distance = Math.abs(dx) + Math.abs(dy) + Math.abs(dz);
                        if (distance > maxDistance) continue;

                        const neighbor = dimension.getBlock({ x: x + dx, y: y + dy, z: z + dz });
                        if (!neighbor) continue;

                        const neighborId = neighbor.typeId;
                        if (neighborId.includes("log") || neighborId.includes("wood")) {
                            return; 
                        }
                        yield; 
                    }
                }
            }

            
            block.setType("minecraft:air");

            
            if (Math.random() > 0.051) return;

            const saplingType = LEAVES_TYPES.get(blockType);
            if (!saplingType) return;

            const saplingItem = new ItemStack(saplingType, 1);
            dimension.spawnItem(saplingItem, block.center());

            
            if (saplingType === "oak_sapling" && Math.random() <= 0.005) {
                const appleItem = new ItemStack("apple", 1);
                dimension.spawnItem(appleItem, block.center());
            }
        } catch (e) {
            
        }
    }

    function dirtDecay(block) {
        const dimension = block.dimension;
        const blockAbove = block.above(1);

        
        if (blockAbove && !blockAbove.isAir) {
            
            const aboveLight = dimension.getLightLevel(blockAbove.location);
            if (aboveLight <= 4) {
                block.setType("minecraft:dirt");
                return;
            }
        }

        
        if (cachedForebodingScore > FOREBODING_CHECK_1 || cachedForebodingScore < FOREBODING_CHECK_7) {
            const flowerChance = (1 / 5400) * tickInterval;
            const grassDecayChance = (1 / 1200) * tickInterval;
            const forebodingAmplifier = 1 + (10 * (cachedForebodingScore / fo.scoreChapter6));

            
            if (Math.random() < (flowerChance * ((forebodingAmplifier - 1) / 3 + 1)) && blockAbove.isAir) {
                const blockSkyLevel = dimension.getSkyLightLevel(blockAbove.location);
                if (blockSkyLevel >= 9) {
                    blockAbove.setType("minecraft:lily_of_the_valley");
                    return;
                }
            }

            
            if (Math.random() < (grassDecayChance * forebodingAmplifier)) block.setType("minecraft:dirt");

        }
    }

    function* farmland(block) {
        const dimension = block.dimension;
        const loc = block.location;
        const currentMoisture = parseInt(block.permutation.getState("moisturized_amount"));

        let foundWater = false;

        try {
            
            for (let dx = -4; dx <= 4 && !foundWater; dx++) {
                for (let dz = -4; dz <= 4 && !foundWater; dz++) {
                    const checkBlock = dimension.getBlock({
                        x: loc.x + dx,
                        y: loc.y,
                        z: loc.z + dz
                    });
                    if (checkBlock?.typeId.includes("water") || checkBlock?.isWaterlogged) {
                        foundWater = true;
                    }
                    yield;
                }
            }

            
            if (!foundWater) {
                for (let dx = -4; dx <= 4 && !foundWater; dx++) {
                    for (let dz = -4; dz <= 4 && !foundWater; dz++) {
                        const checkBlock = dimension.getBlock({
                            x: loc.x + dx,
                            y: loc.y + 1,
                            z: loc.z + dz
                        });
                        if (checkBlock?.typeId.includes("water") || checkBlock?.isWaterlogged) {
                            foundWater = true;
                        }
                        yield;
                    }
                }
            }

            let newMoisture = currentMoisture;

            if (foundWater) {
                if (currentMoisture !== 7) {
                    newMoisture = 7;
                }
            } else {
                if (currentMoisture > 0) {
                    newMoisture = currentMoisture - 1;
                } else if (Math.random() < 0.15 * tickInterval) {
                    block.setType("minecraft:dirt");
                    return;
                }
            }

            if (newMoisture !== currentMoisture) {
                const newPermutation = block.permutation.withState("moisturized_amount", newMoisture);
                block.setPermutation(newPermutation);
            }
        } catch (e) {
            
        }
    }

    function* copperOxidation(block) {
        
        if (block.typeId.includes("waxed")) return;

        const copperGroup = COPPER_BLOCKS.get(block.typeId);
        if (!copperGroup) return;

        
        let currentOxLevel = -1;
        for (let i = 0; i < copperGroup.length; i++) {
            if (block.typeId === copperGroup[i]) {
                currentOxLevel = i + 1;
                break;
            }
        }
        if (currentOxLevel === -1 || currentOxLevel === 4) return; 

        
        if (Math.random() >= (64 / 1125) * tickInterval) return;

        const dimension = block.dimension;
        const loc = block.location;

        let totalCopper = 0;
        let higherOxidized = 0;
        const maxDist = 4;

        
        for (let dx = -maxDist; dx <= maxDist; dx++) {
            for (let dy = -maxDist; dy <= maxDist; dy++) {
                for (let dz = -maxDist; dz <= maxDist; dz++) {
                    const distance = Math.abs(dx) + Math.abs(dy) + Math.abs(dz);
                    if (distance > maxDist) continue;

                    const neighbor = dimension.getBlock({
                        x: loc.x + dx,
                        y: loc.y + dy,
                        z: loc.z + dz
                    });
                    if (!neighbor) continue;

                    const neighborType = neighbor.typeId;
                    if (neighborType.includes("waxed_")) continue;

                    const neighborGroup = COPPER_BLOCKS.get(neighborType);
                    if (neighborGroup) {
                        let neighborOxLevel = -1;
                        for (let i = 0; i < neighborGroup.length; i++) {
                            if (neighborType === neighborGroup[i]) {
                                neighborOxLevel = i + 1;
                                break;
                            }
                        }

                        if (neighborOxLevel !== -1) {
                            totalCopper++;
                            if (neighborOxLevel > currentOxLevel) {
                                higherOxidized++;
                            } else if (neighborOxLevel < currentOxLevel) {
                                
                                return;
                            }
                        }
                    }
                    yield;
                }
            }
        }

        if (totalCopper === 0) return;

        
        const m = (currentOxLevel === 1) ? 0.75 : 1;
        const c = (higherOxidized + 1) / (totalCopper + 1);
        const finalChance = m * c * c;

        if (Math.random() < finalChance) {
            
            const oldStates = block.permutation.getAllStates();
            const nextState = copperGroup[currentOxLevel]; 

            block.setType(nextState);

            
            let newPerm = block.permutation;
            for (const [stateName, stateValue] of Object.entries(oldStates)) {
                try {
                    newPerm = newPerm.withState(stateName, stateValue);
                } catch (e) {
                    
                }
            }
            block.setPermutation(newPerm);
        }
    }

    function redstoneOreTurnsOff(block) {
        const blockType = block.typeId;
        if (blockType === "minecraft:lit_redstone_ore") block.setType("minecraft:redstone_ore");
        else if (blockType === "minecraft:lit_deepslate_redstone_ore") block.setType("minecraft:deepslate_redstone_ore");
    }

    function mudToClay(block) {
        
        if (Math.random() > (44 / 256) * tickInterval) return;

        const dripstoneCheckBlock = block.below(2);
        if (!dripstoneCheckBlock) return;

        const hangingState = dripstoneCheckBlock.permutation.getState("hanging");

        if (dripstoneCheckBlock.typeId === "minecraft:pointed_dripstone" && hangingState) {
            block.setType("minecraft:clay");
        }
    }


    function* cauldronFill(block) {
        const roll = Math.random();
        if (roll > (45 / 256) * tickInterval) return;

        const cauldronFillLevel = block.permutation.getState("fill_level");
        if (cauldronFillLevel === 6) return;

        const dimension = block.dimension;
        const loc = block.location;

        try {
            
            let foundDripstone = null;
            for (let offset = 1; offset <= 10; offset++) {
                if ((loc.y + offset) > dimension.heightRange.max) return;

                const aboveBlock = block.above(offset);
                if (!aboveBlock) continue;

                if (aboveBlock.typeId === "minecraft:air") {
                    continue; 
                }

                if (aboveBlock.typeId === "minecraft:pointed_dripstone") {
                    foundDripstone = aboveBlock;
                    break; 
                }

                yield;
                break; 
            }

            if (!foundDripstone) return;

            
            let highestDripstone = foundDripstone;
            for (let h = 1; h <= 10; h++) {
                const nextDripstone = highestDripstone.above(1);
                if (!nextDripstone || nextDripstone.typeId !== "minecraft:pointed_dripstone") {
                    break;
                }
                highestDripstone = nextDripstone;
                yield;
            }

            
            const sourceBlock = highestDripstone.above(2);
            if (!sourceBlock) return;

            let sourceLiquid = null;
            if (sourceBlock.typeId === "minecraft:water") {
                sourceLiquid = "water";
            } else if (sourceBlock.typeId === "minecraft:lava") {
                sourceLiquid = "lava";
            } else {
                return; 
            }

            const cauldronLiquid = block.permutation.getState("cauldron_liquid");
            const newFillLevel = Math.min(cauldronFillLevel + 1, 6);

            
            if (cauldronFillLevel === 0) {
                
                let liquidPermutation = block.permutation.withState("cauldron_liquid", "water");
                block.setPermutation(liquidPermutation);

                if (sourceLiquid === "water") {
                    block.setPermutation(block.permutation.withState("fill_level", newFillLevel));
                } else if (sourceLiquid === "lava" && roll <= (15 / 256)) {
                    block.setPermutation(block.permutation.withState("fill_level", newFillLevel));
                    liquidPermutation = block.permutation.withState("cauldron_liquid", "lava");
                    block.setPermutation(liquidPermutation);
                }
            } else {
                
                if (cauldronLiquid === "water" && sourceLiquid === "water") {
                    block.setPermutation(block.permutation.withState("fill_level", newFillLevel));
                } else if (cauldronLiquid === "lava" && sourceLiquid === "lava" && roll <= (15 / 256)) {
                    block.setPermutation(block.permutation.withState("fill_level", newFillLevel));
                }
            }
        } catch (e) {
            
        }
    }

    
    function netherPortalSpawn(block) {
        
        if (Math.random() >= (3 / 2000) * tickInterval) return;

        const loc = block.location;

        
        if (loc.y < 0) return;

        
        let axis = block.permutation.getState("portal_axis");
        if (axis === "unknown") axis = "z";

        
        let offset = { x: 0, y: 0, z: 0 };
        if (axis === "x") {
            offset.z = 1; 
        } else if (axis === "z") {
            offset.x = 1; 
        }

        const spawnPos = {
            x: loc.x + offset.x,
            y: loc.y + offset.y,
            z: loc.z + offset.z
        };

        
        const spawnBlock = block.dimension.getBlock(spawnPos);
        if (!spawnBlock || (!spawnBlock.isAir && !spawnBlock.above(1)?.isAir)) return;

        
        try {
            block.dimension.spawnEntity("minecraft:zombie_pigman", spawnBlock.location);
        } catch (e) {
            
        }
    }

    function amethystGrow(block) {
        if (Math.random() >= 0.20 * tickInterval) return;

        try {
            const growthDirections = [
                { block: block.above(1), direction: "up" },
                { block: block.below(1), direction: "down" },
                { block: block.east(1), direction: "east" },
                { block: block.west(1), direction: "west" },
                { block: block.north(1), direction: "north" },
                { block: block.south(1), direction: "south" }
            ];

            
            const randomIndex = Math.floor(Math.random() * growthDirections.length);
            const { block: targetBlock, direction } = growthDirections[randomIndex];

            if (!targetBlock) return;

            const targetType = targetBlock.typeId;

            if (targetBlock.isAir || targetType === "minecraft:water") {
                
                targetBlock.setType("minecraft:small_amethyst_bud");
                const permutation = targetBlock.permutation.withState("minecraft:block_face", direction);
                targetBlock.setPermutation(permutation);

            } else if (AMETHYST_CLUSTERS.includes(targetType)) {
                
                const currentIndex = AMETHYST_CLUSTERS.indexOf(targetType);
                if (currentIndex < AMETHYST_CLUSTERS.length - 1) {
                    const nextStage = AMETHYST_CLUSTERS[currentIndex + 1];
                    const faceState = targetBlock.permutation.getState("minecraft:block_face");

                    targetBlock.setType(nextStage);
                    const permutation = targetBlock.permutation.withState("minecraft:block_face", faceState);
                    targetBlock.setPermutation(permutation);
                }
            }
        } catch (e) {
            
        }
    }

    function waterToIce(block) {
        if (cachedForebodingScore >= FOREBODING_CHECK_7) return;
        const iceChance = (1 / 30) * tickInterval;
        if (Math.random() > iceChance) return;

        const blockAbove = block.above(1);
        if (blockAbove?.isAir) {
            block.setType("minecraft:ice");
        }
    }

}