import { EntityDamageCause, ItemStack, system, world } from "@minecraft/server";
import { namespace } from "../../../config/_config";
export class GravityBlock {
    constructor(config) {
        this.id = config.id;
        this.entity = config.entity;
        this.states = config.states;
        this.onLand = config.onLand;
        this.trigger = config.trigger;
    }
    static register(block) {
        GravityBlock.blocks.push(block);
    }
    static registerBehavior(config) {
        const block = new GravityBlock(config);
        this.register(block);
    }
    static initialize() {
        // Handle block break event
        world.afterEvents.playerBreakBlock.subscribe((event) => {
            const destroyedBlock = event.block;
            const dimension = event.player.dimension;
            GravityBlock.blocks.forEach((registeredBlock) => {
                GravityBlock.handleBlockDestruction(registeredBlock, destroyedBlock, dimension);
            });
        });
        // Handle projectile hit event
        world.afterEvents.projectileHitBlock.subscribe((event) => {
            const hitBlock = event.getBlockHit().block;
            const dimension = event.dimension;
            GravityBlock.blocks.forEach((block) => {
                this.handleBlockHit(block, hitBlock, dimension);
            });
        });
    }
    static isMatchingBlock(registeredBlock, targetBlock) {
        if (targetBlock.typeId !== registeredBlock.id) {
            return false;
        }
        // Check block states if defined
        if (registeredBlock.states) {
            const blockStates = targetBlock.permutation.getAllStates();
            for (const blockState of registeredBlock.states) {
                if (blockStates[blockState.state] !== blockState.value) {
                    return false;
                }
            }
        }
        return true;
    }
    static handleBlockDestruction(registeredBlock, destroyedBlock, dimension) {
        this.handleAdjacentBlocks(registeredBlock, destroyedBlock, dimension);
        const surroundingBlocks = this.getSurroundingBlocks(destroyedBlock, dimension);
        let entitySpawned = false;
        surroundingBlocks.forEach(({ block: adjacentBlock, face }) => {
            if (!entitySpawned && this.isMatchingBlock(registeredBlock, adjacentBlock)) {
                if (this.shouldTriggerFall(registeredBlock, destroyedBlock, face)) {
                    const location = this.getEntitySpawnLocation(adjacentBlock);
                    system.runTimeout(() => {
                        const entity = dimension.spawnEntity(registeredBlock.entity.id, location);
                        // Trigger all provided events on the entity
                        registeredBlock.entity.events?.forEach((event) => {
                            entity.triggerEvent(event);
                        });
                        adjacentBlock.setType("minecraft:air");
                        this.monitorEntity(registeredBlock, entity);
                        entitySpawned = true;
                    }, 1);
                }
            }
        });
    }
    static handleBlockHit(registeredBlock, hitBlock, dimension) {
        // First, handle any blocks below the hit block
        this.handleAdjacentHitBlocks(registeredBlock, hitBlock, dimension);
        // Then, make the hit block itself fall
        if (this.isMatchingBlock(registeredBlock, hitBlock)) {
            const location = this.getEntitySpawnLocation(hitBlock);
            system.runTimeout(() => {
                const entity = dimension.spawnEntity(registeredBlock.entity.id, location);
                // Trigger all provided events on the entity
                registeredBlock.entity.events?.forEach((event) => {
                    entity.triggerEvent(event);
                });
                hitBlock.setType("minecraft:air");
                this.monitorEntity(registeredBlock, entity);
            }, 1);
        }
    }
    static triggerBlockFall(registeredBlock, block, dimension) {
        const location = this.getEntitySpawnLocation(block);
        const entity = dimension.spawnEntity(registeredBlock.entity.id, location);
        // Trigger all provided events on the entity
        registeredBlock.entity.events?.forEach((event) => {
            entity.triggerEvent(event);
        });
        // Set the block to air after spawning the entity
        block.setType("minecraft:air");
        // Monitor the entity after it falls
        this.monitorEntity(registeredBlock, entity);
    }
    static handleAdjacentBlocks(registeredBlock, block, dimension) {
        if (!block.below())
            return;
        if (block.below()?.typeId === registeredBlock.id) {
            this.handleBlockDestruction(registeredBlock, block.below(), dimension);
        }
    }
    static handleAdjacentHitBlocks(registeredBlock, block, dimension) {
        // Check if there's a block below and handle it first
        const blockBelow = block.below();
        if (blockBelow && blockBelow.typeId === registeredBlock.id) {
            this.handleBlockHit(registeredBlock, blockBelow, dimension); // Handle the block below recursively
        }
    }
    static getSurroundingBlocks(block, dimension) {
        const positions = [
            { offset: { x: 0, y: 1, z: 0 }, face: "top" },
            { offset: { x: 0, y: -1, z: 0 }, face: "bottom" },
            { offset: { x: 1, y: 0, z: 0 }, face: "east" },
            { offset: { x: -1, y: 0, z: 0 }, face: "west" },
            { offset: { x: 0, y: 0, z: 1 }, face: "south" },
            { offset: { x: 0, y: 0, z: -1 }, face: "north" },
        ];
        return positions.map(({ offset, face }) => ({
            block: dimension.getBlock({
                x: block.location.x + offset.x,
                y: block.location.y + offset.y,
                z: block.location.z + offset.z,
            }),
            face,
        }));
    }
    static shouldTriggerFall(registeredBlock, destroyedBlock, face) {
        if (registeredBlock.trigger?.stickToBlocks) {
            if (registeredBlock.trigger.stickToBlocks !== "all" &&
                !registeredBlock.trigger.stickToBlocks.includes(destroyedBlock.typeId)) {
                return false;
            }
        }
        if (registeredBlock.trigger?.stickToFace) {
            if (registeredBlock.trigger.stickToFace === "all") {
                return true;
            }
            if (registeredBlock.trigger.stickToFace === "sides") {
                if (["north", "south", "east", "west"].includes(face)) {
                    return true;
                }
                else {
                    return false;
                }
            }
            else if (registeredBlock.trigger.stickToFace !== face) {
                return false;
            }
        }
        return true;
    }
    static monitorEntity(block, entity) {
        const intervalId = system.runInterval(() => {
            if (!entity.isValid()) {
                system.clearRun(intervalId);
                return;
            }
            if (entity.isValid() && !entity.isFalling) {
                system.runTimeout(() => {
                    if (entity.isValid()) {
                        GravityBlock.handleOnLandActions(block, entity);
                        entity.triggerEvent(`${namespace}:despawn`);
                    }
                }, 5);
            }
        }, 1);
    }
    static handleOnLandActions(block, entity) {
        const { transform, damage, particles, sound } = block.onLand || {};
        // Handle block transformation
        if (transform) {
            const targetLocation = entity.location;
            const targetBlock = entity.dimension.getBlock(targetLocation);
            if (transform.block) {
                // If there's a block transform, set it at the current location
                if (targetBlock.typeId === "minecraft:air") {
                    targetBlock.setType(transform.block);
                }
                else {
                    const aboveTargetLocation = {
                        x: targetLocation.x,
                        y: targetLocation.y + 1,
                        z: targetLocation.z,
                    };
                    const aboveTargetBlock = entity.dimension.getBlock(aboveTargetLocation);
                    if (aboveTargetBlock.typeId === "minecraft:air") {
                        aboveTargetBlock.setType(transform.block);
                    }
                }
            }
            // Handle item transformation
            if (transform.item) {
                // Spawn the item at the entity's current location
                const itemLocation = {
                    x: targetLocation.x,
                    y: targetLocation.y,
                    z: targetLocation.z,
                };
                entity.dimension.spawnItem(new ItemStack(transform.item), itemLocation);
            }
        }
        // Apply damage to nearby entities if specified
        if (damage) {
            entity.dimension
                .getEntities({
                maxDistance: 3,
                location: entity.location,
            })
                .forEach((nearbyEntity) => {
                nearbyEntity.applyDamage(damage, { cause: EntityDamageCause.fall });
            });
        }
        // Spawn particles if specified
        if (particles) {
            particles.forEach((particle) => {
                entity.dimension.spawnParticle(particle, entity.location);
            });
        }
        // Play sound if specified
        if (sound) {
            entity.dimension.playSound(sound, entity.location);
        }
    }
    static getEntitySpawnLocation(block) {
        return {
            x: block.location.x + 0.5,
            y: block.location.y,
            z: block.location.z + 0.5,
        };
    }
}
GravityBlock.blocks = [];
