<?php
/**
 * NIS2 Agile — Wrapper SSE per simulate-nis2.php (pattern proc_open, lg231-inspired)
 *
 * DocumentRoot è public/ → il file principale è fuori dalla web root.
 * Usa proc_open per eseguire il simulatore come sottoprocesso CLI con NIS2_SSE=1,
 * streamando ogni riga SSE al browser man mano che viene prodotta (no buffering).
 *
 * Vantaggi rispetto a require diretto:
 *   - PHP-FPM gestisce solo il proxy SSE (leggero, non può fare timeout del simulatore)
 *   - Output reale in tempo reale (nessun buffering Apache/FPM)
 *   - Errori del simulatore visibili come eventi SSE
 *
 * URL: https://nis2.agile.software/simulate-nis2.php?sim=all
 */

set_time_limit(0);
ignore_user_abort(true);
ini_set('memory_limit', '64M');

// ── SSE headers ──────────────────────────────────────────────────────────────
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('X-Accel-Buffering: no');
header('Connection: keep-alive');
ob_implicit_flush(true);
while (ob_get_level()) ob_end_flush();

// ── Parametri ────────────────────────────────────────────────────────────────
$simParam  = strtolower(trim($_GET['sim'] ?? 'all'));
$scriptPath = realpath(__DIR__ . '/../simulate-nis2.php');

if (!$scriptPath || !is_file($scriptPath)) {
    echo 'data: ' . json_encode(['t' => 'error', 'm' => 'simulate-nis2.php non trovato']) . "\n\n";
    echo 'data: ' . json_encode(['t' => 'done',  'stats' => ['pass' => 0, 'fail' => 1, 'skip' => 0, 'warn' => 0]]) . "\n\n";
    flush();
    exit;
}

// ── Ambiente subprocess ───────────────────────────────────────────────────────
// NIS2_SSE=1 → il simulatore usa output SSE anche quando php_sapi_name()='cli'
// NIS2_SIM=SIM06 → esegue solo lo scenario B2B (opzionale)
$env = [];
foreach ($_ENV ?: [] as $k => $v) {
    if (is_string($v)) $env[$k] = $v;
}
$env['NIS2_SSE'] = '1';
if ($simParam === 'sim06') {
    $env['NIS2_SIM'] = 'SIM06';
}

// ── Avvia subprocess (pattern lg231 runCommand) ───────────────────────────────
$descriptors = [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']];
$cwd  = dirname($scriptPath);
// PHP_BINARY in PHP-FPM punta a php-fpm, non al CLI → cerca il binario CLI corretto
$phpBin = PHP_BINARY;
if (str_contains($phpBin, 'fpm') || !is_executable($phpBin)) {
    foreach (['/usr/bin/php' . PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION, '/usr/bin/php', 'php'] as $try) {
        if (is_executable($try) || shell_exec("which $try 2>/dev/null")) { $phpBin = $try; break; }
    }
}
$cmd  = $phpBin . ' ' . escapeshellarg($scriptPath);
$proc = proc_open($cmd, $descriptors, $pipes, $cwd, $env);

if (!is_resource($proc)) {
    echo 'data: ' . json_encode(['t' => 'error', 'm' => 'Impossibile avviare il simulatore']) . "\n\n";
    echo 'data: ' . json_encode(['t' => 'done',  'stats' => ['pass' => 0, 'fail' => 1, 'skip' => 0, 'warn' => 0]]) . "\n\n";
    flush();
    exit;
}

fclose($pipes[0]);
stream_set_blocking($pipes[1], false);
stream_set_blocking($pipes[2], false);

// ── Streaming riga per riga ──────────────────────────────────────────────────
// Il simulatore emette direttamente eventi SSE (data: {...}\n\n).
// Il wrapper li passa al browser immediatamente senza buffering.
while (true) {
    $chunk = fread($pipes[1], 8192);
    if ($chunk !== false && $chunk !== '') {
        echo $chunk;
        flush();
    }

    // Stderr → errori PHP fatali visibili nel terminale SSE
    $err = fread($pipes[2], 1024);
    if ($err !== false && $err !== '') {
        foreach (explode("\n", trim($err)) as $errLine) {
            if (trim($errLine) !== '') {
                echo 'data: ' . json_encode(['t' => 'error', 'm' => '[stderr] ' . trim($errLine)]) . "\n\n";
            }
        }
        flush();
    }

    $status = proc_get_status($proc);
    if (!$status['running']) {
        // Leggi eventuale output residuo dopo la fine del processo
        $tail = stream_get_contents($pipes[1]);
        if ($tail) { echo $tail; flush(); }
        fclose($pipes[1]);
        fclose($pipes[2]);
        proc_close($proc);
        break;
    }

    usleep(50000); // 50ms — bilanciamento latenza/CPU (come lg231)
}
