import { world, system } from "@minecraft/server";
import { Random } from "../util/random";
import { FXSequencer } from "../util/fx";
import BIOMES from "../util/biomes";
import { sendDeveloperMessage } from "../util/debug";
import { settingEnabled } from "../settings";
import { Vector } from "../util/vector";

const MIN_DELAY_SECONDS = { loop: 5, spot: 3 };
const MAX_DELAY_SECONDS = { loop: 5, spot: 8 };

const getRandomSeconds = (type) => 20 * Random.IntegerWithinRange(MIN_DELAY_SECONDS[type], MAX_DELAY_SECONDS[type]);

const sequences = {
    loop: {
        // Forest
        forest_day: FXSequencer.register("loop_forest_day"),
        forest_night: FXSequencer.register("loop_forest_night"),
        // Beach
        beach_day: FXSequencer.register("loop_beach_day"),
        beach_night: FXSequencer.register("loop_beach_night"),
        // Plains
        plains_day: FXSequencer.register("loop_plains_day"),
        plains_night: FXSequencer.register("loop_plains_night"),
        // Desert
        desert_day: FXSequencer.register("loop_desert_day"),
        desert_night: FXSequencer.register("loop_desert_night"),
        // River
        river_day: FXSequencer.register("loop_river_day"),
        river_night: FXSequencer.register("loop_river_night"),
        // Swamp
        swamp_day: FXSequencer.register("loop_swamp_day"),
        swamp_night: FXSequencer.register("loop_swamp_night"),
        // Ocean
        ocean_day: FXSequencer.register("loop_ocean_day"),
        ocean_night: FXSequencer.register("loop_ocean_night"),
        // Mesa
        mesa_day: FXSequencer.register("loop_mesa_day"),
        mesa_night: FXSequencer.register("loop_mesa_night"),
        // Jungle
        jungle_day: FXSequencer.register("loop_jungle_day"),
        jungle_night: FXSequencer.register("loop_jungle_night"),
        // Taiga
        taiga_day: FXSequencer.register("loop_taiga_day"),
        taiga_night: FXSequencer.register("loop_taiga_night"),
        // Frozen Ocean
        frozen_ocean_day: FXSequencer.register("loop_frozen_ocean_day"),
        frozen_ocean_night: FXSequencer.register("loop_frozen_ocean_night"),
        // Mountains
        mountains_day: FXSequencer.register("loop_mountains_day"),
        mountains_night: FXSequencer.register("loop_mountains_night"),
        // Savanna
        savanna_day: FXSequencer.register("loop_savanna_day"),
        savanna_night: FXSequencer.register("loop_savanna_night")
    },
    spot: {
        // Forest
        forest_day: FXSequencer.register("spot_forest_day"),
        forest_night: FXSequencer.register("spot_forest_night"),
        // Beach
        beach_day: FXSequencer.register("spot_beach_day"),
        beach_night: FXSequencer.register("spot_beach_night"),
        // Plains
        plains_day: FXSequencer.register("spot_plains_day"),
        plains_night: FXSequencer.register("spot_plains_night"),
        // Desert
        desert_day: FXSequencer.register("spot_desert_day"),
        desert_night: FXSequencer.register("spot_desert_night"),
        // River
        river_day: FXSequencer.register("spot_river_day"),
        river_night: FXSequencer.register("spot_river_night"),
        // Swamp
        swamp_day: FXSequencer.register("spot_swamp_day"),
        swamp_night: FXSequencer.register("spot_swamp_night"),
        // Ocean
        ocean_day: FXSequencer.register("spot_ocean_day"),
        // Ocean night spots: (Empty, no sounds to register)
        // Mesa
        mesa_day: FXSequencer.register("spot_mesa_day"),
        mesa_night: FXSequencer.register("spot_mesa_night"),
        // Jungle
        jungle_day: FXSequencer.register("spot_jungle_day"),
        jungle_night: FXSequencer.register("spot_jungle_night"),
        // Taiga
        taiga_day: FXSequencer.register("spot_taiga_day"),
        taiga_night: FXSequencer.register("spot_taiga_night"),
        // Frozen Ocean
        frozen_ocean_day: FXSequencer.register("spot_frozen_ocean_day"),
        frozen_ocean_night: FXSequencer.register("spot_frozen_ocean_night"),
        // Mountains
        mountains_day: FXSequencer.register("spot_mountains_day"),
        mountains_night: FXSequencer.register("spot_mountains_night"),
        // Savanna
        savanna_day: FXSequencer.register("spot_savanna_day"),
        savanna_night: FXSequencer.register("spot_savanna_night")
    }
};

