<?php
// ═══════════════════════════════════════════════════════════════════════════════════════════════
// PURPOSE
//   Header snapshot. The small always-visible summary carried across every tab — CPU, RAM,
//   fallback state, partner peers, and active stream and transcode counts.
//
// OPERATIONAL MODEL
//   This is the most frequently requested endpoint in the plugin: it is polled from whichever
//   tab happens to be open, continuously, for as long as the page is open. Everything about
//   it is shaped by that. Each field is the cheapest available answer to its question, not
//   the most complete one — monitor.php exists for the complete one.
//
// DESIGN PRINCIPLES
//   Shares the CPU baseline rather than sampling its own.
//       vv_cpu_per_core() keeps its counters in /tmp/vv_cpu_stat.json, and reading through it
//       means the header and the monitor page report the same number. Two independent
//       samplers would drift apart and produce a visible disagreement between the header and
//       the page directly below it.
//
//   Fallback state is read from the file, never derived.
//       A direct read of fallback_state.db with no exec and no SSH. The authoritative answer
//       is the one fallback.sh wrote; recomputing it here would be both slower and capable of
//       disagreeing with the process that actually controls failover.
//
//   Media counts are cached 30 seconds, everything else is live.
//       Streams are the only field that costs HTTP round trips to another service. Caching
//       just that keeps the poll cheap without making CPU or RAM stale, and 30s is well under
//       the time it takes anyone to notice a stream started.
//
//   Peers exclude self.
//       is_me is filtered out here rather than in the UI, so every consumer of this payload
//       gets the same definition of "peers" and none of them can forget to apply it.
//
// OPERATIONAL SAFEGUARDS
//   Read-only. Nothing here starts, stops, or changes anything.
//
//   The fallback read degrades to a named unknown.
//       @file_get_contents with a ?: '' fallback, and the parsed state defaults to 'UNKNOWN'.
//       A missing or unreadable state file shows UNKNOWN in the header rather than NORMAL —
//       reporting healthy for a fallback process that is not running is the one error this
//       field must never make.
//
//   Division is guarded.
//       ram_total_mb > 0 is checked before the percentage, so a failed meminfo read yields 0
//       rather than a division by zero that would fatal on every tab at once.
//
//   Every count is cast on the way out.
//       (int) on the cached stream and transcode counts with ?? 0 defaults, so a cache file
//       written by an older schema cannot put a null or a string into the header payload.
//
//   Media failures are already contained upstream — vv_media_sessions() time-boxes each
//   request at 3s and returns an empty session list on any failure, so an unreachable Emby
//   costs this poll nothing beyond that timeout, once every 30 seconds.
//
// REQUEST
//   GET, no parameters
//
// RESPONSE
//   {"cpu_pct","ram_pct","ram_used_mb","ram_total_mb","fallback","partner_enabled",
//    "peers":[…],"stream_count","transcode_count"}
//
// DEPENDS ON
//   include/common.php    vv_cpu_per_core(), vv_system_resources()
//                         (loaded transitively through include/monitor.php)
//   include/monitor.php   vv_partner_state()
//   include/config.php    vv_parse_kv_db(), vv_cache_read(), vv_cache_write(), STATE_DIR
//   include/media.php     vv_media_sessions()
//   STATE_DIR             fallback_state.db — written by Fallback/fallback.sh
// ═══════════════════════════════════════════════════════════════════════════════════════════════
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/monitor.php';
require_once dirname(__DIR__) . '/include/media.php';

// CPU% — shares /tmp/vv_cpu_stat.json with vv_cpu_per_core() so both read the same baseline
$cpuPct = vv_cpu_per_core()['overall'] ?? 0;

// RAM%
$res        = vv_system_resources();
$ramTotalMb = $res['ram_total_mb'];
$ramUsedMb  = $ramTotalMb - $res['ram_free_mb'];
$ramPct     = $ramTotalMb > 0 ? (int)round($ramUsedMb / $ramTotalMb * 100) : 0;

// Fallback state (fast file read, no exec)
$fbRaw         = @file_get_contents(STATE_DIR . '/fallback_state.db') ?: '';
$fbData        = vv_parse_kv_db($fbRaw);
$fallbackState = $fbData['state'] ?? 'UNKNOWN';

// Partner
$partner = vv_partner_state();
$peers   = array_values(array_filter($partner['hosts'], fn($h) => !$h['is_me']));

// Media sessions — cached 30s so the HTTP calls don't hold up every snapshot poll
$mediaCache     = vv_cache_read('snap_media', 30);
if (!$mediaCache) {
    $media      = vv_media_sessions();
    $mediaCache = [
        'stream_count'    => count($media['sessions']),
        'transcode_count' => count(array_filter($media['sessions'], fn($s) => !empty($s['is_tc']))),
    ];
    vv_cache_write('snap_media', $mediaCache);
}
$streamCount    = (int)($mediaCache['stream_count']    ?? 0);
$transcodeCount = (int)($mediaCache['transcode_count'] ?? 0);

echo json_encode([
    'cpu_pct'         => $cpuPct,
    'ram_pct'         => $ramPct,
    'ram_used_mb'     => $ramUsedMb,
    'ram_total_mb'    => $ramTotalMb,
    'fallback'        => $fallbackState,
    'partner_enabled' => $partner['enabled'],
    'peers'           => $peers,
    'stream_count'    => $streamCount,
    'transcode_count' => $transcodeCount,
]);
