import { system, world, EntityRidingComponent, EquipmentSlot, BlockPermutation, EntityInventoryComponent, ItemStack, EntityDamageCause } from '@minecraft/server';

import * as utils from "./monsters/bossEntity/misc";
import { startCoroutineForBoss } from "./monsters/bossEntity/CoroutineClass";
import { EventsManager, CustomComponentEventHooks, Equippable, sleep } from "./monsters/index";
import MonsterAndBossGuide from './monsters/utils/openMenu';
import { allEventConfigs } from './monsters/entitySubscriptions/index'
export const entityEventsManager = new EventsManager();
entityEventsManager.subscribe(allEventConfigs);
const customEventHooks = new CustomComponentEventHooks();
customEventHooks.setup();

import { eventHandlers } from './monsters/bossEntity/EventHandlers';

const activeGrapples = new Set(); // Tracks players currently grappling

world.afterEvents.playerSpawn.subscribe((e) => {
    const player = e.player
    if (e.initialSpawn === true && !player.hasTag('monsters_spawned')) {
        player.runCommand(`loot spawn ~~~ loot "lv/mo/menu"`)
        player.addTag('monsters_spawned')
    }
});

world.afterEvents.itemStartUse.subscribe((event) => {
    const { source, itemStack } = event;
    if (!source || !itemStack) return;
    if (itemStack.typeId === "lv_mo:menu") {
        MonsterAndBossGuide.openMenu(source);
    }
    if (itemStack.typeId === "lv_mo:stinger") {
        source.runCommand(`playsound mob.phantom.flap @s ~~~ 1 3`);
        source.runCommand(`playanimation @s animation.lv_mo.player.custom_attack`);
        // Add Logic to shoot the projectile: "lv_mo:stinger.projectile"
        const lookDirection = source.getViewDirection();
        const position = source.location;

        // Calculate the spawn position slightly in front of the player
        const spawnPosition = {
            x: position.x + lookDirection.x * 2,
            y: position.y + lookDirection.y * 2 + 1.5, // Adjust to spawn at player's eye level
            z: position.z + lookDirection.z * 2,
        };

        // Summon the projectile entity
        const summonedProjectile = source.dimension.spawnEntity("lv_mo:stinger.projectile", spawnPosition);

        if (summonedProjectile) {

            // Apply velocity to the projectile
            summonedProjectile.clearVelocity();
            summonedProjectile.applyImpulse({
                x: lookDirection.x * 3, // Adjust speed multiplier as needed
                y: lookDirection.y * 3,
                z: lookDirection.z * 3,
            });
        }
    }
    if (itemStack.typeId === "lv_mo:grappling_hook") {
        const playerId = source.id;

        // Check if the player is already grappling
        if (activeGrapples.has(playerId)) {
            return; // Prevent re-triggering if already grappling
        }

        // Find block in range and handle accordingly
        const blockLocation = findBlockInLookDirection(source, 40);
        if (blockLocation) {
            blockLocation.y += 1; // Adjust the final block position to y + 1
            activeGrapples.add(playerId);

            // Automatically pull the player to the block
            pullPlayerToBlock(source, blockLocation, () => {
                activeGrapples.delete(playerId);
            });
        } else {
            playNoBlockSound(source);
        }
    }
});

function findBlockInLookDirection(player, maxDistance) {
    const start = player.getHeadLocation();
    const direction = player.getViewDirection();
    const dimension = player.dimension;

    for (let i = 1; i <= maxDistance; i++) {
        const testLocation = {
            x: start.x + direction.x * i,
            y: start.y + direction.y * i,
            z: start.z + direction.z * i,
        };

        const block = dimension.getBlock(testLocation);
        if (block && !block.isAir) {
            return block.location; // Return the location of the first non-air block
        }
    }

    return null; // No block found within range
}

