<?php
// ═══════════════════════════════════════════════════════════════════════════════════════════════
// PURPOSE
//   Full monitor payload. Every metric the monitor page renders — system, CPU, memory,
//   network, GPUs, disks and pools, array, parity, UPS, containers, VMs, transcodes, watchdog
//   and rsync summaries, and remote node roll-ups — in one document.
//
// OPERATIONAL MODEL
//   Deliberately one large response rather than many small ones. Most of these metrics share
//   an underlying source — the Unraid API, /proc, the same emhttp ini files — so collecting
//   them together means one pass over each source instead of one per endpoint. The page
//   renders from a single consistent snapshot; twenty parallel fetches would render from
//   twenty slightly different moments.
//
//   Served from a 300s cache written by Tools/api_cache_writer.sh, which runs every minute.
//   The page therefore almost never pays for collection — the background writer does. ?live
//   forces a fresh build for the refresh button.
//
// DESIGN PRINCIPLES
//   The cache check happens before the heavy includes.
//       Only include/config.php is loaded to reach vv_cache_read(). monitor.php, vms.php and
//       docker_folders.php are required only after a miss, so a cache hit costs one file read
//       rather than parsing three libraries.
//
//   The API cache is pre-warmed once, on purpose.
//       vv_api_data() is called before the payload is assembled so the API-first functions
//       below it share a single GraphQL round trip instead of each making their own.
//
//   Every field is a named function call, in render order.
//       The payload is a flat map of key to collector. Adding a metric is adding a line, and
//       nothing in the assembly depends on anything else in it — so one expensive or broken
//       collector can be moved or removed without touching the others.
//
//   Reports the API's own health alongside the data.
//       _api_status travels with the payload, so the page can show that a section degraded to
//       its local fallback rather than silently presenting less detail.
//
// OPERATIONAL SAFEGUARDS
//   Read-only. Every function here observes; none of them start, stop, or change anything.
//
//   Cache miss is distinguished from empty payload.
//       vv_cache_read() returns null on a miss, expiry, or unparseable file, and the check is
//       an explicit !== null — so a legitimately sparse payload is served from cache instead
//       of being mistaken for a miss and forced onto the expensive path on every poll.
//
//   Every collector degrades to empty rather than fatal.
//       The library suppresses its filesystem reads and redirects stderr on every shell call,
//       so absent hardware — no GPU, no UPS, no ZFS, no VMs — yields an empty section and an
//       unrendered card. On a payload this wide that property is what keeps one missing
//       subsystem from blanking the entire page.
//
//   Remote collection degrades per node, so one dark partner costs its own card and nothing
//   else.
//
//   Known cost: a cache miss on a host with an unreachable partner pays the remote SSH
//   timeouts inline. That is why the background writer exists and why the cache window is
//   long — the miss path is the exception, not the design.
//
// REQUEST
//   GET             served from the 300s cache when one is present
//   GET ?live       bypass the cache and collect everything fresh
//
// RESPONSE
//   A flat object of the keys listed in the assembly below, plus _api_status and ts.
//
// DEPENDS ON
//   include/config.php          vv_cache_read()
//   include/common.php          raw hardware metrics — system, cpu, mem, net, gpu, disks,
//                               ups, parity, containers, transcodes, remote roll-ups
//                               (loaded transitively through include/monitor.php)
//   include/monitor.php         the "is anything wrong" roll-ups — vv_partner_state(),
//                               vv_fallback_state(), vv_watchdog_summary(),
//                               vv_scripts_status(), vv_rsync_status()
//   include/unraid_api.php      vv_api_data(), vv_api_get_status()
//   include/vms.php             vv_get_vms()
//   include/docker_folders.php  vv_get_docker_folders()
//   Tools/api_cache_writer.sh   writes the cache this endpoint normally serves
// ═══════════════════════════════════════════════════════════════════════════════════════════════
header('Content-Type: application/json');
require_once dirname(__DIR__) . '/include/config.php';

if (!isset($_GET['live'])) {
    $_vv_cached = vv_cache_read('monitor', 300);
    if ($_vv_cached !== null) { echo json_encode($_vv_cached); exit; }
    unset($_vv_cached);
}

require_once dirname(__DIR__) . '/include/monitor.php';
require_once dirname(__DIR__) . '/include/vms.php';
require_once dirname(__DIR__) . '/include/docker_folders.php';

// Pre-warm the API cache with one request (shared by all API-first functions below).
vv_api_data();

echo json_encode([
    'system'          => vv_system_info(),
    'fallback'        => vv_fallback_state(),
    'fallback_active' => vv_fallback_active(),
    'partner'         => vv_partner_state(),
    'resources'   => vv_system_resources(),
    'cpu'         => vv_cpu_per_core(),
    'mem'         => vv_memory_breakdown(),
    'net'         => vv_network_stats(),
    'gpu'         => vv_gpu_stats(),
    'gpus'        => vv_gpu_stats_all(),
    'gpu_procs'   => vv_gpu_processes(),
    'containers'  => vv_docker_containers(),
    'stopped'     => vv_docker_stopped(),
    'transcode'   => vv_transcode_sessions(),
    'ups'         => vv_ups_stats(),
    'parity'      => vv_parity_status(),
    'storage'     => vv_storage_pools(),
    'array_disks' => vv_array_disks(),
    'disk_io'     => vv_disk_io_rates(),
    'watchdog'       => vv_watchdog_summary(),
    'scripts'        => vv_scripts_status(),
    'rsync'          => vv_rsync_status(),
    'thresholds'     => vv_disk_thresholds(),
    'vms'            => vv_get_vms(),
    'docker_folders' => vv_get_docker_folders(),
    'remote_hosts'   => vv_remote_hosts_stats(),
    '_api_status'    => vv_api_get_status(),
    'ts'             => time(),
]);