// Manually register sounds for each sequence
sequences.loop.forest_day.addSound(0, "squaredreams_realism:loop_forest_day");
sequences.loop.forest_night.addSound(0, "squaredreams_realism:loop_forest_night");
sequences.spot.forest_day.addSound(0, "squaredreams_realism:spot_forest_day");
sequences.spot.forest_night.addSound(0, "squaredreams_realism:spot_forest_night");

// Beach
sequences.loop.beach_day.addSound(0, "squaredreams_realism:loop_beach_day");
sequences.loop.beach_night.addSound(0, "squaredreams_realism:loop_beach_night");
sequences.spot.beach_day.addSound(0, "squaredreams_realism:spot_beach_day");
sequences.spot.beach_night.addSound(0, "squaredreams_realism:spot_beach_night");

// Plains
sequences.loop.plains_day.addSound(0, "squaredreams_realism:loop_plains_day");
sequences.loop.plains_night.addSound(0, "squaredreams_realism:loop_plains_night");
sequences.spot.plains_day.addSound(0, "squaredreams_realism:spot_plains_day");
sequences.spot.plains_night.addSound(0, "squaredreams_realism:spot_plains_night");

// Desert
sequences.loop.desert_day.addSound(0, "squaredreams_realism:loop_desert_day");
sequences.loop.desert_night.addSound(0, "squaredreams_realism:loop_desert_night");
sequences.spot.desert_day.addSound(0, "squaredreams_realism:spot_desert_day");
sequences.spot.desert_night.addSound(0, "squaredreams_realism:spot_desert_night");

// River
sequences.loop.river_day.addSound(0, "squaredreams_realism:loop_river_day");
sequences.loop.river_night.addSound(0, "squaredreams_realism:loop_river_night");
sequences.spot.river_day.addSound(0, "squaredreams_realism:spot_river_day");
sequences.spot.river_night.addSound(0, "squaredreams_realism:spot_river_night");

// Swamp
sequences.loop.swamp_day.addSound(0, "squaredreams_realism:loop_swamp_day");
sequences.loop.swamp_night.addSound(0, "squaredreams_realism:loop_swamp_night");
sequences.spot.swamp_day.addSound(0, "squaredreams_realism:spot_swamp_day");
sequences.spot.swamp_night.addSound(0, "squaredreams_realism:spot_swamp_night");

// Ocean
sequences.loop.ocean_day.addSound(0, "squaredreams_realism:loop_ocean_day");
sequences.loop.ocean_night.addSound(0, "squaredreams_realism:loop_ocean_night");
sequences.spot.ocean_day.addSound(0, "squaredreams_realism:spot_ocean_day");

// Mesa
sequences.loop.mesa_day.addSound(0, "squaredreams_realism:loop_mesa_day");
sequences.loop.mesa_night.addSound(0, "squaredreams_realism:loop_mesa_night");
sequences.spot.mesa_day.addSound(0, "squaredreams_realism:spot_mesa_day");
sequences.spot.mesa_night.addSound(0, "squaredreams_realism:spot_mesa_night");

// Jungle
sequences.loop.jungle_day.addSound(0, "squaredreams_realism:loop_jungle_day");
sequences.loop.jungle_night.addSound(0, "squaredreams_realism:loop_jungle_night");
sequences.spot.jungle_day.addSound(0, "squaredreams_realism:spot_jungle_day");
sequences.spot.jungle_night.addSound(0, "squaredreams_realism:spot_jungle_night");

