import { EquipmentSlot, ItemStack, system, world } from "@minecraft/server";

const ZOMBIE_IDS = [
    "lv_zo:zomb",
    "lv_zo:chubby",
    "lv_zo:small_zomb",
    "lv_zo:tanky",
    "lv_zo:slapstick",
    "lv_zo:slime_shooter",
    "lv_zo:zombomb",
    "lv_zo:mini",
];

world.afterEvents.entitySpawn.subscribe((e) => {
    const entity = e.entity;

    try {
        if (!ZOMBIE_IDS.includes(entity.typeId)) return;

        const objective = world.scoreboard.getObjective("lv_zo_global_toggles");

        const participant = objective?.getParticipants().find((p) =>
            p.displayName === "slider_zombie_difficulty"
        );

        const difficulty = participant ? objective.getScore(participant) : 0;

        if (difficulty === 1) {
            entity.addEffect("strength", 20000000, {
                amplifier: 0,
                showParticles: false,
            });
        } else if (difficulty === 2) {
            entity.addEffect("strength", 20000000, {
                amplifier: 1,
                showParticles: false,
            });
        }
    } catch (error) {
        console.log(error);
        
    }
});

/**
 * Subscribes to entity hit events and applies knockback if hit with "slapstick"
 */
world.afterEvents.entityHitEntity.subscribe((e) => {
    const { damagingEntity, hitEntity } = e;

    if (damagingEntity?.typeId === "lv_zo:slapstick" && hitEntity) {
        applyKnockbackToEntity(damagingEntity, hitEntity, 3); // Adjust knockback strength
    }
    if (damagingEntity?.typeId === "lv_zo:tanky" && hitEntity) {
        applyKnockbackToEntity(damagingEntity, hitEntity, 1); // Adjust knockback strength
    }
});

const LIGHT_BLOCK_TYPE = "lv_zo:custom_light";
const FLASHLIGHT_ITEM = "lv_zo:flashlight_on";
const distances = [0, 1, 2, 3, 4, 5, 7];
const playerLightMap = new Map();

system.runInterval(() => {
    for (const player of world.getPlayers()) {
        const equippable = player.getComponent("equippable");
        const held = equippable?.getEquipment(EquipmentSlot.Mainhand);
        const playerId = player.id;

        const previouslyLit = playerLightMap.get(playerId) ?? [];

        // Remove old lights
        for (const pos of previouslyLit) {
            const block = player.dimension.getBlock(pos);
            if (block && block.typeId === LIGHT_BLOCK_TYPE) {
                block.setType("minecraft:air");
            }
        }

        // If not holding the flashlight, skip placing new lights
        if (held?.typeId !== FLASHLIGHT_ITEM) {
            playerLightMap.set(playerId, []);
            continue;
        }

        const viewDir = player.getViewDirection();
        const origin = player.location;
        const newLit = [];

        for (const dist of distances) {
            const target = {
                x: Math.floor(origin.x + viewDir.x * dist),
                y: Math.floor(origin.y + viewDir.y * dist + 1),
                z: Math.floor(origin.z + viewDir.z * dist),
            };

            const block = player.dimension.getBlock(target);
            if (block && block.typeId === "minecraft:air") {
                block.setType(LIGHT_BLOCK_TYPE);
                newLit.push(target);
            }
        }
        
        playerLightMap.set(playerId, newLit);
    }
}, 2); // Every 2 ticks (~0.1s)

/**
 * Applies knockback to an entity
 * @param {Entity} attacker - The entity dealing the hit
 * @param {Entity} target - The entity being knocked back
 * @param {number} strength - The strength of the knockback
 */
export function applyKnockbackToEntity(attacker, target, strength = 1) {
    const attackerPos = attacker.location;
    const targetPos = target.location;

    // Calculate direction vector (normalized)
    const dx = targetPos.x - attackerPos.x;
    const dz = targetPos.z - attackerPos.z;
    const magnitude = Math.sqrt(dx * dx + dz * dz);

    if (magnitude === 0) return; // Prevent division by zero

    const knockbackY = 0.6; // Small upward boost for a better effect

    // Apply motion (velocity)
    target.applyKnockback(dx, dz, strength, knockbackY);

    //Only fire this event if entity is tanky type id and is not query illager captain
    if (
        attacker?.typeId === "lv_zo:tanky" &&
        !attacker.hasComponent("minecraft:is_illager_captain")
    ) {
        attacker.triggerEvent("switch_attack");
    }
}

/**
 * Subscribes to script events received and summons the lost limb dummies
 */
