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




export function initFog() {

    const overworld = fo.overworld;
    const nether = fo.nether;
    const the_end = fo.theEnd;
    const forebodingObjective = fo.forebodingObjective;

    const
        scoreChapter2 = fo.scoreChapter2,
        scoreChapter3 = fo.scoreChapter3,
        scoreChapter4 = fo.scoreChapter4,
        scoreChapter5 = fo.scoreChapter5,
        scoreChapter7 = fo.scoreChapter7;


    const fogAdjustmentScores = [
        Math.round(scoreChapter3 * 0.13888),
        Math.round(scoreChapter3 * 0.27777),
        Math.round(scoreChapter3 * 0.41666),
        Math.round(scoreChapter3 * 0.55555),
        Math.round(scoreChapter3 * 0.69444),
        Math.round(scoreChapter3 * 0.83333)
    ]

    const fogRanges = [
        { from: 1, to: fogAdjustmentScores[0] - 1, render: "fog01r", fixed: "fog01f", score: 1 },
        { from: fogAdjustmentScores[0], to: fogAdjustmentScores[1] - 1, render: "fog01r", fixed: "fog02f", score: 2 },
        { from: fogAdjustmentScores[1], to: fogAdjustmentScores[2] - 1, render: "fog01r", fixed: "fog03f", score: 3 },
        { from: fogAdjustmentScores[2], to: fogAdjustmentScores[3] - 1, render: "fog01r", fixed: "fog04f", score: 4 },
        { from: fogAdjustmentScores[3], to: fogAdjustmentScores[4] - 1, render: "fog01r", fixed: "fog05f", score: 5 },
        { from: fogAdjustmentScores[4], to: fogAdjustmentScores[5] - 1, render: "fog01r", fixed: "fog06f", score: 6 },
        { from: fogAdjustmentScores[5], to: scoreChapter3 - 1, render: "fog01r", fixed: "fog07f", score: 7 },
        { from: scoreChapter3, to: scoreChapter4 - 1, render: "fog03r", fixed: "fog09f", score: 8 },
        { from: scoreChapter4, to: scoreChapter5 - 1, render: "fog05r", fixed: "fog11f", score: 9 },
        { from: scoreChapter5, to: Infinity, render: "fog07", fixed: "fog07", score: 10 },
        { from: -Infinity, to: scoreChapter7, render: "fog08b", fixed: "fog08b", score: -1 }
    ];

    const fogRangesUnderground = [
        { from: 1, to: fogAdjustmentScores[0] - 1, render: "fog01ru", fixed: "fog01fu", score: 11 },
        { from: 1, to: fogAdjustmentScores[1] - 1, render: "fog01ru", fixed: "fog02fu", score: 12 },
        { from: 1, to: fogAdjustmentScores[3] - 1, render: "fog01ru", fixed: "fog03fu", score: 13 },
        { from: 1, to: fogAdjustmentScores[4] - 1, render: "fog01ru", fixed: "fog04fu", score: 14 },
        { from: 1, to: fogAdjustmentScores[5] - 1, render: "fog01ru", fixed: "fog05fu", score: 15 },
        { from: 1, to: scoreChapter3 - 1, render: "fog01ru", fixed: "fog06fu", score: 16 },
        { from: 1, to: scoreChapter5 - 1, render: "fog01ru", fixed: "fog07fu", score: 17 },
        { from: scoreChapter3, to: scoreChapter4 - 1, render: "fog03ru", fixed: "fog09fu", score: 18 },
        { from: scoreChapter4, to: scoreChapter5 - 1, render: "fog05ru", fixed: "fog11fu", score: 19 },
        { from: scoreChapter5, to: Infinity, render: "fog07u", fixed: "fog07u", score: 20 },
        { from: -Infinity, to: scoreChapter7, render: "fog08ub", fixed: "fog08ub", score: -2 }
    ];


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

    function getFogLevel(score, isUnderground) {
        const fogSet = isUnderground ? fogRangesUnderground : fogRanges;
        return fogSet.find(level => score >= level.from && score <= level.to) || fogSet[0];
    }

    
    function removeFog(player) {
        if (fo.forebodingScore > 0 || fo.forebodingScore < -1) return;

        const currentForebodingScore = forebodingObjective?.getScore(player) || 0;

        if (currentForebodingScore !== 0) {
            player.runCommand(`fog @s remove Fog`);
            player.runCommand(`fog @s remove FogR`);
            player.runCommand(`fog @s remove FogF`);

            forebodingObjective.setScore(player, 0)

        }

    };


    function applyFog(player) {
        const playerDim = player.dimension;
        

        
        if (fo.forebodingScore === 0 || fo.forebodingScore === -1) {
            
            return;
        }

        
        if (playerDim === nether || playerDim === the_end) {
            const currentScore = forebodingObjective?.getScore(player) || 0;
            if (currentScore !== 0) {
                
                player.runCommand(`scoreboard players set @s Foreboding 0`);
                player.runCommand(`fog @s remove FogR`);
                player.runCommand(`fog @s remove FogF`);
            }
            return;
        }

        const y = Math.floor(player.location.y);
        const isUnderground = y < 30;
        const fogData = getFogLevel(fo.forebodingScore, isUnderground);

        
        const currentForebodingScore = forebodingObjective?.getScore(player) || 0;

        

        
        if (currentForebodingScore !== fogData.score) {
            
            player.runCommand(`scoreboard players set @s Foreboding ${fogData.score}`);
            player.runCommand(`fog @s remove FogR`);
            player.runCommand(`fog @s remove FogF`);
            player.runCommand(`fog @s push foreboding:${fogData.render} FogR`);
            

            if (player.clientSystemInfo.maxRenderDistance > 32) { 
                player.runCommand(`fog @s push foreboding:${fogData.fixed} FogF`);
                
            }

            
            if (nightFogEvent) {
                const timeOfDay = world.getTimeOfDay();

                if (timeOfDay > 13670 && timeOfDay < 22000) {
                    player.runCommand(`fog @s push foreboding:fog08ub FogR`);
                }
            }

            
            if (ashFogEvent) {
                const timeOfDay = world.getTimeOfDay();

                if (timeOfDay <= 11000 && y >= 30) {
                    player.runCommand(`fog @s push foreboding:fog08b FogR`);

                }
            }


        } else {
            
        }
    }


    system.runInterval(() => {
        for (let player of world.getPlayers()) {
            applyFog(player);
            removeFog(player);
        }
    }, 10); 



    

    const
        fogEventchance = 1 / 180, 

        forebodingCheck = fo.scoreChapter5, 
        FogEventInterval = 1213; 

    let
        nightFogEvent = false, 
        nightFogEvent2 = false, 

        ashFogEvent = false, 
        ashFogEvent2 = false, 

        weatherChangeSubscription; 

    function applyNightFog(player) {
        player.runCommand(`fog @s push foreboding:fog08ub FogR`);

    };

    function RemoveNightFog(player) {
        player.runCommand(`fog @s remove FogR`);
        player.runCommand(`fog @s remove FogF`);
        player.runCommand(`scoreboard players set @s Foreboding 0`);

    };

    function applyAshFog(player) {
        if (player.location.y < 30) return;
        player.runCommand(`fog @s push foreboding:fog08b FogR`);
        system.runTimeout(() => { player.playSound("ambient.cave", player.location) }, 200)

    };

    function RemoveAshFog(player) {
        if (player.location.y < 30) return;
        player.runCommand(`fog @s remove FogR`);
        player.runCommand(`fog @s remove FogF`);
        player.runCommand(`scoreboard players set @s Foreboding 0`);

    };

    
    function handleWeatherChange() {
        
        weatherChangeSubscription = world.beforeEvents.weatherChange.subscribe((event) => {
            
            if ((event.newWeather === "rain" || event.newWeather === "thunder")) {
                
                event.cancel = true;
                
            }
        });
    }

    
    function unsubscribeWeatherChange() {
        if (weatherChangeSubscription) {
            
            world.beforeEvents.weatherChange.unsubscribe(weatherChangeSubscription);
            weatherChangeSubscription = undefined; 
            
        }
    }

    system.runInterval(() => {

        

        if (!nightFogEvent) { 
            const probabilityModifier = Math.min(fo.netProbabilityModifier, 1.5);

            if (fo.forebodingScore > forebodingCheck && Math.random() <= (fogEventchance * probabilityModifier))
                nightFogEvent = true;

            
        };

        if (nightFogEvent) { 
            const OVERWORLDPLAYERS = overworld.getPlayers();

            const timeOfDay = world.getTimeOfDay();

            if (!nightFogEvent2 && timeOfDay >= 13670 && timeOfDay <= 22000) {
                nightFogEvent2 = true 

                

                if (OVERWORLDPLAYERS.length === 0) return; 
                for (let player of OVERWORLDPLAYERS) {
                    applyNightFog(player);
                }



            } else if (nightFogEvent2) {
                if (timeOfDay < 13670 || timeOfDay > 22000) { 
                    nightFogEvent = false;
                    nightFogEvent2 = false;

                    

                    if (OVERWORLDPLAYERS.length === 0) return; 
                    for (let player of OVERWORLDPLAYERS) {
                        RemoveNightFog(player);
                    }

                }
            }
        };

        

        if (!ashFogEvent) { 

            const probabilityModifier = Math.min(fo.netProbabilityModifier, 1.5);

            if (fo.forebodingScore > forebodingCheck && Math.random() <= (fogEventchance * probabilityModifier))
                ashFogEvent = true;

            
        };


        if (ashFogEvent) { 
            const OVERWORLDPLAYERS = overworld.getPlayers();

            const timeOfDay = world.getTimeOfDay();

            if (!ashFogEvent2 && timeOfDay <= 11000) {
                ashFogEvent2 = true 

                overworld.setWeather("Clear")
                handleWeatherChange(); 

                

                if (OVERWORLDPLAYERS.length === 0) return; 
                for (let player of OVERWORLDPLAYERS) {
                    applyAshFog(player);
                }



            } else if (ashFogEvent2) {
                if (timeOfDay > 11000) { 
                    ashFogEvent = false;
                    ashFogEvent2 = false;

                    unsubscribeWeatherChange(); 

                    

                    if (OVERWORLDPLAYERS.length === 0) return; 
                    for (let player of OVERWORLDPLAYERS) {
                        RemoveAshFog(player);
                    }

                }
            }
        };


    }, FogEventInterval)

    
    system.runInterval(() => {
        if (!ashFogEvent2) return;

        
        const players = [...overworld.getPlayers()].filter(player => player.location.y > 40);

        
        const processedPlayers = new Set();

        for (const player of players) {
            
            if (processedPlayers.has(player)) continue;

            
            const nearbyPlayers = [...world.getPlayers()].filter(
                p => p !== player &&
                    p.location.y > 40 && 
                    getDistance(p.location, player.location) <= 40 
            );

            
            nearbyPlayers.push(player);

            
            const targetPlayer = nearbyPlayers.reduce((highestPlayer, currentPlayer) => {
                return currentPlayer.location.y > highestPlayer.location.y ? currentPlayer : highestPlayer;
            }, nearbyPlayers[0]);

            
            overworld.spawnParticle("fo_ashes", targetPlayer.location);
            

            
            nearbyPlayers.forEach(p => processedPlayers.add(p));
        }
    }, 5); 


}

