<?php
declare(strict_types=1);

/** Live WireGuard transfer counters from `wg show dump` (BE data plane). */
final class WireGuardTrafficStats {
    private function __construct() {}

    /** @return array<string, array{rx_bytes: int, tx_bytes: int, latest_handshake: int, endpoint: string}> */
    public static function peerMap(): array {
        static $cache = null;
        static $cacheAt = 0;
        if ($cache !== null && (time() - $cacheAt) < 5) {
            return $cache;
        }
        $cache = self::loadPeerMap();
        $cacheAt = time();
        return $cache;
    }

    /** @return array{rx_bytes: int, tx_bytes: int, latest_handshake: int, endpoint: string}|null */
    public static function forPublicKey(string $publicKey): ?array {
        $pub = trim($publicKey);
        if ($pub === '') {
            return null;
        }
        return self::peerMap()[$pub] ?? null;
    }

    /** @return array<string, array{rx_bytes: int, tx_bytes: int, latest_handshake: int, endpoint: string}> */
    private static function loadPeerMap(): array {
        $iface = (string) cfg('remote_access.wg_interface', 'wg0');
        $prefix = rtrim((string) cfg('remote_access.wg_set_prefix', ''), " \t");
        $cmd = ($prefix !== '' ? $prefix . ' ' : '') . 'wg show ' . escapeshellarg($iface) . ' dump 2>/dev/null';
        $raw = @shell_exec($cmd);
        if (!is_string($raw) || trim($raw) === '') {
            return [];
        }
        $out = [];
        foreach (preg_split('/\r\n|\n|\r/', trim($raw)) as $line) {
            if ($line === '' || str_starts_with($line, 'interface:')) {
                continue;
            }
            $cols = explode("\t", $line);
            if (count($cols) < 7) {
                continue;
            }
            $pub = trim($cols[0]);
            if ($pub === '') {
                continue;
            }
            $out[$pub] = [
                'rx_bytes' => (int) ($cols[5] ?? 0),
                'tx_bytes' => (int) ($cols[6] ?? 0),
                'latest_handshake' => (int) ($cols[4] ?? 0),
                'endpoint' => trim((string) ($cols[2] ?? '')),
            ];
        }
        return $out;
    }

    public static function formatBytes(int $bytes): string {
        if ($bytes < 1024) {
            return $bytes . ' B';
        }
        if ($bytes < 1024 * 1024) {
            return round($bytes / 1024, 1) . ' KiB';
        }
        if ($bytes < 1024 * 1024 * 1024) {
            return round($bytes / (1024 * 1024), 2) . ' MiB';
        }
        return round($bytes / (1024 * 1024 * 1024), 2) . ' GiB';
    }
}
