<?php
// ═══════════════════════════════════════════════════════════════════════════════════════════════
// PURPOSE
//   Partner connectivity test. Runs one SSH `echo ok` against a named host slot and reports
//   success plus round-trip latency — the "Test" button on the partnership tab.
//
// OPERATIONAL MODEL
//   Deliberately a GET, despite being an action. php -S and this nginx setup drop POST
//   bodies on Unraid PHP 8.4, so a POST would arrive with no id at all. The operation is
//   safe to repeat and changes nothing, which is what makes GET acceptable here rather than
//   merely convenient.
//
// DESIGN PRINCIPLES
//   Tests the real path, not a substitute for it.
//       The probe is SSH over the Tailscale IP with the same key every partnership operation
//       uses. An ICMP ping would answer a question nobody is asking — what matters is whether
//       this host can actually drive the partner.
//
//   Distinguishes why it failed.
//       vv_pt_ping() returns separate errors for an unknown slot, a missing key, and an
//       unresolvable Tailscale name, because those need three different fixes.
//
// OPERATIONAL SAFEGUARDS
//   The host id is pattern-matched before it is used.
//       ^host\d+$ (case-insensitive) is enforced here, and the value is then only used as a
//       conf-key lookup — it never reaches a path or a shell. The remote command is the fixed
//       literal `echo ok`; nothing from the request composes it.
//
//   The probe is time-boxed at 8 seconds.
//       vv_pt_ping() passes an explicit timeout to vv_pt_ssh(), which also sets BatchMode=yes
//       so it can never sit waiting for a password. A powered-off partner costs 8s, not a
//       stuck request.
//
//   Read-only on the partner. `echo ok` is the entire remote payload.
//
//   Never cached.
//       Cache-Control: no-store, no-cache — a connectivity test served from cache is worse
//       than no test, because it reports a partner reachable after it has gone dark.
//
// REQUEST
//   GET ?id=host<n>   (case-insensitive; the slot name, not the hostname)
//
// RESPONSE
//   {"ok":true,"ms":int,…}                on success
//   {"ok":false,"error":string}           invalid id, no key, unresolvable, or no reply
//
// DEPENDS ON
//   include/partnership.php   vv_pt_ping() → vv_pt_ssh(), vv_resolve_tailscale_ip()
// ═══════════════════════════════════════════════════════════════════════════════════════════════
header('Content-Type: application/json');
header('Cache-Control: no-store, no-cache');
require_once dirname(__DIR__) . '/include/partnership.php';

$id = trim($_GET['id'] ?? '');
if (!preg_match('/^host\d+$/i', $id)) {
    echo json_encode(['ok' => false, 'error' => 'Invalid host id']);
    exit;
}

echo json_encode(vv_pt_ping($id));
