const playerCtx = {
    mesh: null,
    pos: { x: 0, y: 1.0, z: 0 },
    heading: Math.PI
};

function spawnPlayer() {
    if (playerCtx.mesh) {
        sceneCtx.scene.remove(playerCtx.mesh);
        if (playerCtx.mesh.material) playerCtx.mesh.material.dispose();
    }

    const tex = getPlayerTexture();
    const mat = new THREE.SpriteMaterial({
        map: tex,
        transparent: true,
        alphaTest: 0.5,
        fog: true
    });
    const mesh = new THREE.Sprite(mat);
    mesh.scale.set(1.4, 2.1, 1);

    const spawn = getSpawnPosition();
    playerCtx.pos.x = spawn.x;
    playerCtx.pos.y = spawn.y;
    playerCtx.pos.z = spawn.z;
    playerCtx.heading = Math.PI;

    mesh.position.set(spawn.x, spawn.y, spawn.z);
    sceneCtx.scene.add(mesh);
    playerCtx.mesh = mesh;
}

function updatePlayer(dt, input) {
    if (!playerCtx.mesh) return;

    const moviendo = input.move.x !== 0 || input.move.y !== 0;
    const puedeSprint = input.action && moviendo && gameState.paciencia > SPRINT_PACIENCIA_FLOOR && !gameState.encuentroActivo;
    if (puedeSprint) {
        gameState.paciencia = Math.max(SPRINT_PACIENCIA_FLOOR, gameState.paciencia - SPRINT_PACIENCIA_COST_PER_SEC * dt);
        gameState.sprinting = true;
        gameState.regenBloqueadoHasta = performance.now() + 600;
    } else {
        gameState.sprinting = false;
        const ahora = performance.now();
        if (ahora >= (gameState.regenBloqueadoHasta || 0) && gameState.paciencia < PACIENCIA_REGEN_CAP) {
            gameState.paciencia = Math.min(PACIENCIA_REGEN_CAP, gameState.paciencia + PACIENCIA_REGEN_PER_SEC * dt);
        }
    }
    const speed = puedeSprint ? VELOCIDAD_JUGADOR * SPRINT_MULTIPLIER : VELOCIDAD_JUGADOR;

    let fx = 0, fz = -1;
    if (typeof cameraCtx !== 'undefined' && cameraCtx.smoothedDir) {
        fx = cameraCtx.smoothedDir[0];
        fz = cameraCtx.smoothedDir[1];
        const flen = Math.sqrt(fx * fx + fz * fz) || 1;
        fx /= flen;
        fz /= flen;
    }
    const rx = -fz;
    const rz = fx;

    const moveForward = -input.move.y;
    const moveRight = input.move.x;

    const dx = (moveForward * fx + moveRight * rx) * speed * dt;
    const dz = (moveForward * fz + moveRight * rz) * speed * dt;

    if (moviendo) {
        playerCtx.heading = Math.atan2(dx, -dz);
    }

    const px = playerCtx.pos.x;
    const pz = playerCtx.pos.z;
    const nx = px + dx;
    const nz = pz + dz;

    const rPlayer = PLAYER_RADIO_COLISION;
    if (isWalkable(nx, nz, rPlayer)) {
        playerCtx.pos.x = nx;
        playerCtx.pos.z = nz;
    } else if (dx !== 0 && isWalkable(nx, pz, rPlayer)) {
        playerCtx.pos.x = nx;
    } else if (dz !== 0 && isWalkable(px, nz, rPlayer)) {
        playerCtx.pos.z = nz;
    }

    actualizarProgreso(playerCtx.pos);

    playerCtx.mesh.position.set(playerCtx.pos.x, playerCtx.pos.y, playerCtx.pos.z);
}

function getPlayerPos() {
    return playerCtx.pos;
}

function getPlayerHeading() {
    return playerCtx.heading;
}

function dañarPaciencia(amount) {
    gameState.paciencia = Math.max(0, gameState.paciencia - amount);
}

function recuperarPaciencia(amount) {
    gameState.paciencia = Math.min(PACIENCIA_INICIAL, gameState.paciencia + amount);
}