system.afterEvents.scriptEventReceive.subscribe((e) => {
    const { id, message, sourceEntity } = e;
    if (!sourceEntity) return;

    switch (id) {
        case "lv_zo:zombie.lose.arm": {
            try {
                // Get the entity type ID
                const typeId = sourceEntity.typeId;
                if (!typeId) return;

                // Construct the new entity ID with '_limb'
                const limbTypeId = `${typeId}_limb`;

                // Get the position and rotation of sourceEntity
                const location = sourceEntity.location;
                const rotation = {
                    x: sourceEntity.getRotation().x,
                    y: sourceEntity.getRotation().y,
                };

                // Retrieve 'lv_zo:skin' property from the source entity
                const skin = sourceEntity.getProperty("lv_zo:skin");

                // Summon the new entity
                const newEntity = sourceEntity.dimension.spawnEntity(
                    limbTypeId,
                    location,
                );

                // Set rotation
                newEntity.setRotation({ x: rotation.x, y: rotation.y });

                // Transfer the skin property
                if (skin) {
                    newEntity.setProperty("lv_zo:skin", skin);
                }

                newEntity.setProperty("lv_zo:missing_arm", true);
            } catch (error) {
                console.warn("Error in zombie limb summoning:", error);
            }
            break;
        }
        case "lv_zo:zombie.lose.leg": {
            try {
                // Get the entity type ID
                const typeId = sourceEntity.typeId;
                if (!typeId) return;

                // Construct the new entity ID with '_limb'
                const limbTypeId = `${typeId}_limb`;

                // Get the position and rotation of sourceEntity
                const location = sourceEntity.location;
                const rotation = {
                    x: sourceEntity.getRotation().x,
                    y: sourceEntity.getRotation().y,
                };

                // Retrieve 'lv_zo:skin' property from the source entity
                const skin = sourceEntity.getProperty("lv_zo:skin");

                // Summon the new entity
                const newEntity = sourceEntity.dimension.spawnEntity(
                    limbTypeId,
                    location,
                );

                // Set rotation
                newEntity.setRotation({ x: rotation.x, y: rotation.y });

                // Transfer the skin property
                if (skin) {
                    newEntity.setProperty("lv_zo:skin", skin);
                }

                newEntity.setProperty("lv_zo:missing_leg", true);
            } catch (error) {
                console.warn("Error in zombie limb summoning:", error);
            }
            break;
        }
        case "lv_zo:remove": {
            try {
                const typeId = sourceEntity.typeId;
                if (!typeId) return;

                const itemID = `${typeId}_spawn_egg`;

                const location = sourceEntity.location;

                // Spawn the item
                sourceEntity.dimension.spawnItem(
                    new ItemStack(itemID, 1),
                    location,
                );
                sourceEntity.triggerEvent("lv_zo:despawn");
            } catch (error) {
                console.warn("Failed to spawn item:", error);
            }
            break;
        }
        case "lv_zo:zombie.lose.head": {
            try {
                // Get the entity type ID
                const typeId = sourceEntity.typeId;
                if (!typeId) return;

                // Construct the new entity ID with '_limb'
                const limbTypeId = `${typeId}_limb`;

                // Get the position and rotation of sourceEntity
                const location = sourceEntity.location;
                const rotation = {
                    x: sourceEntity.getRotation().x,
                    y: sourceEntity.getRotation().y,
                };

                // Retrieve 'lv_zo:skin' property from the source entity
                const skin = sourceEntity.getProperty("lv_zo:skin");

                // Summon the new entity
                const newEntity = sourceEntity.dimension.spawnEntity(
                    limbTypeId,
                    location,
                );

                // Set rotation
                newEntity.setRotation({ x: rotation.x, y: rotation.y });

                // Transfer the skin property
                if (skin) {
                    newEntity.setProperty("lv_zo:skin", skin);
                }

                newEntity.setProperty("lv_zo:missing_head", true);

                sourceEntity.triggerEvent("despawn");
            } catch (error) {
                console.warn("Error in zombie limb summoning:", error);
            }
            break;
        }
        case "lv_zo:tanky.spawn_mini": {
            const typeId = sourceEntity.typeId;
            if (!typeId) return;
            // Retrieve 'lv_zo:skin' property from the source entity
            const skin = sourceEntity.getProperty("lv_zo:skin");

            // Summon the new entity
            const newEntity = sourceEntity.dimension.spawnEntity(
                "lv_zo:mini",
                sourceEntity.location,
            );

            // Set rotation
            newEntity.setRotation({
                x: sourceEntity.getRotation().x,
                y: sourceEntity.getRotation().y,
            });
            system.runTimeout(() => {
                // Transfer the skin property
                if (skin) {
                    newEntity.setProperty("lv_zo:skin", skin);
                }

                sourceEntity.kill();
            }, 2);

            break;
        }
        case "lv_zo:zomboss.attack.summon": {
            system.runTimeout(() => {
                sourceEntity.runCommand(
                    "/camerashake add @a[r=12] 0.07 1 positional",
                );
                sourceEntity.runCommand(
                    "/camerashake add @a[r=12] 0.01 1 rotational",
                );
            }, 40);
            system.runTimeout(() => {
                const zombieTypes = [
                    "lv_zo:zomb",
                    "lv_zo:chubby",
                    "lv_zo:small_zomb",
                ];

                const radius = 4;
                const amount = 5;

                const sourceLoc = sourceEntity.location;
                const yawOffset = Math.random() * Math.PI * 2;

                for (let i = 0; i < amount; i++) {
                    const angle = yawOffset + (i / amount) * Math.PI * 2;
                    const x = sourceLoc.x + Math.cos(angle) * radius;
                    const z = sourceLoc.z + Math.sin(angle) * radius;
                    const y = sourceLoc.y;

                    const type = zombieTypes[i % zombieTypes.length];

                    const spawned = sourceEntity.dimension.spawnEntity(type, {
                        x,
                        y,
                        z,
                    });
                    if (spawned) {
                        try {
                            spawned.runCommandAsync(
                                "/playanimation @s animation.lv_zo.zomb.dig_out",
                            );
                            spawned.setProperty("lv_zo:horde_night", true);
                            spawned.runCommandAsync(
                                "/effect @s slowness 1 2 true",
                            );
                        } catch (err) {
                        }
                    }
                }

                const players = sourceEntity.dimension.getPlayers({
                    location: sourceEntity.location,
                    maxDistance: 15,
                });

                for (const player of players) {
                    const dx = sourceEntity.location.x - player.location.x;
                    const dz = sourceEntity.location.z - player.location.z;
                    const dy = sourceEntity.location.y - player.location.y;

                    const distance = Math.sqrt(dx * dx + dz * dz);
                    const pullStrength = 0.6;
                    const verticalStrength = 0.2;

                    if (distance > 0.1) {
                        const dirX = dx / distance;
                        const dirZ = dz / distance;

                        player.applyKnockback(
                            dirX,
                            dirZ,
                            pullStrength,
                            verticalStrength,
                        );
                    }
                }
            }, 60);
            break;
        }
        case "lv_zo:zomboss.attack.smash": {
            system.runTimeout(() => {
                sourceEntity.runCommand(
                    "/camerashake add @a[r=12] 0.07 0.5 positional",
                );
                sourceEntity.runCommand(
                    "/camerashake add @a[r=12] 0.01 0.5 rotational",
                );
            }, 15);
            system.runTimeout(() => {
                try {
                    const radius = 10;
                    //damage should be random float between 8 and 12
                    const baseDamage =
                        Math.floor(Math.random() * (12 - 9 + 1)) + 9;
                    const baseKnockback = 6;

                    const origin = sourceEntity.location;

                    const nearbyEntities = world.getDimension("overworld")
                        .getEntities({
                            location: origin,
                            maxDistance: radius,
                        });

                    for (const entity of nearbyEntities) {
                        if (!entity.isValid() || entity === sourceEntity) {
                            continue;
                        }

                        const entityLoc = entity.location;
                        const dx = entityLoc.x - origin.x;
                        const dz = entityLoc.z - origin.z;
                        const distance = Math.sqrt(dx * dx + dz * dz) || 0.01;

                        const knockbackScale = 1 - (distance / radius); // closer = stronger
                        const knockbackX = (dx / distance) * baseKnockback *
                            knockbackScale;
                        const knockbackZ = (dz / distance) * baseKnockback *
                            knockbackScale;

                        // Apply knockback to all
                        try {
                            entity.applyKnockback(knockbackX, knockbackZ, 3, 1);
                            entity.runCommand(
                                "/camerashake add @s 0.1 0.5 positional",
                            );
                            entity.runCommand(
                                "/camerashake add @s 0.02 0.5 rotational",
                            );
                        } catch (err) {
                        }

                        // Only apply damage if NOT a zombie
                        if (
                            !entity.getComponent("minecraft:type_family")
                                .hasTypeFamily("zombie")
                        ) {
                            try {
                                entity.applyDamage(baseDamage, {
                                    cause: "entityAttack",
                                    damagingEntity: sourceEntity,
                                });
                            } catch (err) {
                            }
                        }
                    }
                } catch (error) {
                }
            }, 20);
            break;
        }
        case "lv_zo:zomboss.attack.normal": {
            system.runTimeout(() => {
                sourceEntity.runCommand(
                    "/camerashake add @a[r=8] 0.07 0.5 positional",
                );
                sourceEntity.runCommand(
                    "/camerashake add @a[r=8] 0.01 0.5 rotational",
                );
            }, 17);
        }
    }
});

