import { world, system, Trigger, ItemStack, EquipmentSlot } from "@minecraft/server";
import { fo } from "../utilities/fo_constants";
import { getDistance3D } from "../utilities/fo_helpers";

export function initEntitiesUtility() {
    enitiesEventUtility()
}


function enitiesEventUtility() {

    
    

    const
        FOREBODINGCHECK1 = fo.scoreChapter3, 
        FOREBODINGCHECK2 = fo.scoreChapter4, 
        FOREBODINGCHECK3 = fo.scoreChapter5, 
        FOREBODINGCHECK7 = fo.scoreChapter7s, 

        overworld = fo.overworld,

        glassBlocks = new Set(fo.glassBlocks),
        lightSourceBlocks = new Set(fo.lightSourceBlocks);

    
    overworld.runCommand(`tickingarea remove forebearer`);


    
    
    function getPositionAround(center, radius) {
        const angle = Math.random() * Math.PI * 2;
        const distance = radius;
        return {
            x: center.x + Math.cos(angle) * distance,
            y: center.y,
            z: center.z + Math.sin(angle) * distance,
        };
    }

    
    function getDistance(pos1, pos2) {
        if (!pos1 || !pos2) {
            return Infinity; 
        }
        return Math.sqrt(
            Math.pow(pos1.x - pos2.x, 2) +
            Math.pow(pos1.y - pos2.y, 2) +
            Math.pow(pos1.z - pos2.z, 2)
        );
    }

    
    function isEntityInPlayerSight(player, entity, maxViewDistance = 32) {
        if (!player || !entity) {
            return false;
        }

        const playerPos = player.location;
        const entityPos = entity.location;

        if (!playerPos || !entityPos) {
            return false;
        }

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

        if (distance > maxViewDistance) {
            return false;
        }

        const playerViewDir = player.getViewDirection();
        if (!playerViewDir) {
            return false;
        }

        
        const entityDir = {
            x: entityPos.x - playerPos.x,
            y: entityPos.y - playerPos.y,
            z: entityPos.z - playerPos.z
        };

        
        const entityDirLength = Math.sqrt(entityDir.x ** 2 + entityDir.y ** 2 + entityDir.z ** 2);
        const normalizedEntityDir = {
            x: entityDir.x / entityDirLength,
            y: entityDir.y / entityDirLength,
            z: entityDir.z / entityDirLength
        };

        
        const dotProduct = playerViewDir.x * normalizedEntityDir.x +
            playerViewDir.y * normalizedEntityDir.y +
            playerViewDir.z * normalizedEntityDir.z;

        
        const angle = Math.acos(dotProduct);

        
        const fovThreshold = (60 * Math.PI) / 180;

        
        return angle <= fovThreshold;
    }



    

    
    function forebearerSpawn(event) {
        const
            player = event.player,
            playerName = player.nameTag,
            playerLoc = player.location,
            playerDim = player.dimension,
            playerRotation = player.getRotation(),
            playerViewDirection = player.getViewDirection();

        const playerEquip = player.getComponent("equippable");

        
        const mainHand = playerEquip.getEquipment(EquipmentSlot.Mainhand);
        const offHand = playerEquip.getEquipment(EquipmentSlot.Offhand);
        const headEquip = playerEquip.getEquipment(EquipmentSlot.Head);
        const chestEquip = playerEquip.getEquipment(EquipmentSlot.Chest);
        const legsEquip = playerEquip.getEquipment(EquipmentSlot.Legs);
        const feetEquip = playerEquip.getEquipment(EquipmentSlot.Feet);

        system.runTimeout(() => {
            playerDim.runCommand(`tickingarea add circle ${playerLoc.x} ${playerLoc.y} ${playerLoc.z} 1 forebearer`)

            system.runTimeout(() => {
                const forebearer = playerDim.spawnEntity("fo:forebearer", playerLoc);

                world.scoreboard.getObjective("Online").addScore(playerName, 0)

                forebearer.nameTag = playerName;
                forebearer.setRotation(playerRotation);
                forebearer.lookAt(playerViewDirection);

                system.runTimeout(() => {
                    
                    const selector = `@e[type=fo:forebearer,name=${playerName},c=-1]`;

                    if (mainHand) playerDim.runCommand(`replaceitem entity ${selector} slot.weapon.mainhand 0 ${mainHand.typeId}`);
                    if (offHand) playerDim.runCommand(`replaceitem entity ${selector} slot.weapon.offhand 0 ${offHand.typeId}`);
                    if (headEquip) playerDim.runCommand(`replaceitem entity ${selector} slot.armor.head 0 ${headEquip.typeId}`);
                    if (chestEquip) playerDim.runCommand(`replaceitem entity ${selector} slot.armor.chest 0 ${chestEquip.typeId}`);
                    if (legsEquip) playerDim.runCommand(`replaceitem entity ${selector} slot.armor.legs 0 ${legsEquip.typeId}`);
                    if (feetEquip) playerDim.runCommand(`replaceitem entity ${selector} slot.armor.feet 0 ${feetEquip.typeId}`);

                    playerDim.runCommand(`tickingarea remove forebearer`);
                }, 1)


            }, 1)
        }, 1)
    };

    
    function forebearerDies(entity) {
        if (!entity || entity.typeId != "fo:forebearer") return;

        const playerName = entity.nameTag

        if (!playerName) return;

        world.scoreboard.getObjective("Online").removeParticipant(playerName)

        
        let stored = world.getDynamicProperty("forebearerDead") || "[]";
        let storedArr = JSON.parse(stored);

        
        if (!storedArr.includes(playerName)) {
            storedArr.push(playerName);
        }

        
        world.setDynamicProperty("forebearerDead", JSON.stringify(storedArr));

        

    };

    function logForebearerDeadList() {
        let stored = world.getDynamicProperty("forebearerDead") || "[]";
        let storedArr = JSON.parse(stored);

        console.warn("=== Forebearer Dead List ===");
        storedArr.forEach((name, i) => {
            console.warn(`${i + 1}: ${name}`);
        });
        console.warn("============================");
    }


    
    system.runInterval(() => {

        const
            forebodingScore = fo.forebodingScore,
            players = overworld.getPlayers();

        if (players.length === 0) return;

        const foreshadows = overworld.getEntities({
            type: "fo:foreshadow"
        });

        foreshadowJump(foreshadows);
        foreshadowPop(forebodingScore, players, foreshadows);
        foreshadowLightOff(forebodingScore, foreshadows);
        forebearerStare(forebodingScore, players);

    }, 253)

    
    system.runInterval(() => {
        const
            forebodingScore = fo.forebodingScore,
            players = overworld.getPlayers();

        if (players.length === 0) return;

        const foreshadows = overworld.getEntities({
            type: "fo:foreshadow"
        });

        foreshadowGlass(forebodingScore, players, foreshadows)

    }, 3)



    
    function forebearerStare(forebodingScore, players) {
        if (forebodingScore < FOREBODINGCHECK2 && forebodingScore > FOREBODINGCHECK7) return;

        const maxDistance = 12;
        const playerDimension = players[0]?.dimension;

        const allForebearers = playerDimension ? playerDimension.getEntities({
            type: "fo:forebearer",
            excludeNames: ["Pilot1782"],
        }) : [];

        if (allForebearers.length === 0) return;

        if (forebodingScore < FOREBODINGCHECK7) {
            for (const forebearer of allForebearers) {
                forebearer.triggerEvent("foreboding:start_aggressive");
            }
        }

        
        for (const player of players) {
            const nearbyPlayers = playerDimension.getPlayers({
                excludeNames: [player.name],
                location: player.location,
                maxDistance: 48,
            });

            
            if (nearbyPlayers.length > 0) continue;

            
            const closeForebearers = allForebearers.filter(forebearer => {
                const distance = getDistance(player.location, forebearer.location);
                return distance <= maxDistance;
            });

            if (closeForebearers.length === 0) continue;

            for (let forebearer of closeForebearers) {
                const isInSight = isEntityInPlayerSight(player, forebearer, 32); 

                if (!isInSight) {
                    forebearer.lookAt(player.location);
                }
            }
        }
    };


    function foreshadowJump(foreshadows) {
        

        if (foreshadows.length === 0) return;

        

        for (let foreshadow of foreshadows) {
            const foreshadowLoc = foreshadow.location;

            const
                foreshadowY = foreshadowLoc.y,
                player = overworld.getPlayers({
                    closest: 1,
                    maxDistance: 64,
                    location: foreshadowLoc
                });

            if (player.length === 0) continue;

            const playerY = player[0].location.y

            if ((foreshadowY + 10) < playerY) {
                foreshadow.triggerEvent("foreboding:start_higher_grounds")

                
                continue;
            } else if (foreshadowY >= playerY) {
                foreshadow.triggerEvent("foreboding:stop_higher_grounds")

                

                continue;
            };

        }

    };


    
    function foreshadowPop(forebodingScore, players, foreshadows) {

        const foreshadowPlayerRatio = foreshadows.length / players.length;

        
        if (forebodingScore >= FOREBODINGCHECK3) { 
            if (foreshadowPlayerRatio > 6) {
                const randomforeshadow = foreshadows[Math.floor(Math.random() * foreshadows.length)]; 

                randomforeshadow.remove();

            } else if (foreshadowPlayerRatio < 6) {

                const
                    randomPlayer = players[Math.floor(Math.random() * players.length)], 
                    spawnPos = getPositionAround(randomPlayer.location, 20),
                    topMostBlock = overworld.getTopmostBlock(spawnPos, randomPlayer.location.y + 20);

                overworld.spawnEntity("fo:foreshadow", topMostBlock.location)

            }
        } else if (forebodingScore >= FOREBODINGCHECK2) {
            if (foreshadowPlayerRatio > 3) {
                const randomforeshadow = foreshadows[Math.floor(Math.random() * foreshadows.length)]; 

                randomforeshadow.remove();

            } else if (foreshadowPlayerRatio < 3) {

                const
                    randomPlayer = players[Math.floor(Math.random() * players.length)], 
                    spawnPos = getPositionAround(randomPlayer.location, 20),
                    topMostBlock = overworld.getTopmostBlock(spawnPos, randomPlayer.location.y + 20);

                overworld.spawnEntity("fo:foreshadow", topMostBlock.location)

            }
        } else if (forebodingScore >= FOREBODINGCHECK1) { 
            if (foreshadowPlayerRatio > 1) {
                const randomforeshadow = foreshadows[Math.floor(Math.random() * foreshadows.length)]; 

                randomforeshadow.remove();

            } else if (foreshadowPlayerRatio < 1) {

                const
                    randomPlayer = players[Math.floor(Math.random() * players.length)], 
                    spawnPos = getPositionAround(randomPlayer.location, 20),
                    topMostBlock = overworld.getTopmostBlock(spawnPos, randomPlayer.location.y + 20);

                overworld.spawnEntity("fo:foreshadow", topMostBlock.location)

            }
        } else if (forebodingScore < FOREBODINGCHECK7) { 
            if (foreshadowPlayerRatio > 12) {
                const randomforeshadow = foreshadows[Math.floor(Math.random() * foreshadows.length)]; 

                randomforeshadow.remove();

            } else if (foreshadowPlayerRatio < 12) {

                const
                    randomPlayer = players[Math.floor(Math.random() * players.length)], 
                    spawnPos = getPositionAround(randomPlayer.location, 20),
                    topMostBlock = overworld.getTopmostBlock(spawnPos, randomPlayer.location.y + 20);

                overworld.spawnEntity("fo:foreshadow", topMostBlock.location)

            }
        } else if (foreshadows.length !== 0) {
            for (const foreshadow of foreshadows) {
                foreshadow.remove();
            }
        }

    };


    function foreshadowLightOff(forebodingScore, foreshadows) {
        if (foreshadows.length === 0) return;
        
        const forebodingAmplifier = 1 + (2 * (Math.max(forebodingScore, fo.scoreChapter4) - fo.scoreChapter4 / fo.scoreChapter6 - fo.scoreChapter4));

        if (Math.random() > 1 / 150 * forebodingAmplifier) return; 
        let i = 0;
        for (const foreshadow of foreshadows) {
            if (i > 18) break;
            system.runTimeout(() => {
                try {
                    if (!foreshadow.isValid) return;

                    const
                        x = foreshadow.location.x,
                        y = foreshadow.location.y + 1,
                        z = foreshadow.location.z;

                    const minX = Math.floor(x) - 1;
                    const maxX = Math.floor(x) + 1;
                    const minZ = Math.floor(z) - 1;
                    const maxZ = Math.floor(z) + 1;

                    const minY = Math.max(Math.floor(y) - 1, overworld.heightRange.min);
                    const maxY = Math.min(Math.floor(y) + 1, overworld.heightRange.max);

                    for (let bx = minX; bx <= maxX; bx++) {
                        for (let by = minY; by <= maxY; by++) {
                            for (let bz = minZ; bz <= maxZ; bz++) {

                                const block = overworld.getBlock({ x: bx, y: by, z: bz });
                                if (!block) continue;

                                const typeId = block.typeId;
                                if (!lightSourceBlocks.has(typeId)) continue;


                                overworld.runCommand(`setblock ${block.location.x} ${block.location.y} ${block.location.z} air destroy`)

                            }
                        }
                    }
                } catch (e) {
                    console.warn(`[DEBUG] Forehsadow failed to destroy light source`)
                }

            }, i)
            i++
        };

    };


    function foreshadowGlass(forebodingScore, players, foreshadows) {
        
        
        
        if (forebodingScore < FOREBODINGCHECK3) return;
        

        for (let foreshadow of foreshadows) {
            const nearbyPlayers = overworld.getPlayers({
                location: foreshadow.location,
                maxDistance: 60,
            });

            if (nearbyPlayers.length > 1 || nearbyPlayers.length === 0) {
                
                foreshadow.triggerEvent("foreboding:stop_lighing_glance");
                continue
            };
            const
                nearbyPlayer = nearbyPlayers[0],
                foreshadowAdjLoc = {
                    x: foreshadow.location.x,
                    y: foreshadow.location.y + 1.62,
                    z: foreshadow.location.z,

                },
                neabyPlayerAdjLoc = {
                    x: nearbyPlayer.location.x,
                    y: nearbyPlayer.location.y + 1.62,
                    z: nearbyPlayer.location.z,
                },
                direction = {
                    x: foreshadowAdjLoc.x - neabyPlayerAdjLoc.x,
                    y: foreshadowAdjLoc.y - neabyPlayerAdjLoc.y,
                    z: foreshadowAdjLoc.z - neabyPlayerAdjLoc.z,
                },

                rayBlockHit = overworld.getBlockFromRay(neabyPlayerAdjLoc, direction, {
                    maxDistance: Math.min(getDistance3D(neabyPlayerAdjLoc, foreshadowAdjLoc), 16),
                    
                });

            if (!rayBlockHit) {
                
                foreshadow.triggerEvent("foreboding:stop_lighing_glance");
                continue
            };

            const rayBlock = rayBlockHit.block;

            

            const isGlass = glassBlocks.has(rayBlock.typeId);
            

            if (!isGlass) {
                
                foreshadow.triggerEvent("foreboding:stop_lighing_glance");
                continue
            };

            foreshadow.triggerEvent("foreboding:start_glass_glance");
            
        }

    };


    
    world.beforeEvents.playerLeave.subscribe((event) => {

        const currentScore = fo.forebodingScore;

        if (currentScore >= FOREBODINGCHECK2 || currentScore <= FOREBODINGCHECK7) forebearerSpawn(event);


    });


    
    world.afterEvents.entityDie.subscribe((event) => {
        const entity = event.deadEntity;

        forebearerDies(entity);

    });


};