// Taiga
sequences.loop.taiga_day.addSound(0, "squaredreams_realism:loop_taiga_day");
sequences.loop.taiga_night.addSound(0, "squaredreams_realism:loop_taiga_night");
sequences.spot.taiga_day.addSound(0, "squaredreams_realism:spot_taiga_day");
sequences.spot.taiga_night.addSound(0, "squaredreams_realism:spot_taiga_night");

// Frozen Ocean
sequences.loop.frozen_ocean_day.addSound(0, "squaredreams_realism:loop_frozen_ocean_day");
sequences.loop.frozen_ocean_night.addSound(0, "squaredreams_realism:loop_frozen_ocean_night");
sequences.spot.frozen_ocean_day.addSound(0, "squaredreams_realism:spot_frozen_ocean_day");
sequences.spot.frozen_ocean_night.addSound(0, "squaredreams_realism:spot_frozen_ocean_night");

// Mountains
sequences.loop.mountains_day.addSound(0, "squaredreams_realism:loop_mountains_day");
sequences.loop.mountains_night.addSound(0, "squaredreams_realism:loop_mountains_night");
sequences.spot.mountains_day.addSound(0, "squaredreams_realism:spot_mountains_day");
sequences.spot.mountains_night.addSound(0, "squaredreams_realism:spot_mountains_night");

// Savanna
sequences.loop.savanna_day.addSound(0, "squaredreams_realism:loop_savanna_day");
sequences.loop.savanna_night.addSound(0, "squaredreams_realism:loop_savanna_night");
sequences.spot.savanna_day.addSound(0, "squaredreams_realism:spot_savanna_day");
sequences.spot.savanna_night.addSound(0, "squaredreams_realism:spot_savanna_night");

const biomeToSequenceName = {
    loop: {
        [BIOMES.cave]: { day: "loop_cave", night: "loop_cave" },
        [BIOMES.forest]: { day: "loop_forest_day", night: "loop_forest_night" },
        [BIOMES.beach]: { day: "loop_beach_day", night: "loop_beach_night" },
        [BIOMES.plains]: { day: "loop_plains_day", night: "loop_plains_night" },
        [BIOMES.desert]: { day: "loop_desert_day", night: "loop_desert_night" },
        [BIOMES.river]: { day: "loop_river_day", night: "loop_river_night" },
        [BIOMES.swamp]: { day: "loop_swamp_day", night: "loop_swamp_night" },
        [BIOMES.ocean]: { day: "loop_ocean_day", night: "loop_ocean_night" },
        [BIOMES.mesa]: { day: "loop_mesa_day", night: "loop_mesa_night" },
        [BIOMES.jungle]: { day: "loop_jungle_day", night: "loop_jungle_night" },
        [BIOMES.taiga]: { day: "loop_taiga_day", night: "loop_taiga_night" },
        [BIOMES.frozen_ocean]: { day: "loop_frozen_ocean_day", night: "loop_frozen_ocean_night" },
        [BIOMES.mountains]: { day: "loop_mountains_day", night: "loop_mountains_night" },
        [BIOMES.savanna]: { day: "loop_savanna_day", night: "loop_savanna_night" }
    },
    spot: {
        [BIOMES.cave]: { day: "spot_cave", night: "spot_cave" },
        [BIOMES.forest]: { day: "spot_forest_day", night: "spot_forest_night" },
        [BIOMES.birch]: { day: "spot_forest_day", night: "spot_forest_night" },
        [BIOMES.beach]: { day: "spot_beach_day", night: "spot_beach_night" },
        [BIOMES.plains]: { day: "spot_plains_day", night: "spot_plains_night" },
        [BIOMES.desert]: { day: "spot_desert_day", night: "spot_desert_night" },
        [BIOMES.river]: { day: "spot_river_day", night: "spot_river_night" },
        [BIOMES.swamp]: { day: "spot_swamp_day", night: "spot_swamp_night" },
        [BIOMES.ocean]: { day: "spot_ocean_day", night: "spot_ocean_day" },
        [BIOMES.mesa]: { day: "spot_mesa_day", night: "spot_mesa_night" },
        [BIOMES.jungle]: { day: "spot_jungle_day", night: "spot_jungle_night" },
        [BIOMES.taiga]: { day: "spot_taiga_day", night: "spot_taiga_night" },
        [BIOMES.frozen_ocean]: { day: "spot_frozen_ocean_day", night: "spot_frozen_ocean_night" },
        [BIOMES.mountains]: { day: "spot_mountains_day", night: "spot_mountains_night" },
        [BIOMES.savanna]: { day: "spot_savanna_day", night: "spot_savanna_night" }
    }
};

