import { GameMode, ItemStack, world, EquipmentSlot, system, } from "@minecraft/server";
import { summonEntitiesInRange, } from "../../helper/summonFromLocation";
import { namespace } from "../../../config/_config";
import { getItemInSlot, removeItemFromInventory } from "../../helper/utils";
const PLAYER_CHECK_INTERVAL = 20; // Check every 200 ticks (~10 seconds)
const MOB_DISTANCE_LIMIT = 50; // Max distance allowed for mobs from the ritual block
const MAX_DISTANCE_LIMIT = 30;
const DELAY_BEFORE_FIRST_WAVE = 100;
const DELAY_BEFORE_MOB_SPAWN = 40;
export class RitualBlock {
    constructor(config) {
        this.id = config.id;
        this.repeat = config.repeat;
        this.triggers = config.triggers;
    }
    static register(block) {
        RitualBlock.blocks.push(block);
    }
    static registerBehavior(config) {
        const block = new RitualBlock(config);
        this.register(block);
    }
    static initialize() {
        world.beforeEvents.worldInitialize.subscribe((event) => {
            event.blockComponentRegistry.registerCustomComponent(`${namespace}:ritual`, {
                onPlayerInteract: (event) => {
                    this.startRitual(event);
                },
                onPlayerDestroy: (event) => {
                    this.handleBlockDestroyed(event);
                },
            });
        });
        // Periodically check player proximity and entity distances
        system.runInterval(() => {
            RitualBlock.checkPlayerProximity();
        }, PLAYER_CHECK_INTERVAL);
    }
    // Start the ritual when a player interacts with the block
    static startRitual(event) {
        const targetBlock = event.block;
        const player = event.player;
        const heldItem = getItemInSlot(player, EquipmentSlot.Mainhand);
        if (!targetBlock || !player || !heldItem)
            return;
        const blockKey = this.generateBlockKey(targetBlock.location);
        this.activeRituals[blockKey] = true;
        RitualBlock.playerProximity[blockKey] = system.currentTick; // Mark ritual as active
        // The rest of the startRitual logic...
        const ritualState = targetBlock.permutation.getState(`${namespace}:ritual_state`);
        if (ritualState === "active" || ritualState === "used") {
            player.sendMessage("The ritual is already active or used and cannot be started again.");
            return;
        }
        const blockData = RitualBlock.getBlockByIdentifier(targetBlock.typeId);
        if (!blockData)
            return;
        const trigger = blockData.triggers.find((t) => t.itemId === heldItem.typeId);
        if (!trigger)
            return;
        if (player.getGameMode() !== GameMode.creative) {
            removeItemFromInventory(player, heldItem.typeId);
        }
        targetBlock.setPermutation(targetBlock.permutation.withState(`${namespace}:ritual_state`, "active"));
        // Call the onStart function if it exists
        if (trigger.onStart) {
            trigger.onStart(targetBlock); // Invoke the onStart callback
        }
        this.playEffects(targetBlock.dimension, targetBlock.location, trigger.startEffects);
        // Start waves after the delay
        system.runTimeout(() => {
            this.startWaves(blockKey, targetBlock.dimension, targetBlock.location, trigger.waves, 0, // Start from the first wave
            trigger.awardItemId, blockData.repeat, trigger.awardEffects);
        }, DELAY_BEFORE_FIRST_WAVE);
    }
    static async startWaves(blockKey, dimension, location, waves, currentWaveIndex, awardItemId, repeat, awardEffects) {
        if (currentWaveIndex >= waves.length || !this.activeRituals[blockKey]) {
            this.finishRitual(blockKey, dimension, location, awardItemId, repeat, awardEffects);
            return;
        }
        const currentWave = waves[currentWaveIndex];
        // Play sound and spawn particles for the current wave (if provided)
        this.playEffects(dimension, location, currentWave);
        // Introduce a delay before mob spawning after the wave effects
        system.runTimeout(() => {
            const summonedEntities = [];
            // Summon entities for the current wave
            for (const entityOptions of currentWave.entities) {
                const entities = summonEntitiesInRange(location, dimension, [entityOptions]);
                summonedEntities.push(...entities);
            }
            this.trackEntitiesInWorld(blockKey, summonedEntities);
            // Wait for all entities of the current wave to be killed
            this.waitForEntitiesToBeKilled(blockKey).then(() => {
                // Check if the block still exists before moving to the next wave
                const block = dimension.getBlock(location);
                if (!block || block.typeId === "minecraft:air") {
                    this.clearBlockTracking(blockKey); // Block is destroyed, terminate the ritual
                    return;
                }
                // Move to the next wave or end the ritual
                if (this.activeRituals[blockKey]) {
                    this.startWaves(blockKey, dimension, location, waves, currentWaveIndex + 1, awardItemId, repeat, awardEffects);
                }
                else {
                    // Ritual is interrupted, do not spawn the award item
                    this.clearBlockTracking(blockKey);
                }
            });
        }, DELAY_BEFORE_MOB_SPAWN); // Delay before spawning the mobs
    }
    static checkPlayerProximity() {
        for (const blockKey in RitualBlock.playerProximity) {
            const lastActiveTick = RitualBlock.playerProximity[blockKey];
            const blockLocation = this.getBlockLocationFromKey(blockKey);
            // Check for nearby players within 20 blocks
            const nearbyPlayers = world
                .getPlayers()
                .filter((player) => this.isPlayerNear(player, blockLocation, MAX_DISTANCE_LIMIT));
            if (nearbyPlayers.length === 0) {
                // No players nearby, reset the ritual
                this.resetRitual(blockKey);
                continue;
            }
            // Reset proximity timer if players are nearby
            RitualBlock.playerProximity[blockKey] = system.currentTick;
            // Check mob proximity
            this.checkMobProximity(blockKey, blockLocation);
        }
    }
    static checkMobProximity(blockKey, blockLocation) {
        const entityIdsString = world.getDynamicProperty(`${namespace}:ritual_${blockKey}`);
        if (!entityIdsString)
            return;
        const entityIds = entityIdsString.split(",").filter(Boolean);
        const aliveEntityIds = entityIds.filter((id) => {
            const entity = world.getEntity(id);
            if (entity && entity.isValid()) {
                const distance = this.getDistance(blockLocation, entity.location);
                return distance <= MOB_DISTANCE_LIMIT; // Keep only those within range
            }
            return false;
        });
        // Update dynamic properties to reflect alive entities
        world.setDynamicProperty(`${namespace}:ritual_${blockKey}`, aliveEntityIds.join(","));
    }
    static getDistance(loc1, loc2) {
        return Math.sqrt((loc1.x - loc2.x) ** 2 + (loc1.y - loc2.y) ** 2 + (loc1.z - loc2.z) ** 2);
    }
    static isPlayerNear(player, blockLocation, maxDistance) {
        const distance = this.getDistance(blockLocation, player.location);
        return distance <= maxDistance;
    }
    static resetRitual(blockKey) {
        const blockLocation = this.getBlockLocationFromKey(blockKey);
        const dimension = world.getDimension("overworld"); // Adjust if necessary
        // Ensure we are dealing with the correct ritual block by checking its type ID
        const block = dimension.getBlock(blockLocation);
        const ritualBlock = this.getBlockByIdentifier(block?.typeId || "");
        if (ritualBlock && block) {
            // The block matches the ritual block ID, so we proceed with the reset
            this.activeRituals[blockKey] = false; // Stop the ritual process
            this.clearBlockTracking(blockKey);
            delete RitualBlock.playerProximity[blockKey];
            // Reset the block state
            block.setPermutation(block.permutation
                .withState(`${namespace}:ritual_state`, "inactive")
                .withState(`${namespace}:ritual_type`, "none"));
            // Kill any remaining entities
            this.killRemainingEntities(blockKey);
        }
    }
    static killRemainingEntities(blockKey) {
        const entityIdsString = world.getDynamicProperty(`${namespace}:ritual_${blockKey}`);
        if (entityIdsString) {
            const entityIds = entityIdsString.split(",");
            entityIds.forEach((id) => {
                const entity = world.getEntity(id);
                if (entity) {
                    entity.kill();
                }
            });
        }
        world.setDynamicProperty(`${namespace}:ritual_${blockKey}`, "");
    }
    static finishRitual(blockKey, dimension, location, awardItemId, repeat, awardEffects) {
        if (this.activeRituals[blockKey]) {
            const block = dimension.getBlock(location);
            const blockData = this.getBlockByIdentifier(block?.typeId || "");
            // Only spawn the award item if the ritual completed all waves successfully
            this.spawnAwardItem(dimension, location, awardItemId);
            this.playEffects(dimension, location, awardEffects);
            // Call the onFinish function if it exists
            if (blockData && block && blockData.triggers) {
                const trigger = blockData.triggers.find((t) => t.awardItemId === awardItemId);
                if (trigger && trigger.onFinish) {
                    trigger.onFinish(block); // Invoke the onFinish callback
                }
            }
            if (repeat) {
                this.resetBlock(dimension, location);
            }
            else {
                this.markBlockAsUsed(dimension, location);
            }
            this.clearBlockTracking(blockKey);
            delete this.activeRituals[blockKey];
        }
    }
    // Helper function to check if the ritual was interrupted
    static isRitualInterrupted(blockKey) {
        return !this.activeRituals[blockKey];
    }
    // Handle block destruction, reset on destroy
    static handleBlockDestroyed(event) {
        const blockKey = this.generateBlockKey(event.block.location);
        this.resetRitual(blockKey);
    }
    // Play sound and spawn particles at a location (with optional checks)
    static playEffects(dimension, location, effects) {
        if (!effects)
            return;
        if (effects.sound) {
            dimension.playSound(effects.sound, {
                x: location.x + 0.5,
                y: location.y + 1,
                z: location.z + 0.5,
            });
        }
        if (effects.particles) {
            for (const particle of effects.particles) {
                dimension.spawnParticle(particle, {
                    x: location.x + 0.5,
                    y: location.y + 1,
                    z: location.z + 0.5,
                });
            }
        }
    }
    // Track summoned entities in the world’s dynamic properties using a comma-separated string
    static trackEntitiesInWorld(blockKey, entities) {
        const entityIds = entities.map((entity) => entity.id);
        const entityIdsString = entityIds.join(",");
        world.setDynamicProperty(`${namespace}:ritual_${blockKey}`, entityIdsString);
    }
    // Wait for all entities of the current wave to be killed
    static async waitForEntitiesToBeKilled(blockKey) {
        return new Promise((resolve) => {
            const checkEntities = () => {
                if (!this.activeRituals[blockKey]) {
                    resolve();
                    return;
                }
                const entityIdsString = world.getDynamicProperty(`${namespace}:ritual_${blockKey}`);
                if (!entityIdsString) {
                    resolve();
                    return;
                }
                const entityIds = entityIdsString.split(",").filter(Boolean);
                const aliveEntityIds = entityIds.filter((id) => {
                    const entity = world.getEntity(id);
                    return entity && entity.isValid;
                });
                world.setDynamicProperty(`${namespace}:ritual_${blockKey}`, aliveEntityIds.join(","));
                if (aliveEntityIds.length === 0) {
                    resolve();
                }
                else {
                    system.runTimeout(checkEntities, 20);
                }
            };
            checkEntities();
        });
    }
    // Spawn the award item after the last wave is completed
    static spawnAwardItem(dimension, location, awardItemId) {
        const awardItem = new ItemStack(awardItemId);
        dimension.spawnItem(awardItem, {
            x: location.x + 0.5,
            y: location.y + 1.5,
            z: location.z + 0.5,
        });
    }
    static getBlockLocationFromKey(blockKey) {
        const [x, y, z] = blockKey.split("_").map(Number);
        return { x, y, z };
    }
    static generateBlockKey(location) {
        return `${location.x}_${location.y}_${location.z}`;
    }
    static clearBlockTracking(blockKey) {
        const entityIdsString = world.getDynamicProperty(`${namespace}:ritual_${blockKey}`);
        if (entityIdsString) {
            const entityIds = entityIdsString.split(",");
            entityIds.forEach((id) => {
                const entity = world.getEntity(id);
                if (entity) {
                    entity.kill();
                }
            });
        }
        world.setDynamicProperty(`${namespace}:ritual_${blockKey}`, "");
    }
    static resetBlock(dimension, location) {
        const block = dimension.getBlock(location);
        if (block) {
            // Assuming you want to reset both the ritual state and any other custom states
            block.setPermutation(block.permutation
                .withState(`${namespace}:ritual_state`, "inactive") // Reset to "inactive"
                .withState(`${namespace}:ritual_type`, "none") // Reset any other custom state (if applicable)
            );
        }
    }
    // Mark the block as used (non-repeatable) if the ritual is not repeatable
    static markBlockAsUsed(dimension, location) {
        const block = dimension.getBlock(location);
        if (block) {
            // Only change the ritual_state to "used"
            block.setPermutation(block.permutation.withState(`${namespace}:ritual_state`, "used") // Mark the block as "used"
            );
        }
    }
    static getBlockByIdentifier(identifier) {
        return RitualBlock.blocks.find((block) => block.id === identifier);
    }
}
RitualBlock.blocks = [];
RitualBlock.playerProximity = {}; // Track players' last proximity to ritual
RitualBlock.activeRituals = {};