function pullPlayerToBlock(player, blockLocation, onComplete) {
    let intervalId;

    player.runCommand(`playsound mob.witch.throw @a ~~~ 0.75 0.3 0.01`);
    player.runCommand(`playsound mob.witch.throw @a ~~~ 1 0.75 0.01`);
    intervalId = system.runInterval(() => {
        const playerLocation = player.location;

        const distance = Math.sqrt(
            Math.pow(blockLocation.x - playerLocation.x, 2) +
            Math.pow(blockLocation.y - playerLocation.y, 2) +
            Math.pow(blockLocation.z - playerLocation.z, 2)
        );

        if (distance < 1) {
            system.clearRun(intervalId);
            onComplete?.();
            return;
        }

        // Spawn particles along the path
        spawnParticles(playerLocation, blockLocation, player.dimension);

        const direction = {
            x: (blockLocation.x - playerLocation.x) / distance,
            y: (blockLocation.y - playerLocation.y) / distance,
            z: (blockLocation.z - playerLocation.z) / distance,
        };

        const stepSize = 1; // Adjust speed
        player.teleport(
            {
                x: playerLocation.x + direction.x * stepSize,
                y: playerLocation.y + direction.y * stepSize,
                z: playerLocation.z + direction.z * stepSize,
            },
            {
                facingLocation: blockLocation,
            }
        );
    }, 1);
}

function spawnParticles(start, end, loc_dimension) {
    const dimension = loc_dimension;
    const distance = Math.sqrt(
        Math.pow(end.x - start.x, 2) +
        Math.pow(end.y - start.y, 2) +
        Math.pow(end.z - start.z, 2)
    );

    const steps = Math.ceil(distance / 0.5); // Divide path into 0.5 block segments
    for (let i = 0; i <= steps; i++) {
        const factor = i / steps;
        const position = {
            x: start.x + (end.x - start.x) * factor,
            y: start.y + (end.y - start.y) * factor,
            z: start.z + (end.z - start.z) * factor,
        };

        dimension.spawnParticle("lv_mo:grappling_trace", position);
    }
}

function playNoBlockSound(player) {
    player.runCommand(`playsound note.bassattack @s ~ ~ ~ 0.6 0.75`);
    player.runCommand(`title @s actionbar §cNo block in range.`)
}


// Store the last tick when each player applied the Void Sword aura effect
const voidSwordCooldowns = new Map();

