<?php
/**
 * GET /modules/turnero/api/sse_turno.php
 * Server-Sent Events — actualiza pantallas TV y kiosko en tiempo real.
 * No requiere autenticación (acceso público).
 *
 * Query params:
 *   area      string  opcional  "recepcion" | "lugar"  (default: "recepcion")
 *   lugar_id  int     requerido si area = "lugar"
 *
 * Eventos emitidos:
 *   cola_update   — estado completo de la cola cada vez que haya cambio o cada 30 s
 *   ping          — comentario de keep-alive cada 15 s
 */

// ── Asegurar que la columna sse_ping_at exista (idempotente) ──
require_once __DIR__ . '/_helpers.php';

$pdo = db();
$pdo->exec(
    "ALTER TABLE turnero_sesiones
     ADD COLUMN IF NOT EXISTS sse_ping_at DATETIME NULL DEFAULT NULL"
);

// ── Headers SSE ───────────────────────────────────────────────
@set_time_limit(0);
@ini_set('output_buffering', 'off');
@ini_set('zlib.output_compression', false);

header('Content-Type: text/event-stream; charset=UTF-8');
header('Cache-Control: no-store, no-cache');
header('X-Accel-Buffering: no'); // Nginx: deshabilitar buffering
header('Connection: keep-alive');

// El arranque del ERP deja un búfer de salida activo (los jsonOk() de
// _helpers hacen ob_clean(), que lo confirma). flush() a secas no lo
// atraviesa: TODO lo emitido quedaba atrapado y el cliente recibía cero
// bytes, para siempre. Por eso este endpoint parecía muerto en producción.
while (ob_get_level() > 0) { @ob_end_clean(); }
@ob_implicit_flush(true);

// ── Parámetros ────────────────────────────────────────────────
$area    = isset($_GET['area'])     ? trim($_GET['area'])     : 'recepcion';
$lugarId = isset($_GET['lugar_id']) ? (int) $_GET['lugar_id'] : null;

if (!in_array($area, ['recepcion', 'lugar'], true)) {
    $area = 'recepcion';
}

// ── Helpers de emisión ────────────────────────────────────────
function sseEvent(string $event, mixed $payload): void
{
    echo "event: {$event}\n";
    echo 'data: ' . json_encode($payload, JSON_UNESCAPED_UNICODE) . "\n\n";
    flush();
}

function ssePing(): void
{
    echo ": ping\n\n";
    flush();
}

// ── Función que arma el snapshot de la cola ───────────────────
function buildSnapshot(PDO $pdo, int $sesionId, string $area, ?int $lugarId): array
{
    if ($area === 'recepcion') {
        $estadosCola   = ["'espera'", "'en_recepcion'"];
        $filtroLugar   = '';
        $bindsCola     = [$sesionId];
        $estadoActivo  = "'en_recepcion'";
        $campoLlamado  = 'llamado_recepcion_at';
        $bindActivo    = [$sesionId];
        $filtroActivo  = '';
    } else {
        $estadosCola   = ["'en_espera_lugar'", "'en_servicio'"];
        $filtroLugar   = 'AND t.lugar_destino_id = ?';
        $bindsCola     = [$sesionId, $lugarId];
        $estadoActivo  = "'en_servicio'";
        $campoLlamado  = 'llamado_lugar_at';
        $bindActivo    = [$sesionId, $lugarId];
        $filtroActivo  = 'AND t.lugar_destino_id = ?';
    }

    $inEstados = implode(', ', $estadosCola);

    $stmt = $pdo->prepare(
        "SELECT t.id, t.codigo, t.numero, t.estado, t.paciente_nombre, t.creado_at,
                p.codigo AS prioridad_codigo, p.nombre AS prioridad_nombre,
                p.color AS prioridad_color, p.orden_peso
           FROM turnero_turnos t
           JOIN turnero_prioridades p ON p.id = t.prioridad_id
          WHERE t.sesion_id = ?
            AND t.estado IN ({$inEstados})
            {$filtroLugar}
          ORDER BY p.orden_peso ASC, t.creado_at ASC"
    );
    $stmt->execute($bindsCola);
    $cola = $stmt->fetchAll(PDO::FETCH_ASSOC);

    $stmt = $pdo->prepare(
        "SELECT t.id, t.codigo, t.numero, t.estado, t.paciente_nombre,
                t.{$campoLlamado} AS llamado_at,
                p.codigo AS prioridad_codigo, p.nombre AS prioridad_nombre,
                p.color AS prioridad_color
           FROM turnero_turnos t
           JOIN turnero_prioridades p ON p.id = t.prioridad_id
          WHERE t.sesion_id = ?
            AND t.estado = {$estadoActivo}
            {$filtroActivo}
          ORDER BY t.{$campoLlamado} DESC
          LIMIT 1"
    );
    $stmt->execute($bindActivo);
    $activo = $stmt->fetch(PDO::FETCH_ASSOC) ?: null;

    return [
        'activo'    => $activo,
        'cola'      => $cola,
        'timestamp' => date('c'),
    ];
}

// ── Loop principal ────────────────────────────────────────────
$sesionId    = obtenerOCrearSesionHoy();
$lastKeepalive = time();
$lastEmit    = 0;

// Emitir estado inicial inmediatamente
$snapshot = buildSnapshot($pdo, $sesionId, $area, $lugarId);
sseEvent('cola_update', $snapshot);

// Inicializar $lastPing con el valor ACTUAL (no null) para que la
// primera iteración del loop no emita un snapshot extra sin cambios
$stmt = $pdo->prepare('SELECT sse_ping_at FROM turnero_sesiones WHERE id = ?');
$stmt->execute([$sesionId]);
$lastPing = $stmt->fetchColumn() ?: null;

while (!connection_aborted()) {
    sleep(1);

    // Leer el último sse_ping_at de la sesión
    $stmt = $pdo->prepare('SELECT sse_ping_at FROM turnero_sesiones WHERE id = ?');
    $stmt->execute([$sesionId]);
    $row = $stmt->fetch(PDO::FETCH_ASSOC);

    $pingActual = $row['sse_ping_at'] ?? null;

    // Emitir si hubo cambio (notificarSSE actualiza sse_ping_at) o cada 30 s
    $ahora = time();
    $forzar = ($ahora - $lastEmit) >= 30;

    if ($pingActual !== $lastPing || $forzar) {
        $snapshot = buildSnapshot($pdo, $sesionId, $area, $lugarId);
        sseEvent('cola_update', $snapshot);
        $lastPing = $pingActual;
        $lastEmit = $ahora;
    }

    // Keep-alive cada 15 s
    if (($ahora - $lastKeepalive) >= 15) {
        ssePing();
        $lastKeepalive = $ahora;
    }
}