//  AIRDROP

// Configuration
const MIN_DISTANCE = 20; // Minimum distance from player (blocks)
const MAX_DISTANCE = 40; // Maximum distance from player (blocks)
const DROP_HEIGHT = 140; // Height at which to spawn airdrops
const MIN_INTERVAL = 6 * 60 * 20; // 8 minutes in ticks (20 ticks per second)
const MAX_INTERVAL = 10 * 60 * 20; // 12 minutes in ticks
const NOTIFICATION_RANGE = 50; // Range to notify players (blocks)
const FALL_VELOCITY = 0.008; // Upward velocity to counteract gravity

/**
 * Spawns an airdrop around a random player
 */
function spawnAirdrop() {
    const players = world.getAllPlayers();
    if (players.length === 0) return;

    // Select a random player
    const player = players[Math.floor(Math.random() * players.length)];
    const playerPos = player.location;
    const dimension = player.dimension;

    // Generate a random angle and distance
    const angle = Math.random() * Math.PI * 2; // Random angle in radians
    const distance = MIN_DISTANCE +
        Math.random() * (MAX_DISTANCE - MIN_DISTANCE);

    // Calculate offset position
    const offsetX = Math.cos(angle) * distance;
    const offsetZ = Math.sin(angle) * distance;

    // Create target position
    const targetPos = {
        x: Math.floor(playerPos.x + offsetX),
        y: DROP_HEIGHT,
        z: Math.floor(playerPos.z + offsetZ),
    };

    // Check if the target position is air
    try {
        const block = dimension.getBlock(targetPos);

        if (block && block.typeId === "minecraft:air") {
            try {
                // Spawn the airdrop
                dimension.spawnEntity("lv_zo:airdrop", targetPos);

                // Notify nearby players - get all players and filter by distance
                const allPlayers = world.getAllPlayers();
                for (const nearbyPlayer of allPlayers) {
                    // Calculate distance to airdrop
                    const playerPos = nearbyPlayer.location;
                    const dx = playerPos.x - targetPos.x;
                    const dy = playerPos.y - targetPos.y;
                    const dz = playerPos.z - targetPos.z;
                    const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

                    // Only notify players within range
                    if (distance <= NOTIFICATION_RANGE) {
                        nearbyPlayer.onScreenDisplay.setActionBar(
                            "§c§lAirdrop §r§fhas been dropped nearby!",
                        );
                    }
                }
            } catch (error) {
            }
        } else {
        }
    } catch (error) {
    }

    // Schedule next airdrop at random interval
    scheduleNextAirdrop();
}