system.runInterval(() => {

    let players = world.getAllPlayers();
    for (let player of players) {
        // Check for the balloon in mainhand or offhand
        const equipment = player.getComponent("minecraft:equippable");

        if (equipment) {
            const mainHandItem = equipment.getEquipment(EquipmentSlot.Mainhand);
            const offHandItem = equipment.getEquipment(EquipmentSlot.Offhand);
            if (
                (mainHandItem && mainHandItem.typeId === 'lv_mo:balloon') ||
                (offHandItem && offHandItem.typeId === 'lv_mo:balloon')
            ) {
                player.addEffect('jump_boost', 30, { amplifier: 5, showParticles: false });
                player.addEffect('slow_falling', 30, { amplifier: 0, showParticles: false });
            }
        }
        // Void Sword functionality
        if (Equippable.Mainhand(player)?.typeId === 'lv_mo:void_sword') {
            const velocity = player.getVelocity();

            // Check if the player is stationary
            if (Math.abs(velocity.x) < 0.01 && Math.abs(velocity.y) < 0.01 && Math.abs(velocity.z) < 0.01) {
                const currentTick = system.currentTick;

                // Check if the player can apply the aura effect
                const lastAuraTick = voidSwordCooldowns.get(player.id) || 0;
                if (currentTick - lastAuraTick >= 20) { // 20 ticks cooldown
                    voidSwordCooldowns.set(player.id, currentTick);

                    const playerLocation = player.location;
                    const dimension = player.dimension;

                    // Find nearby entities within a 3-block radius
                    const nearbyEntities = dimension.getEntities({
                        location: playerLocation,
                        maxDistance: 3,
                    });

                    // Damage only entities with the "monster" family type
                    for (let entity of nearbyEntities) {
                        try {
                            const familyComponent = entity.getComponent('minecraft:type_family');
                            if (familyComponent && familyComponent.getTypeFamilies().includes('monster')) {
                                entity.applyDamage(1);
                            }
                        } catch (error) {
                        }
                    }
                }
            }
        }

        //neptune_vest
        if (Equippable.Chest(player)?.typeId === 'lv_mo:neptune_vest') {
            player.addEffect('water_breathing', 30, { amplifier: 1, showParticles: false });
        }
        //crab_hat
        if (Equippable.Head(player)?.typeId === 'lv_mo:crab_hat') {
            player.addEffect('fire_resistance', 30, { amplifier: 1, showParticles: false });
        }
        //crab_hat
        if (Equippable.Head(player)?.typeId === 'lv_mo:basilisk_head') {
            player.removeEffect("poison");
        }
        //plasma_boots
        if (Equippable.Feet(player)?.typeId === 'lv_mo:plasma_boots') {
            const timeOfDay = world.getTimeOfDay();
            if (timeOfDay >= 13000 && timeOfDay < 23000) {
                player.addEffect('speed', 30, { amplifier: 0, showParticles: false });
                player.addEffect('invisibility', 30, { amplifier: 0, showParticles: false });
                player.addEffect('regeneration', 30, { amplifier: 0, showParticles: false });
            }
        }

        world.getPlayers().forEach((player) => {
            // Lava Loafers
            if (Equippable.Feet(player)?.typeId === "lv_mo:lava_loafers") {
                const playerLocation = player.location;
                const dimension = player.dimension;

                const changeBlockTemporarily = (block) => {
                    if (
                        block?.type.id === "minecraft:lava" ||
                        block?.type.id === "minecraft:flowing_lava" ||
                        block?.type.id === "lv_mo:cracked_magma_stone"
                    ) {
                        try {
                            // Change block to magma stone
                            const magmaStonePermutation = BlockPermutation.resolve("lv_mo:magma_stone");
                            block.setPermutation(magmaStonePermutation);

                            // Change to cracked magma stone after 2 seconds
                            system.runTimeout(() => {
                                try {
                                    const crackedMagmaStonePermutation = BlockPermutation.resolve("lv_mo:cracked_magma_stone");
                                    block.setPermutation(crackedMagmaStonePermutation);

                                    // Restore to lava after an additional 2 seconds, but only if the block is still cracked magma stone
                                    system.runTimeout(() => {
                                        try {
                                            const currentBlock = dimension.getBlock(block.location);
                                            if (currentBlock?.type.id === "lv_mo:cracked_magma_stone") {
                                                const lavaPermutation = BlockPermutation.resolve("minecraft:lava");
                                                currentBlock.setPermutation(lavaPermutation);
                                            }
                                        } catch (error) {
                                        }
                                    }, 40); // 40 ticks = 2 seconds
                                } catch (error) {
                                }
                            }, 40); // 40 ticks = 2 seconds
                        } catch (error) {
                        }
                    }
                };

                // Replace a 3x3 area of blocks below the player, covering two layers (-1 and -2 on the y-axis)
                for (let dx = -1; dx <= 1; dx++) {
                    for (let dz = -1; dz <= 1; dz++) {
                        for (let dy = -1; dy >= -2; dy--) { // Add this loop to cover two layers
                            const block = dimension.getBlock({
                                x: Math.floor(playerLocation.x) + dx,
                                y: Math.floor(playerLocation.y) + dy,
                                z: Math.floor(playerLocation.z) + dz,
                            });
                            changeBlockTemporarily(block);
                        }
                    }
                }
            }
        });







        // Snout
        const rider = player.getComponent(EntityRidingComponent.componentId);
        if (!rider) return;

        //Gripstone entity will throw an error here since the entityRidingOn property will be null
        try {
            
        if (rider.entityRidingOn.typeId === 'lv_mo:snout') {
            const { x, y, z } = rider.entityRidingOn.location;

            // Apply slowness effect based on whether the player is jumping
            if (player.isJumping) {
                rider.entityRidingOn.addEffect("slowness", 10, { amplifier: 4, showParticles: false });

                // Convert only the layer directly below the snout to farmland if they are grass or dirt
                for (let dx = -1; dx <= 1; dx++) {
                    for (let dz = -1; dz <= 1; dz++) {
                        const block = player.dimension.getBlock({
                            x: x + dx,
                            y: y - 1,  // Only the layer directly beneath the entity
                            z: z + dz
                        });
                        if (block.permutation.matches('minecraft:grass_block') || block.permutation.matches('minecraft:dirt')) {
                            block.setPermutation(BlockPermutation.resolve('minecraft:farmland'));
                            world.playSound("dig.grass", block.location, { volume: 0.2 });
                        }
                    }
                }
            } else {
                rider.entityRidingOn.addEffect("slowness", 10, { amplifier: 2, showParticles: false });
            }
        }
        else if (rider.entityRidingOn.typeId === "lv_mo:sculkosaurus_pet") {
            if (player.isJumping) {
                let ridingEntity = rider.entityRidingOn;
                let RANGE_ATTACK_DURATION = 1.875 * 20;
                let RANGE_ATTACK_TIME = 0.67 * 20;
                let RANGE_ATTACK_DAMAGE = Math.floor(Math.random() * (18 - 16 + 1)) + 16;
                function runCoroutine(generator) {
                    const iterator = generator();

                    function step(result) {
                        if (result.done) return;

                        const delay = result.value || 1; // default to 1 tick if nothing yielded
                        system.runTimeout(() => {
                            step(iterator.next());
                        }, delay);
                    }

                    step(iterator.next());
                }

                const rangeAttack = function () {
                    const active = function* () {
                        if (!ridingEntity.isValid() || ridingEntity.getProperty('lv_mo:special') !== 'ready') return;

                        ridingEntity.setProperty('lv_mo:special', 'cast');
                        ridingEntity.addEffect("slowness", 30, { amplifier: 5, showParticles: false });
                        yield RANGE_ATTACK_TIME;
                        if (!ridingEntity.isValid()) return;

                        ridingEntity.setProperty('lv_mo:special', 'cooldown');

                        let victims = utils.getRadiusEntity(
                            ridingEntity,
                            utils.getPosForward(ridingEntity, 2),
                            14,
                            ['mob']
                        );

                        for (let victim of victims) {
                            if (victim.id === ridingEntity.id) continue;
                            if (!ridingEntity.isValid()) return;

                            utils.effect(victim, 'nausea', 8, 3);
                            utils.setPosKnockback(victim, ridingEntity.location, [5, 0.8]);

                            victim.applyDamage(RANGE_ATTACK_DAMAGE, {
                                cause: EntityDamageCause.entityAttack,
                                damagingEntity: ridingEntity,
                            });
                        }

                        yield (RANGE_ATTACK_DURATION - RANGE_ATTACK_TIME);

                        ridingEntity.setProperty('lv_mo:special', 'ready');
                    };
                    runCoroutine(active);
                };


                // Call the function
                rangeAttack();

            }
        }

        } catch (error) {
            
        }
    }



})


