(function() {
    let lastTime = performance.now();

    function bootstrap() {
        if (!detectarWebGL()) {
            initOverlays();
            showIncompatible();
            return;
        }

        const canvas = document.getElementById('game-canvas');
        initScene(canvas);
        initHUD();
        initEncuentroUI();
        initOverlays();
        initJoystick();
        initControls();
        initBocadillos();
        initAudio();

        const datos = cargarPartida();
        const hayPartida = datos && (datos.nivelMaxAlcanzado > 0 || datos.ultimoNivelIdx > 0);
        actualizarBotonContinuar(!!hayPartida);

        setOverlayCallbacks({
            onEmpezar: empezarPartidaDesdeCero,
            onContinuar: continuarPartida,
            onSiguienteNivel: avanzarNivel,
            onReintentar: reintentarTrasGameOver,
            onReiniciarNivel: reiniciarNivelActual
        });

        setOnCaught((npc) => {
            iniciarEncuentro(npc).then((res) => {
                if (res && res.mortal) {
                    gameState.vidas = 0;
                    gameState.gameOver = true;
                    if (typeof stopSpeak === 'function') stopSpeak();
                    showOverlayGameOver();
                } else if (gameState.paciencia <= 0) {
                    perderVida();
                }
            });
        });

        instalarAutosave();
        requestAnimationFrame(loop);
    }

    function empezarPartidaDesdeCero() {
        resetEstadoCompleto();
        cargarNivel(0);
        gameState.started = true;
        showHUD();
        guardarPartida();
    }

    function continuarPartida() {
        const idx = Math.max(0, gameState.nivelMaxAlcanzado || 0);
        gameState.nivelIdx = idx;
        gameState.vidas = VIDAS_INICIALES;
        gameState.gameOver = false;
        gameState.runTotalMs = 0;
        gameState.runMaxLevel = idx + 1;
        cargarNivel(idx);
        gameState.started = true;
        showHUD();
    }

    function reiniciarNivelActual() {
        gameState.paused = false;
        cargarNivel(gameState.nivelIdx);
    }

    function cargarNivel(idx) {
        const nivel = getNivel(idx);
        if (!nivel) return;
        gameState.nivelIdx = idx;
        if (idx + 1 > (gameState.runMaxLevel || 0)) {
            gameState.runMaxLevel = idx + 1;
        }
        resetEstadoNivel();
        aplicarAmbiente(nivel.ambiente);
        clearBocadillos();
        buildWorld(nivel);
        spawnPlayer();
        snapCameraToPlayer();
        spawnNPCs(nivel);
        spawnPowerups(nivel);
        if (typeof startMusic === 'function') startMusic(nivel.ambiente);
    }

    function avanzarNivel() {
        const nextIdx = gameState.nivelIdx + 1;
        gameState.nivelMaxAlcanzado = Math.max(gameState.nivelMaxAlcanzado, nextIdx);
        guardarPartida();
        cargarNivel(nextIdx);
    }

    function perderVida() {
        gameState.vidas--;
        if (typeof stopSpeak === 'function') stopSpeak();
        if (gameState.vidas <= 0) {
            gameState.gameOver = true;
            showOverlayGameOver();
        } else {
            cargarNivel(gameState.nivelIdx);
            showOverlayGameOver();
        }
    }

    function reintentarTrasGameOver() {
        if (gameState.vidas <= 0) {
            resetEstadoCompleto();
            cargarNivel(0);
        }
        gameState.paused = false;
    }

    function checkVictoriaNivel() {
        const nivel = getNivel(gameState.nivelIdx);
        if (!nivel || gameState.nivelCompletado) return;
        const player = getPlayerPos();
        const home = getHomePosition();
        const dx = player.x - home.x;
        const dz = player.z - home.z;
        if (dx * dx + dz * dz < 4.0) {
            gameState.nivelCompletado = true;
            gameState.paused = true;
            const tiempo = (performance.now() - gameState.tiempoNivelInicio) / 1000;
            const prev = gameState.mejorTiempos[nivel.id];
            if (!prev || tiempo < prev) {
                gameState.mejorTiempos[nivel.id] = tiempo;
            }
            gameState.nivelMaxAlcanzado = Math.max(gameState.nivelMaxAlcanzado, gameState.nivelIdx + 1);
            guardarPartida();
            showOverlayNivelCompletado(nivel, tiempo, false);
        }
    }

    function loop(now) {
        const dtMs = Math.min(now - lastTime, 100);
        const dt = dtMs / 1000;
        lastTime = now;

        if (gameState.started && !gameState.paused && !gameState.encuentroActivo && !gameState.gameOver) {
            const input = readInput();
            updatePlayer(dt, input);
            updateNPCs(dt);
            updatePowerups(dt);
            updateCamera();
            updateBocadillos();
            updateHUD();
            checkVictoriaNivel();
            gameState.runTotalMs += dtMs;

            if (gameState.paciencia <= 0 && !gameState.encuentroActivo && !gameState.nivelCompletado) {
                perderVida();
            }
        } else if (gameState.encuentroActivo) {
            tickEncuentro(dtMs);
            updateBocadillos();
        } else if (gameState.started) {
            updateBocadillos();
        }

        renderScene();
        requestAnimationFrame(loop);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', bootstrap);
    } else {
        bootstrap();
    }
})();
