import { BlockPermutation, ItemStack, world, system } from "@minecraft/server";
import { dropItemCenterOfBlock } from "../utils/item";
import { randInt } from "../utils/number";
import { isPlayerHoldingItem, getPlayerHoldingItem } from "../utils/inventory.js";

world.beforeEvents.worldInitialize.subscribe(({ blockComponentRegistry }) => {

    function getBlockState(block, typeId) {
        if (!block) return 0;
        if (block.matches(typeId)) return 2;
        return block.isAir || block.isLiquid ? 0 : 1;
    }

    function collectDripstoneColumn(startBlock, direction, blockId) {
        const column = [startBlock];
        const dim = startBlock.dimension;
        const increment = direction === "up" ? 1 : -1;

        for (const offset of [-1, 1]) {
            let searchY = startBlock.location.y + offset * increment;

            while (true) {
                const block = dim.getBlock({ x: startBlock.location.x, y: searchY, z: startBlock.location.z });
                if (!block || block.typeId !== blockId || block.permutation.getState("gs_cb:direction") !== direction) break;
                if (offset === -1) column.push(block);
                else column.unshift(block);
                searchY += offset * increment;
            }
        }

        return column;
    }

    function assignDripstoneThickness(column, direction) {
        const len = column.length;
        for (let i = 0; i < len; i++) {
            let thickness;
            if (len === 1) thickness = 0;
            else if (i === 0) thickness = 0;
            else if (i === 1) thickness = 1;
            else if (i === len - 1) thickness = 3;
            else thickness = 2;

            const perm = column[i].permutation
                .withState("gs_cb:thickness", thickness)
                .withState("gs_cb:direction", direction);
            column[i].setPermutation(perm);
        }
    }

    function updateDripstone(block) {
        const blockId = block.typeId;
        const direction = block.permutation.getState("gs_cb:direction");
        const column = collectDripstoneColumn(block, direction, blockId);
        assignDripstoneThickness(column, direction);
    }

    function breakDripstoneChain(block, direction) {
        const nextBlock = direction === "up" ? block.above() : block.below();
        block.dimension.runCommandAsync(`setblock ${block.location.x} ${block.location.y} ${block.location.z} air destroy`);

        if (nextBlock?.typeId === block.typeId) {
            system.runTimeout(() => breakDripstoneChain(nextBlock, direction), 1);
        }
    }

    blockComponentRegistry.registerCustomComponent("gs_cb:dripstone", {
        onPlace(event) {
            const block = event.block;
            const playersNearby = block.dimension.getEntities({
                location: block.location,
                maxDistance: 8,
                type: "minecraft:player"
            });
            if (playersNearby.length === 0) return;
            try {
                const above = block.above();
                const below = block.below();
                const blockId = block.typeId;

                const aboveState = getBlockState(above, blockId);
                const belowState = getBlockState(below, blockId);

                const direction =
                    belowState === 0 || (belowState === 2 && aboveState === 1)
                        ? "down"
                        : "up";

                block.setPermutation(block.permutation.withState("gs_cb:direction", direction));

                system.runTimeout(() => {
                    const updatedBlock = block.dimension.getBlock(block.location);
                    updateDripstone(updatedBlock);
                }, 1);
            } catch(ignored) {}
        },

        onPlayerDestroy(event) {
            const block = event.block;
            const blockId = event.destroyedBlockPermutation.type.id;
            const direction = event.destroyedBlockPermutation.getState("gs_cb:direction");

            const above = block.above();
            const below = block.below();
            const aboveState = getBlockState(above, blockId);
            const belowState = getBlockState(below, blockId);

            if (belowState === 2) {
                direction === "up" ? updateDripstone(below) : breakDripstoneChain(below, "down");
            }

            if (aboveState === 2) {
                direction === "down" ? updateDripstone(above) : breakDripstoneChain(above, "up");
            }
        }
    });

    blockComponentRegistry.registerCustomComponent("gs_cb:plant_shearable", {
        onPlayerDestroy: ({ player, block, dimension, destroyedBlockPermutation }) => {
            if (isPlayerHoldingItem(player, "minecraft:shears")) {
                dropItemCenterOfBlock(dimension, block.location, new ItemStack(destroyedBlockPermutation.type.id.replace("_top", "").replace("_bottom", ""), 1));
            }
        },
    });
    blockComponentRegistry.registerCustomComponent("gs_cb:double_plant_bottom", {
        onPlace: ({ block, dimension }) => {
            try {
                if (block.above(1).typeId !== "minecraft:air") {
                    const { x, y, z } = block.location;
                    dimension.runCommandAsync(`setblock ${x} ${y} ${z} minecraft:air destroy`);
                    dropItemCenterOfBlock(dimension, block.location, new ItemStack(block.typeId.replace("_bottom", ""), 1));
                    return;
                }
                block.above(1).setType(block.typeId.replace("_bottom", "_top"));
            } catch (ignored) { }
        },
    });
    blockComponentRegistry.registerCustomComponent("gs_cb:double_plant_top", {
        onPlayerDestroy: ({ block, dimension, destroyedBlockPermutation }) => {
            const blockBelow = block.below(1);
            if (destroyedBlockPermutation.type.id.replace("_top", "") === blockBelow.typeId.replace("_bottom", "")) {
                const { x, y, z } = blockBelow.location;
                dimension.runCommandAsync(`setblock ${x} ${y} ${z} minecraft:air destroy`);
            }
        },
    });

    blockComponentRegistry.registerCustomComponent("gs_cb:blue_grass", {
        onPlace: ({ block }) => {
            try {
                let randomValue = Math.floor(Math.random() * 4) + 1;
                block.setPermutation(BlockPermutation.resolve("gs_cb:blue_grass", { "gs_cb:variant": randomValue }))
            } catch(ignored) { }
        },
    });

    blockComponentRegistry.registerCustomComponent("gs_cb:orange_grass", {
        onPlace: ({ block }) => {
            try {
                let randomValue = Math.floor(Math.random() * 4) + 1;
                block.setPermutation(BlockPermutation.resolve("gs_cb:orange_grass", { "gs_cb:variant": randomValue }))
            } catch(ignored) {  }
        },
    });

    blockComponentRegistry.registerCustomComponent("gs_cb:blue_spore_blossom", {
        onTick: ({ block }) => {
            let location = block.location
            let anchorLoc = { x: location.x + 0.5, y: location.y, z: location.z + 0.5 }
            block.dimension.spawnParticle("gs_cb:blue_spore_drip", anchorLoc);
        },
    });

    blockComponentRegistry.registerCustomComponent("gs_cb:dynamic_light", {
        onTick: ({ block }) => {
            block.setType('air')
        },
    });

    blockComponentRegistry.registerCustomComponent("gs_cb:ice_cave_vines_body", {
        onRandomTick: ({ block }) => {
            const blockPermutation = block.permutation;
            const plantAge = blockPermutation.getState("gs_cb:growing_plant_age");
            if (plantAge < 12) {
                block.setPermutation(blockPermutation.withState("gs_cb:growing_plant_age", plantAge + 1));
                return;
            }
            const blockBelow = block.below(1);
            if (blockBelow.typeId === "minecraft:air") {
                if (Math.random() < 0.5) {
                    block.setPermutation(BlockPermutation.resolve("gs_cb:ice_cave_vines", { "gs_cb:has_berries": true }));
                } else {
                    block.setPermutation(BlockPermutation.resolve("gs_cb:ice_cave_vines", { "gs_cb:has_berries": false }));
                }

                if (Math.random() < 0.25) {
                    blockBelow.setPermutation(blockPermutation.withState("gs_cb:has_berries", true).withState("gs_cb:growing_plant_age", 0));
                } else {
                    blockBelow.setPermutation(blockPermutation.withState("gs_cb:growing_plant_age", 0));
                }
            }
        },
    });

    blockComponentRegistry.registerCustomComponent("gs_cb:ice_cave_vines", {
        onPlayerInteract: ({ block, dimension }) => {
            if (block.permutation.getState("gs_cb:has_berries")) {
                block.setPermutation(block.permutation.withState("gs_cb:has_berries", false));
                dropItemCenterOfBlock(dimension, block.location, new ItemStack("gs_cb:ice_glow_berries", randInt(1, 3)));
            }
        },
        onPlayerDestroy: ({ block, dimension }) => {
            const blockAbove = block.above(1);
            if (blockAbove.typeId === "gs_cb:ice_cave_vines") {
                blockAbove.setType("gs_cb:ice_cave_vines_body");
            }
        },
    });

    const HOLD_DURATION = 80;
    const INTERACT_TIMEOUT = 8;
    const TICK_INTERVAL = 2;

    const BLOCK_CONFIG = {
        "gs_cb:suspicious_ice": {
            replacement: "minecraft:ice",
            lootPool: "blocks/suspicious_ice"
        },
        "gs_cb:suspicious_packed_ice": {
            replacement: "minecraft:packed_ice",
            lootPool: "blocks/suspicious_ice"
        },
        "gs_cb:suspicious_magma": {
            replacement: "minecraft:magma",
            lootPool: "blocks/suspicious_magma"
        }
    };

    const BREAK_MODIFIERS = {
        "gs_cb:diamond_chisel": 1.0,
        "gs_cb:netherite_chisel": 1.4
    };

    const blockHoldStates = new Map();

    function getBlockKey(block) {
        return `${block.x},${block.y},${block.z}`;
    }

    function updateVariant(block, countdown) {
        let anchorLoc = { x: block.location.x + 0.5, y: block.location.y, z: block.location.z + 0.5 }
        let newVariant = 0;
        if (countdown <= 20) newVariant = 3;
        else if (countdown <= 40) newVariant = 2;
        else if (countdown <= 60) newVariant = 1;

        const current = block.permutation.getState("gs_cb:variant");
        if (current !== newVariant) {
            block.dimension.spawnParticle("minecraft:critical_hit_emitter", anchorLoc)
            block.dimension.playSound("random.glass", anchorLoc)
            block.setPermutation(BlockPermutation.resolve(block.typeId, { "gs_cb:variant": newVariant }));
        }
    }

    function handleSuspiciousInteract({ block, player }) {
        const held = getPlayerHoldingItem(player);
        if (!held || !["gs_cb:diamond_chisel", "gs_cb:netherite_chisel"].includes(held.typeId)) return;
        if (!(block.typeId in BLOCK_CONFIG)) return;

        const key = getBlockKey(block);
        const now = system.currentTick;

        if (!blockHoldStates.has(key)) {
            blockHoldStates.set(key, {
                playerId: player.id,
                countdown: HOLD_DURATION,
                lastTicked: now,
                toolType: held.typeId,
                timeoutId: null
            });
            startHoldLoop(block, player, player.id, held);
        } else {
            const state = blockHoldStates.get(key);
            if (state.playerId === player.id) {
                state.lastTicked = now;
            }
        }
    }

    function startHoldLoop(block, player, playerId, item) {
        const key = getBlockKey(block);
        const state = blockHoldStates.get(key);
        if (!state) return;

        const breakModifier = BREAK_MODIFIERS[state.toolType] ?? 1.0;

        let anchorLoc = { x: block.location.x + 0.5, y: block.location.y, z: block.location.z + 0.5 }

        state.timeoutId = system.runTimeout(() => {
            const now = system.currentTick;

            const liveBlock = block.dimension.getBlock(block.location);
            if (!liveBlock || !(liveBlock.typeId in BLOCK_CONFIG)) {
                blockHoldStates.delete(key);
                return;
            }

            const current = blockHoldStates.get(key);
            if (!current || current.playerId !== playerId) return;

            if (current.lastTicked + INTERACT_TIMEOUT < now) {
                current.countdown = Math.min(current.countdown + 2, HOLD_DURATION);
            } else {
                current.countdown = Math.max(current.countdown - 2 * breakModifier, 0);
            }

            updateVariant(liveBlock, current.countdown);

            if (current.countdown <= 0) {
                const { replacement, lootPool } = BLOCK_CONFIG[liveBlock.typeId];
                const playerEquippableComp = player.getComponent("equippable");
                if (!playerEquippableComp) return;
                const itemUsedDurabilityComp = item.getComponent("durability");
                if (!itemUsedDurabilityComp) return;
                system.run(function () {
                    itemUsedDurabilityComp.damage += 1;
                    const maxDurability = itemUsedDurabilityComp.maxDurability
                    const currentDamage = itemUsedDurabilityComp.damage
                    if (currentDamage >= maxDurability) {
                        system.run(() => {
                            player.playSound('random.break', { pitch: 1, location: player.location, volume: 1 })
                            playerEquippableComp.setEquipment("Mainhand", new ItemStack('minecraft:air', 1));
                        });
                    }
                    else if (currentDamage < maxDurability) {
                        system.run(() => {
                            playerEquippableComp.setEquipment("Mainhand", item);
                        });
                    }
                })

                block.dimension.playSound("random.glass", anchorLoc)
                block.dimension.runCommand(`setblock ${block.x} ${block.y} ${block.z} ${replacement}`);
                block.dimension.runCommand(`loot spawn ${block.x + 0.5} ${block.y} ${block.z + 0.5} loot "${lootPool}"`);

                blockHoldStates.delete(key);
                return;
            }

            startHoldLoop(block, player, playerId, item);
        }, TICK_INTERVAL);
    }

    for (const typeId of Object.keys(BLOCK_CONFIG)) {
        blockComponentRegistry.registerCustomComponent(typeId, {
            onPlayerInteract: handleSuspiciousInteract
        });
    }
    blockComponentRegistry.registerCustomComponent("gs_cb:magma_damage", {
        onTick: ({ block }) => {
            const anchorLoc = {
                x: block.location.x + 0.5,
                y: block.location.y + 1,
                z: block.location.z + 0.5,
            };

            const entitiesNearby = block.dimension.getEntities({
                location: anchorLoc,
                maxDistance: 0.5,
            });
            if (entitiesNearby.length === 0) return;

            for (const entity of entitiesNearby) {
                entity.setOnFire(1.5)
            }
        }
    });
});