//
const dimensions = [
    world.getDimension("overworld"),
    world.getDimension("nether"),
    world.getDimension("the_end")
].filter(Boolean);

system.runInterval(() => {
    for (const dimension of dimensions) {
        for (const entity of dimension.getEntities()) {
            if (!entity) continue;
            if (entity.typeId === 'lv_mo:torch_golem') {

                const players = dimension.getEntities({
                    location: entity.location,
                    maxDistance: 24,
                    type: "minecraft:player"
                });

                if (players.length > 0) {

                    const roundedLocation = {
                        x: Math.floor(entity.location.x),
                        y: Math.floor(entity.location.y),
                        z: Math.floor(entity.location.z)
                    };

                    const block = entity.dimension.getBlock(roundedLocation);
                    if (block && block.isAir) {
                        const torch = entity.getProperty('lv_mo:torch');
                        switch (torch) {
                            case 0:
                                block.setPermutation(BlockPermutation.resolve('lv_mo:custom_light_4'));
                                break;
                            case 1:
                                block.setPermutation(BlockPermutation.resolve('lv_mo:custom_light_8'));
                                break;
                            case 2:
                                block.setPermutation(BlockPermutation.resolve('lv_mo:custom_light_7'));
                                break;
                            case 3:
                                block.setPermutation(BlockPermutation.resolve('lv_mo:custom_light_14'));
                                break;
                        }
                    }
                }
            }
        }
    }
});
system.runInterval(() => {
    for (const dimension of dimensions) {
        for (const entity of dimension.getEntities()) {
            try {
                if (!entity || entity.typeId !== 'lv_mo:gripstone') continue;

                const entityLocation = entity.location;
                const entityDimension = entity.dimension;

                for (let i = 1; i <= 10; i++) {
                    const blockPos = {
                        x: entityLocation.x,
                        y: entityLocation.y - i,
                        z: entityLocation.z,
                    };

                    if (blockPos.y < -64 || blockPos.y > 320) break;

                    const blockBelow = entityDimension.getBlock(blockPos);
                    if (!blockBelow.isAir) break;

                    // Check if players are nearby
                    const nearbyPlayers = entityDimension.getEntities({
                        location: entityLocation,
                        maxDistance: 0.8,
                        type: "minecraft:player"
                    });

                    if (nearbyPlayers.length > 0) {
                        const player = nearbyPlayers[0]; // Assuming one player per gripstone

                        // Check if a dummy entity already exists near the gripstone
                        const existingDummyEntities = entityDimension.getEntities({
                            location: entityLocation,
                            maxDistance: 0.5,
                            type: "lv_mo:gripstone_dummy"
                        });

                        if (existingDummyEntities.length === 0) {
                            // Spawn the dummy entity
                            const dummyEntity = entityDimension.spawnEntity("lv_mo:gripstone_dummy", {
                                x: entityLocation.x,
                                y: entityLocation.y,
                                z: entityLocation.z,
                            });

                            // Let the player ride the dummy entity and play the animation
                            const rideComponent = dummyEntity.getComponent('minecraft:rideable');
                            if (rideComponent) {
                                rideComponent.addRider(player);
                                try {
                                    // Start and loop the hang animation if the player is riding the dummy entity
                                    system.runInterval(() => {
                                        try {
                                            
                                        if (player.getComponent('minecraft:riding')?.entityRidingOn?.typeId === "lv_mo:gripstone_dummy") {
                                            player.runCommand(`playanimation ${player.nameTag} animation.lv_mo.player.gripstone_hang`);
                                        }
                                        } catch (error) {
                                            //
                                        }
                                    }, 5); // Repeat every 5 ticks
                                } catch (error) {
                                    //Suppress error
                                }
                            }
                        } else {
                            // Dummy entity already exists; ensure the player is riding it
                            const dummyEntity = existingDummyEntities[0];
                            const rideComponent = dummyEntity.getComponent('minecraft:rideable');
                            if (rideComponent) {
                                if (!player.getComponent('minecraft:riding')) {
                                    rideComponent.addRider(player);

                                    // Only play the animation if the player is riding the dummy entity
                                    if (player.getComponent('minecraft:riding')?.entityRidingOn?.typeId === "lv_mo:gripstone_dummy") {
                                        player.runCommand(`playanimation ${player.nameTag} animation.lv_mo.player.gripstone_hang`);
                                    }
                                }
                            }
                        }
                    } else {
                        // No players nearby; remove any existing dummy entities and stop the animation
                        const existingDummyEntities = entityDimension.getEntities({
                            location: entityLocation,
                            maxDistance: 0.5,
                            type: "lv_mo:gripstone_dummy"
                        });

                        existingDummyEntities.forEach((dummyEntity) => {
                            dummyEntity.triggerEvent('minecraft:despawn');
                        });
                    }

                    // Teleport players directly if there's a block below
                    const playersOnBlock = blockBelow.dimension.getEntities({
                        location: blockBelow.location,
                        maxDistance: 1,
                        type: "minecraft:player"
                    });

                    playersOnBlock.forEach((player) => {
                        const riding = player.getComponent('minecraft:riding');
                        if (!riding) {
                            player.teleport({
                                x: blockBelow.location.x,
                                y: blockBelow.location.y + 1,
                                z: blockBelow.location.z
                            });
                        }
                    });

                    if (playersOnBlock.length > 0) break;
                }
            } catch (error) { }
        }
    }
});