/**
 * Schedules the next airdrop at random interval
 */
function scheduleNextAirdrop() {
    const nextInterval = MIN_INTERVAL +
        Math.floor(Math.random() * (MAX_INTERVAL - MIN_INTERVAL));
    system.runTimeout(spawnAirdrop, nextInterval);
}

/**
 * Continuously applies slow fall effect to all airdrops that aren't on the ground
 */
system.runInterval(() => {
    // Get all airdrop entities in all dimensions
    const airdrops = world.getDimension("overworld").getEntities({
        type: "lv_zo:airdrop",
        excludeTags: ["drop_is_on_ground"],
    });

    for (const airdrop of airdrops) {
        // Check if the airdrop is still in the air (not on ground)
        if (airdrop && !isEntityOnGround(airdrop)) {
            // Apply slow fall effect
            slowEntityFall(airdrop, FALL_VELOCITY);
        }
    }
}, 1); // Run every tick (1/20 of a second)

/**
 * Checks if an entity is on the ground
 * @param {Entity} entity - The entity to check
 * @returns {boolean} - True if the entity is on the ground
 */
function isEntityOnGround(entity) {
    try {
        // Check if there's a block directly below the entity
        // As a double check make it so this entity is tagged and then make it so the entity is ignored if it has the tag
        if (entity.getComponent("minecraft:is_baby")) {
            entity.addTag("drop_is_on_ground");
        }
        return entity.getComponent("minecraft:is_baby");
    } catch (error) {
    }
}
/**
 * Applies a small upward velocity to an entity to make it fall slower
 * @param {Entity} entity - The entity to affect
 * @param {number} yVelocity - The upward velocity to apply (default: 0.1)
 */
function slowEntityFall(entity, yVelocity) {
    // First try to use the velocity component if available
    const velocity = entity.getComponent("minecraft:velocity");
    if (velocity) {
        velocity.y = yVelocity;
        return;
    }

    // Alternative: use setVelocity method
    entity.applyImpulse({ x: 0, y: yVelocity, z: 0 });
}

scheduleNextAirdrop();
