prepare( "DELETE FROM turnero_lugar_ocupacion WHERE lugar_id = ? AND user_id = ?" )->execute([$lugarId, $userId]); jsonOk(['liberado' => true]); } /* ── ping ────────────────────────────────────────────────────── */ if ($action === 'ping') { $pdo->prepare( "UPDATE turnero_lugar_ocupacion SET ultimo_ping = NOW() WHERE lugar_id = ? AND user_id = ?" )->execute([$lugarId, $userId]); jsonOk(['ok' => true]); } /* ── aceptar (ocupante A acepta la solicitud y libera) ───────── */ if ($action === 'aceptar') { $pdo->prepare( "DELETE FROM turnero_lugar_ocupacion WHERE lugar_id = ? AND user_id = ?" )->execute([$lugarId, $userId]); jsonOk(['liberado' => true]); } /* ── denegar (ocupante A rechaza) ────────────────────────────── */ if ($action === 'denegar') { $pdo->prepare( "UPDATE turnero_lugar_ocupacion SET solicitud_resultado = 'denegada' WHERE lugar_id = ? AND user_id = ?" )->execute([$lugarId, $userId]); jsonOk(['ok' => true]); } /* ── check_solicitud (A consulta si hay solicitud pendiente) ─── */ if ($action === 'check_solicitud') { $st = $pdo->prepare( "SELECT solicitud_user_id, solicitud_nombre, solicitud_at, solicitud_resultado FROM turnero_lugar_ocupacion WHERE lugar_id = ? AND user_id = ?" ); $st->execute([$lugarId, $userId]); $row = $st->fetch(PDO::FETCH_ASSOC); if (!$row || $row['solicitud_resultado'] !== 'pendiente') { jsonOk(['hay_solicitud' => false]); } jsonOk([ 'hay_solicitud' => true, 'solicitante' => $row['solicitud_nombre'], 'solicitud_at' => $row['solicitud_at'], ]); } /* ── check_transfer (B consulta si el puesto quedó libre) ───── */ if ($action === 'check_transfer') { $st = $pdo->prepare( "SELECT user_id, solicitud_user_id, solicitud_resultado FROM turnero_lugar_ocupacion WHERE lugar_id = ?" ); $st->execute([$lugarId]); $row = $st->fetch(PDO::FETCH_ASSOC); if (!$row) { jsonOk(['disponible' => true]); } if ($row['solicitud_resultado'] === 'denegada') { jsonOk(['disponible' => false, 'denegado' => true]); } jsonOk(['disponible' => false, 'denegado' => false]); } /* ── solicitar (B pide transferencia) ───────────────────────── */ if ($action === 'solicitar') { // Limpiar expirados primero $pdo->prepare( "DELETE FROM turnero_lugar_ocupacion WHERE ultimo_ping < $timeout" )->execute(); $st = $pdo->prepare( "SELECT user_id, user_nombre, solicitud_at, solicitud_user_id FROM turnero_lugar_ocupacion WHERE lugar_id = ?" ); $st->execute([$lugarId]); $ocupado = $st->fetch(PDO::FETCH_ASSOC); if (!$ocupado || (int)$ocupado['user_id'] === $userId) { // Ya libre o soy yo — reclamar directamente $pdo->prepare( "INSERT INTO turnero_lugar_ocupacion (lugar_id, user_id, user_nombre, ultimo_ping) VALUES (?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE user_id=?, user_nombre=?, ultimo_ping=NOW(), solicitud_user_id=NULL, solicitud_nombre=NULL, solicitud_at=NULL, solicitud_resultado=NULL" )->execute([$lugarId, $userId, $nombre, $userId, $nombre]); jsonOk(['transferido' => true]); } // Guardar solicitud (o renovarla si ya existía de este mismo B) $pdo->prepare( "UPDATE turnero_lugar_ocupacion SET solicitud_user_id = ?, solicitud_nombre = ?, solicitud_at = NOW(), solicitud_resultado = 'pendiente' WHERE lugar_id = ?" )->execute([$userId, $nombre, $lugarId]); jsonOk(['solicitado' => true, 'ocupado_por' => $ocupado['user_nombre']]); } /* ── disponibles (puestos del mismo tipo sin ocupante) ───────── */ if ($action === 'disponibles') { $stLugar = $pdo->prepare("SELECT tipo FROM turnero_lugares WHERE id = ?"); $stLugar->execute([$lugarId]); $lugar = $stLugar->fetch(PDO::FETCH_ASSOC); if (!$lugar) jsonError('Lugar no encontrado.', 404); $stDisp = $pdo->prepare( "SELECT l.id, l.nombre FROM turnero_lugares l WHERE l.tipo = ? AND l.activo = 1 AND l.id != ? AND NOT EXISTS ( SELECT 1 FROM turnero_lugar_ocupacion o WHERE o.lugar_id = l.id AND o.ultimo_ping >= $timeout ) ORDER BY l.sort_order ASC" ); $stDisp->execute([$lugar['tipo'], $lugarId]); jsonOk(['disponibles' => $stDisp->fetchAll(PDO::FETCH_ASSOC)]); } /* ── claim ───────────────────────────────────────────────────── */ // Limpiar ocupaciones expiradas $pdo->prepare( "DELETE FROM turnero_lugar_ocupacion WHERE ultimo_ping < $timeout" )->execute(); // Verificar si hay un ocupante $st = $pdo->prepare( "SELECT user_id, user_nombre, solicitud_user_id, solicitud_at, solicitud_resultado FROM turnero_lugar_ocupacion WHERE lugar_id = ?" ); $st->execute([$lugarId]); $ocupado = $st->fetch(PDO::FETCH_ASSOC); if ($ocupado && (int)$ocupado['user_id'] !== $userId) { // Si hay una solicitud de este usuario pendiente hace más de 6s → auto-liberar $esMiSolicitud = (int)($ocupado['solicitud_user_id'] ?? 0) === $userId; $solicitudVieja = $ocupado['solicitud_at'] && (strtotime($ocupado['solicitud_at']) < time() - 6); if ($esMiSolicitud && $solicitudVieja) { $pdo->prepare("DELETE FROM turnero_lugar_ocupacion WHERE lugar_id = ?") ->execute([$lugarId]); $ocupado = null; // caer al INSERT/UPDATE de abajo } else { jsonOk([ 'disponible' => false, 'ocupado_por' => $ocupado['user_nombre'], ]); } } // Reclamar o renovar $pdo->prepare( "INSERT INTO turnero_lugar_ocupacion (lugar_id, user_id, user_nombre, ultimo_ping) VALUES (?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE user_id=?, user_nombre=?, ultimo_ping=NOW(), solicitud_user_id=NULL, solicitud_nombre=NULL, solicitud_at=NULL, solicitud_resultado=NULL" )->execute([$lugarId, $userId, $nombre, $userId, $nombre]); jsonOk(['disponible' => true]);