const getTimeOfDay = () => {
    const currentTime = world.getAbsoluteTime() % 24000; // Ensure time wraps around every 24,000 ticks
    return currentTime < 12000 ? "day" : "night";
};

const isUnderGround = function(entity, maxHeight = 320) {
    let start_location = {
        x: entity.location.x,
        y: entity.location.y + 1.7,
        z: entity.location.z,
    };
    let hit = entity.dimension.getBlockFromRay(start_location, Vector.up, {
        maxDistance: maxHeight + 64,
    });
    if (hit == undefined) return false;
    return true;
};

function mainLoop(type) {
    // Return if not enabled
    if (!settingEnabled("ambient_sounds")) return;
    // Get all players
    const allPlayers = world.getAllPlayers();
    if (allPlayers.length === 0) {
        scheduleNextRun(type);
        return;
    }

    const chosenPlayer = Random.chooseFromArray(allPlayers);
    const { dimension, location } = chosenPlayer;

    // Return if player is underground
    if (location < 60 && isUnderGround(chosenPlayer)) return;

    const resultLocation = {
        x: Math.floor(location.x + Random.IntegerWithinRange(-1, 1)),
        y: Math.floor(location.y + Random.IntegerWithinRange(-1, 1)),
        z: Math.floor(location.z + Random.IntegerWithinRange(-1, 1))
    };

    const timeOfDay = getTimeOfDay();

    try {
        let biomeTester = dimension.spawnEntity("squaredreams_realism:biome_tester", resultLocation);
        if (!biomeTester || !biomeTester.isValid()) {
            sendDeveloperMessage(chosenPlayer, "§6§l[!]§e§l Realism Debug:§r§7 Biome tester entity is invalid.");
            scheduleNextRun(type);
            return;
        }
        biomeTester.triggerEvent("squaredreams_realism:query_biome");

        system.runTimeout(() => handleBiomeQuery(biomeTester, chosenPlayer, resultLocation, type, timeOfDay), 20);

    } catch (err) {
        sendDeveloperMessage(chosenPlayer, `§6§l[!]§e§l Realism Debug:§r§7 Error during biome check: ${err.message}`);
        scheduleNextRun(type);
    }
}

function handleBiomeQuery(biomeTester, chosenPlayer, resultLocation, type, timeOfDay) {
    try {
        const biomeId = biomeTester.getProperty("squaredreams_realism:biome_query");
        const sequenceName = biomeToSequenceName[type][biomeId][timeOfDay];

        if (sequenceName) {
            FXSequencer.play(sequenceName, chosenPlayer.dimension, resultLocation);
        }

        biomeTester.triggerEvent("squaredreams_realism:instant_despawn");
    } catch (err) {
        sendDeveloperMessage(chosenPlayer, `§6§l[!]§e§l Realism Debug:§r§7 Error handling biome query: ${err.message}`);
    } finally {
        scheduleNextRun(type);
    }
}

const scheduleNextRun = (type) => system.runTimeout(() => mainLoop(type), getRandomSeconds(type));

scheduleNextRun("loop");
scheduleNextRun("spot");