//
export let playerCooldowns = new Map();

system.runInterval(() => {
    const cooldownDuration = 8;
    let players = world.getAllPlayers();
    try {
        for (let player of players) {
            const playerContainer = player.getComponent("inventory").container;

            for (let i = 0; i < playerContainer.size; i++) {
                const item = playerContainer.getItem(i);
                if (!item) continue;
                if (item.typeId === "lv_mo:empty_infinite_water_bucket") {
                    let uniqueId = item.getDynamicProperty("cooldownUniqueId");

                    if (!uniqueId) {
                        uniqueId = `bucket_${Date.now()}_${i}`;
                        item.setDynamicProperty("cooldownUniqueId", uniqueId);
                        item.setDynamicProperty("cooldownCountdown", cooldownDuration);
                        item.setLore([`Cooldown: ${cooldownDuration}s`]);
                        playerContainer.setItem(i, item);
                    }
                    let countdown = item.getDynamicProperty("cooldownCountdown") ?? cooldownDuration;

                    if (countdown <= 1) {
                        const newItem = new ItemStack("lv_mo:infinite_water_bucket", 1);
                        playerContainer.setItem(i, newItem);
                        playerCooldowns.delete(uniqueId);
                    } else {
                        countdown -= 1;
                        item.setDynamicProperty("cooldownCountdown", countdown);
                        item.setLore([`Cooldown: ${countdown}s`]);
                        playerContainer.setItem(i, item);
                    }
                } else if (item.getDynamicProperty("cooldownUniqueId")) {
                    playerCooldowns.delete(item.getDynamicProperty("cooldownUniqueId"));
                    item.removeDynamicProperty("cooldownUniqueId");
                    item.removeDynamicProperty("cooldownCountdown");
                }
            }
        }
    } catch (error) { }
}, 20); 
