import { world, system } from "@minecraft/server";
import { fo } from "./utilities/fo_constants";




export function initEventSubscriptions() {

    const CHEST_LIST_PROPERTY = "chest_locations";
    const DOOR_LIST_PROPERTY = "door_locations";
    const FURNACE_LIST_PROPERTY = "furnace_locations";
    const GLASS_LIST_PROPERTY = "glass_locations";
    const TORCH_LIST_PROPERTY = "torch_locations";
    const MINED_BLOCKS_KEY = "minedBlocks";
    const BLOCK_HISTORY_KEY = "blockHistory";
    const TUNNEL_DATA_KEY = "tunnelData";

    const
        forebodingChapter03 = fo.scoreChapter3,
        forebodingChapter04 = fo.scoreChapter4,
        forebodingChapter05 = fo.scoreChapter5;

    const bellHitHistory = new Map(); 
    const BREAKINGSOUNDINTERVAL = 5;
    const BREAKINTERVAL = BREAKINGSOUNDINTERVAL * 4;
    const PLACEINTERVAL = 4;

    const woodenDoors = fo.doorIds;
    const furnaces = fo.furnacesIds;
    const glassBlocks = fo.glassBlocks;
    const VALID_BLOCKS = fo.validMinedBlocks;
    const ValidBlocksArray = fo.ValidMinedBlocksArray;


    let
        bellCooldown = 0;

    

    
    function getDistance3V(loc1, loc2) {
        const dx = loc1.x - loc2.x;
        const dy = loc1.y - loc2.y;
        const dz = loc1.z - loc2.z;
        return Math.sqrt(dx * dx + dy * dy + dz * dz);
    }

    
    function isPlayerLookingAtTarget(player, targetLocation) {
        const playerViewDirection = player.getViewDirection();
        const toTarget = {
            x: targetLocation.x - player.location.x,
            y: targetLocation.y - player.location.y,
            z: targetLocation.z - player.location.z
        };

        
        const length = Math.sqrt(toTarget.x * toTarget.x + toTarget.y * toTarget.y + toTarget.z * toTarget.z);
        if (length === 0) return true;

        const normalizedToTarget = {
            x: toTarget.x / length,
            y: toTarget.y / length,
            z: toTarget.z / length
        };

        
        const dotProduct = playerViewDirection.x * normalizedToTarget.x +
            playerViewDirection.y * normalizedToTarget.y +
            playerViewDirection.z * normalizedToTarget.z;

        
        return dotProduct > 0.5;
    }

    
    function blockBreakingAction(overworld, data, block, blockLoc) {
        const breakingSoundInterval = system.runInterval(() => {
            overworld.playSound(data.beingBrokenSound, blockLoc, {
                volume: 2,
            });
        }, BREAKINGSOUNDINTERVAL)

        system.runTimeout(() => {
            const dropBlockId = new ItemStack(block.typeId, 1);
            block.setType("minecraft:air");
            overworld.playSound(data.brokenSound, blockLoc, {
                volume: 2,
            });
            overworld.spawnItem(dropBlockId, blockLoc);
            system.clearRun(breakingSoundInterval)
        }, BREAKINTERVAL);
    }

    
    function blockPlacingAction(overworld, data, block, blockLoc) {
        system.runTimeout(() => {
            block.setType("minecraft:mossy_cobblestone");
            overworld.playSound(data.brokenSound, blockLoc, {
                volume: 2,
            });
        }, PLACEINTERVAL)

    }


    

    world.afterEvents.playerInteractWithBlock.subscribe((event) => {

        chestSaveChest(event);
        doorSaveDoor(event);
        noSleep(event); 
        bellSpam(event);


    });

    world.afterEvents.playerPlaceBlock.subscribe((event) => {

        chestSaveChest(event);
        doorSaveDoor(event);
        glassSaveGlass(event);
        randomTorchSaveTorch(event);

    });

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

        chestRemoveChest(event);
        doorRemoveDoor(event);
        glassRemoveGlass(event);
        randomTorchRemoveTorch(event);
        mineRestoreSaveBlocks(event);


    });


    

    function chestSaveChest(event) {
        const block = event.block;
        if (block.typeId === "minecraft:chest") {
            const chestLocations = JSON.parse(world.getDynamicProperty(CHEST_LIST_PROPERTY) || "[]");
            const chestPos = { x: block.location.x, y: block.location.y, z: block.location.z, dimension: block.dimension.id };

            
            if (!chestLocations.some(loc => loc.x === chestPos.x && loc.y === chestPos.y && loc.z === chestPos.z && loc.dimension === chestPos.dimension)) {
                chestLocations.push(chestPos);
                world.setDynamicProperty(CHEST_LIST_PROPERTY, JSON.stringify(chestLocations));
                
            } else {
                
            }
        }

    };

    function chestRemoveChest(event) {
        const block = event.block;
        if (block.typeId === "minecraft:chest") {
            const chestLocations = JSON.parse(world.getDynamicProperty(CHEST_LIST_PROPERTY) || "[]");
            const chestPos = { x: block.location.x, y: block.location.y, z: block.location.z, dimension: block.dimension.id };

            
            const updatedLocations = chestLocations.filter(loc => !(loc.x === chestPos.x && loc.y === chestPos.y && loc.z === chestPos.z && loc.dimension === chestPos.dimension));
            world.setDynamicProperty(CHEST_LIST_PROPERTY, JSON.stringify(updatedLocations));
            
        }
    };

    function doorSaveDoor(event) {
        const block = event.block;
        const player = event.player;

        if (woodenDoors.includes(block.typeId)) {
            
            const isUpperBlock = block.permutation.getState("upper_block_bit");
            if (isUpperBlock) {
                return; 
            }

            const doorLocations = JSON.parse(world.getDynamicProperty(DOOR_LIST_PROPERTY) || "[]");
            const doorPos = {
                x: block.location.x, y: block.location.y, z: block.location.z,
                dimensionId: player.dimension.id
            };

            
            if (!doorLocations.some(loc => loc.x === doorPos.x && loc.y === doorPos.y && loc.z === doorPos.z && loc.dimensionId === doorPos.dimensionId)) {
                doorLocations.push(doorPos);
                world.setDynamicProperty(DOOR_LIST_PROPERTY, JSON.stringify(doorLocations));
                
            } else {
                
            }
        }

        
        if (furnaces.includes(block.typeId)) {
            const furnaceLocations = JSON.parse(world.getDynamicProperty(FURNACE_LIST_PROPERTY) || "[]");
            const furnacePos = {
                x: block.location.x, y: block.location.y, z: block.location.z,
                dimensionId: player.dimension.id
            };

            
            if (!furnaceLocations.some(loc => loc.x === furnacePos.x && loc.y === furnacePos.y && loc.z === furnacePos.z && loc.dimensionId === furnacePos.dimensionId)) {
                furnaceLocations.push(furnacePos);
                world.setDynamicProperty(FURNACE_LIST_PROPERTY, JSON.stringify(furnaceLocations));
                
            } else {
                
            }
        }

    };

    function doorRemoveDoor(event) {
        const block = event.block;
        const player = event.player;

        if (woodenDoors.includes(block.typeId)) {
            const doorLocations = JSON.parse(world.getDynamicProperty(DOOR_LIST_PROPERTY) || "[]");
            const doorPos = {
                x: block.location.x, y: block.location.y, z: block.location.z,
                dimensionId: player.dimension.id
            };

            
            const updatedLocations = doorLocations.filter(loc => !(loc.x === doorPos.x && loc.y === doorPos.y && loc.z === doorPos.z && loc.dimensionId === doorPos.dimensionId));
            world.setDynamicProperty(DOOR_LIST_PROPERTY, JSON.stringify(updatedLocations));
            
        }


        
        if (furnaces.includes(block.typeId)) {
            const furnaceLocations = JSON.parse(world.getDynamicProperty(FURNACE_LIST_PROPERTY) || "[]");
            const furnacePos = {
                x: block.location.x, y: block.location.y, z: block.location.z,
                dimensionId: player.dimension.id
            };

            
            const updatedLocations = furnaceLocations.filter(loc => !(loc.x === furnacePos.x && loc.y === furnacePos.y && loc.z === furnacePos.z && loc.dimensionId === furnacePos.dimensionId));
            world.setDynamicProperty(FURNACE_LIST_PROPERTY, JSON.stringify(updatedLocations));
            
        }
    };

    function noSleep(event) {
        if (event.block?.typeId === "minecraft:bed" && fo.forebodingScore >= fo.scoreChapter5) {
            const time = world.getTimeOfDay();

            
            if ((time >= 12542 && time < 23992)) {
                
                disableDaylightCycle();
            } else {
                
            }
        }
    };

    function disableDaylightCycle() {
        world.gameRules.doDayLightCycle = false;
        

        system.runTimeout(() => {
            world.gameRules.doDayLightCycle = true;
            
        }, 102);
    };

    function glassSaveGlass(event) {
        const block = event.block;

        if (glassBlocks.includes(block.typeId)) {
            const glassLocations = JSON.parse(world.getDynamicProperty(GLASS_LIST_PROPERTY) || "[]");
            const glassPos = { x: block.location.x, y: block.location.y, z: block.location.z, dimension: block.dimension.id };

            
            if (!glassLocations.some(loc => loc.x === glassPos.x && loc.y === glassPos.y && loc.z === glassPos.z && loc.dimension === glassPos.dimension)) {
                glassLocations.push(glassPos);
                world.setDynamicProperty(GLASS_LIST_PROPERTY, JSON.stringify(glassLocations));
                
            } else {
                
            }
        }
    };

    function glassRemoveGlass(event) {
        const block = event.block;

        if (glassBlocks.includes(block.typeId)) {

            const glassLocations = JSON.parse(world.getDynamicProperty(GLASS_LIST_PROPERTY) || "[]");
            const glassPos = { x: block.location.x, y: block.location.y, z: block.location.z, dimension: block.dimension.id };

            
            const updatedLocations = glassLocations.filter(loc => !(loc.x === glassPos.x && loc.y === glassPos.y && loc.z === glassPos.z && loc.dimension === glassPos.dimension));
            world.setDynamicProperty(GLASS_LIST_PROPERTY, JSON.stringify(updatedLocations));
            
        }
    }

    function randomTorchSaveTorch(event) {
        const block = event.block;
        if (block.typeId !== "minecraft:torch" || block.y >= 30 || block.dimension.id !== "minecraft:overworld") return;

        const list = JSON.parse(world.getDynamicProperty(TORCH_LIST_PROPERTY) || "[]");
        const pos = { x: block.location.x, y: block.location.y, z: block.location.z };

        if (!list.some(p => p.x === pos.x && p.y === pos.y && p.z === pos.z)) {
            list.push(pos);

            world.setDynamicProperty(TORCH_LIST_PROPERTY, JSON.stringify(list));
            
        }
    };

    function randomTorchRemoveTorch(event) {
        const block = event.block;

        if (block.typeId !== "minecraft:torch" || block.dimension.id !== "minecraft:overworld") return;

        const list = JSON.parse(world.getDynamicProperty(TORCH_LIST_PROPERTY) || "[]");
        const pos = { x: block.location.x, y: block.location.y, z: block.location.z };
        const updated = list.filter(p => !(p.x === pos.x && p.y === pos.y && p.z === pos.z));

        world.setDynamicProperty(TORCH_LIST_PROPERTY, JSON.stringify(updated));
        
    };


    function mineRestoreSaveBlocks(event) {
        const player = event.player;
        const blockLocation = event.block.location;
        const blockType = event.block.typeId;
        const dimensionId = event.block.dimension.id;


        const forebodingScore = fo.forebodingScore;
        if (forebodingScore >= 10 && forebodingScore < forebodingChapter04) {
            
            if (blockLocation.y < 30 && VALID_BLOCKS.includes(blockType) && dimensionId === "minecraft:overworld") { 
                const minedBlocks = JSON.parse(world.getDynamicProperty(MINED_BLOCKS_KEY) || "[]");
                minedBlocks.push({ x: blockLocation.x, y: blockLocation.y, z: blockLocation.z });
                world.setDynamicProperty(MINED_BLOCKS_KEY, JSON.stringify(minedBlocks));
                
            }
        }

    };

    function bellSpam(event) {
        const block = event.block;

        
        if (block.typeId !== "minecraft:bell") return;

        const forebodingScore = fo.forebodingScore;
        if (forebodingScore < forebodingChapter03) return;

        if (bellCooldown > 0) {
            bellCooldown--
            return
        };

        const bellKey = `${block.x},${block.y},${block.z},${block.dimension.id}`;
        const now = Date.now();

        
        if (!bellHitHistory.has(bellKey)) {
            bellHitHistory.set(bellKey, { count: 0, lastHit: 0 });
        }

        const bellData = bellHitHistory.get(bellKey);

        
        if (now - bellData.lastHit <= 4000) {
            bellData.count++;
            bellData.lastHit = now;
        } else {
            
            bellData.count = 1;
            bellData.lastHit = now;
        }

        
        if (bellData.count >= 3) {
            if (Math.random() < (1 / 5)) checkAndSpamBell(block);
            
            bellData.count = 0;
            bellCooldown = 40;
        }

    };

    
    function checkAndSpamBell(block) {
        const bellLocation = block.location;
        const dimension = block.dimension;

        const playersIn8Blocks = block.dimension.getPlayers({
            location: block.location,
            maxDistance: 5,
        });

        if (playersIn8Blocks.length > 0) {
            system.runTimeout(() => { checkAndSpamBell(block) }, 67)
            return; 
        }

        
        const playersIn32Blocks = block.dimension.getPlayers({
            location: block.location,
            minDistance: 6,
            maxDistance: 32,
        });

        if (playersIn32Blocks.length === 0) {
            system.runTimeout(() => { checkAndSpamBell(block) }, 67)
            return; 
        }

        
        const isNotBeingLookedAt = playersIn32Blocks.every(player =>
            !isPlayerLookingAtTarget(player, bellLocation)
        );

        if (!isNotBeingLookedAt) {
            system.runTimeout(() => { checkAndSpamBell(block) }, 67)
            return; 
        }

        
        spamBellSound(dimension, bellLocation);
    }

    
    function spamBellSound(dimension, location) {
        const pitches = [0.93, 0.95, 0.97];
        const numberOfHits = Math.floor(Math.random() * 2 + 4);
        const minDelay = 2; 
        const maxDelay = 4; 
        let delay = 0;

        for (let i = 0; i < numberOfHits; i++) {
            delay = delay + minDelay + (Math.random() * (maxDelay - minDelay));
            const pitch = pitches[Math.floor(Math.random() * pitches.length)];

            system.runTimeout(() => {
                const bellBlock = dimension.getBlock(location);

                if (bellBlock && bellBlock.typeId === "minecraft:bell") {
                    
                    dimension.playSound("block.bell.hit", location, {
                        pitch: pitch,
                    });

                    
                }

            }, delay);
        }
    }



}