import { system, world, GameMode } from "@minecraft/server";
import { addIslandGen } from "../../compiled";

const playersInPortal = new Map();
const originalGamemode = new Map();

let islandGenQueue = [];
const RETRY_DELAY = 100; // ticks to wait before retrying failed generation
const MAX_RETRIES = 3;

// Initialize scoreboard objectives
world.afterEvents.worldInitialize.subscribe(() => {
    const sb = world.scoreboard;
    try {
        if (!sb.getObjective("gamemode")) {
            sb.addObjective("gamemode", "Gamemode Tracker");
        }
        // Adds originalGamemode objective for backup tracking
        if (!sb.getObjective("originalGamemode")) {
            sb.addObjective("originalGamemode", "Original Gamemode");
        }
    } catch (e) {
        console.warn(`Failed to initialize scoreboard objectives: ${e}`);
    }
});

// Function to update gamemode score in scoreboard
function updateGamemodeScore(player) {
    const gm = player.getGameMode();
    let value = -1;

    switch (gm) {
        case GameMode.survival: value = 0; break;
        case GameMode.creative: value = 1; break;
        case GameMode.adventure: value = 2; break;
        case GameMode.spectator: value = 3; break;
    }

    try {
        const obj = world.scoreboard.getObjective("gamemode");
        if (obj) obj.setScore(player, value);
    } catch (e) {
        console.warn(`Failed to set gamemode score for ${player.nameTag}: ${e}`);
    }
}

// Function to store original gamemode
function storeOriginalGamemode(player) {
    const name = player.name;
    const originalMode = player.getGameMode();
    originalGamemode.set(name, originalMode);
    
    try {
        const origObj = world.scoreboard.getObjective("originalGamemode");
        if (origObj) {
            let gamemodeValue = 0;
            switch (originalMode) {
                case GameMode.survival: gamemodeValue = 0; break;
                case GameMode.creative: gamemodeValue = 1; break;
                case GameMode.adventure: gamemodeValue = 2; break;
                case GameMode.spectator: gamemodeValue = 3; break;
            }
            origObj.setScore(player, gamemodeValue);
        }
    } catch (e) {
        console.warn(`Failed to set original gamemode score: ${e}`);
    }
}

// Function to restore original gamemode
function restoreOriginalGamemode(player) {
    const name = player.name;
    
    // Try to restore from Map first
    const originalMode = originalGamemode.get(name);
    if (originalMode !== undefined) {
        player.setGameMode(originalMode);
        originalGamemode.delete(name);
    } else {
        // Fallback: restore from scoreboard
        try {
            const origObj = world.scoreboard.getObjective("originalGamemode");
            if (origObj) {
                const score = origObj.getScore(player);
                if (score !== undefined) {
                    switch (score) {
                        case 0: player.setGameMode(GameMode.survival); break;
                        case 1: player.setGameMode(GameMode.creative); break;
                        case 2: player.setGameMode(GameMode.adventure); break;
                        case 3: player.setGameMode(GameMode.spectator); break;
                    }
                }
            }
        } catch (e) {
            console.warn(`Failed to restore gamemode from scoreboard: ${e}`);
            player.setGameMode(GameMode.survival); // Final fallback
        }
    }
    
    // Clean up scoreboard entry
    try {
        const origObj = world.scoreboard.getObjective("originalGamemode");
        if (origObj) {
            origObj.removeParticipant(player);
        }
    } catch (e) {
        console.warn(`Failed to clean up original gamemode scoreboard: ${e}`);
    }
}

// Structure to track generation state and retries
class IslandGenState {
    constructor(generator, playerLocation) {
        this.generator = generator;
        this.playerLocation = playerLocation;
        this.retryCount = 0;
        this.retryAfter = 0;
        this.isProcessing = false;
        this.isComplete = false;
        this.hasFailed = false;
        this.jobStarted = false;
    }
    
    shouldRetry() {
        return this.retryCount < MAX_RETRIES;
    }
    
    incrementRetry() {
        this.retryCount++;
        this.retryAfter = system.currentTick + RETRY_DELAY;
        this.hasFailed = false;
        this.jobStarted = false;
        this.isProcessing = false;
    }
    
    canProcess() {
        return !this.jobStarted && !this.isProcessing && system.currentTick >= this.retryAfter;
    }
}

function generateIslands(playerLocation) {
    try {
        const ig = addIslandGen(playerLocation);
        const genState = new IslandGenState(ig, playerLocation);
        islandGenQueue.push(genState);
    } catch (error) {
        console.warn("Failed to create island generator:", error);
    }
}

// Use system.runJob for proper yielding between islands
function* processIslandGeneration(genState) {
    const { generator } = genState;
    
    while (true) {
        try {
            const nextLocation = generator.getNextStructureLocation();
            if (!nextLocation) {
                // No more structures to generate, we're done
                genState.isComplete = true;
                return;
            }
            
            genState.isProcessing = true;
            const success = generator.generateNext();
            genState.isProcessing = false;
            
            if (!success) {
                // Generation failed
                genState.hasFailed = true;
                return;
            }
            //TO SAVE LOCATION
            // Yield after each island to prevent server lag
            yield;
            
        } catch (error) {
            genState.isProcessing = false;
            genState.hasFailed = true;
            console.warn("Error during island generation:", error);
            return;
        }
    }
}

// Optimized processing system that handles one island at a time
system.runInterval(() => {
    if (islandGenQueue.length === 0) return;

    for (let i = islandGenQueue.length - 1; i >= 0; i--) {
        const genState = islandGenQueue[i];
        
        // Check if job is complete
        if (genState.isComplete) {
            // Successfully completed all islands
            islandGenQueue.splice(i, 1);
            continue;
        }
        
        // Check if job failed
        if (genState.hasFailed) {
            if (genState.shouldRetry()) {
                genState.incrementRetry();
                console.log(`Island generation failed, retrying in ${RETRY_DELAY} ticks (attempt ${genState.retryCount}/${MAX_RETRIES})`);
            } else {
                // Max retries reached, remove from queue
                console.warn("Island generation failed after max retries, removing from queue");
                islandGenQueue.splice(i, 1);
            }
            continue;
        }
        
        // Start generation if ready and not already started
        if (genState.canProcess()) {
            try {
                genState.jobStarted = true;
                system.runJob(processIslandGeneration(genState));
            } catch (error) {
                console.error("Error starting island generation job:", error);
                genState.hasFailed = true;
                genState.jobStarted = false;
            }
        }
    }
}, 5);

world.beforeEvents.worldInitialize.subscribe(({ blockComponentRegistry }) => {
    blockComponentRegistry.registerCustomComponent("gs_ce:resin_portal", {
        onTick: ({ block }) => {
            const location = block.location;
            const dimension = block.dimension;

            const isBlockType = (block, type) => block && block.typeId === type;

            const fireLocations = [
                { x: location.x, y: location.y + 1, z: location.z },
                { x: location.x - 1, y: location.y + 1, z: location.z },
                { x: location.x + 1, y: location.y + 1, z: location.z },
            ];

            let fireDetected = false;
            for (const fireLocation of fireLocations) {
                const fireBlock = dimension.getBlock(fireLocation);
                if (isBlockType(fireBlock, "minecraft:fire")) {
                    fireDetected = true;
                    break;
                }
            }

            if (fireDetected) {
                for (let y = location.y + 1; y <= location.y + 4; y++) {
                    for (let offset = -1; offset <= 1; offset++) {
                        const portalLocation = {
                            x: location.x + offset,
                            y,
                            z: location.z,
                        };
                        dimension.getBlock(portalLocation)?.setType("gs_ce:portal");
                    }
                }
            }

            const radius = 5;
            const playersNearby = dimension.getEntities({
                type: "minecraft:player",
                location: location,
                maxDistance: radius,
            });

            playersNearby.forEach((player) => {
                const playerLocation = player.location;
                const blockAtPlayer = dimension.getBlock({
                    x: Math.floor(playerLocation.x),
                    y: Math.floor(playerLocation.y),
                    z: Math.floor(playerLocation.z),
                });

                const isInPortal = isBlockType(blockAtPlayer, "gs_ce:portal");

                if (isInPortal) {
                    if (!playersInPortal.get(player.id)) {
                        // Store original gamemode before portal activation
                        storeOriginalGamemode(player);
                        updateGamemodeScore(player);
                        
                        player.runCommandAsync('/camera @s fade time 0 10 2');
                        generateIslands(playerLocation);
                        block.setType("gs_ce:warped_resin");
                        player.runCommandAsync('/tag @s add cinematic');
                    }
                    playersInPortal.set(player.id, true);
                } else {
                    playersInPortal.set(player.id, false);
                }
            });
        },
    });
});