<?php
session_start();
require_once __DIR__ . '/auth.php';
$pageTitle = 'Cloudflare Web Stats';


/**
 * Cloudflare Web Stats (GraphQL) - Almost Heaven Radio
 * Uses the simpler working query/parse flow and loads secrets from:
 *   C:\inetpub\Authentication\api\almostheaven.php
 */

require_once __DIR__ . '/../includes/auth_paths.php';

$configPath = ah_api_config_path();

$CF_ZONE_ID   = '';
$CF_API_TOKEN = '';

if (file_exists($configPath)) {
    $cf = require $configPath;
    $CF_ZONE_ID   = trim((string)($cf['zone_id'] ?? ''));
    $CF_API_TOKEN = trim((string)($cf['api_token'] ?? ''));
}

$tokenMissing = ($CF_API_TOKEN === '' || preg_match('/\s/', $CF_API_TOKEN) || $CF_ZONE_ID === '');

// ----------------- Helpers -----------------
function iso_utc(int $ts): string {
    return gmdate('Y-m-d\TH:i:s\Z', $ts);
}

function fmt_int($n): string {
    if ($n === null || $n === '') return '—';
    return number_format((float)$n, 0, '.', ',');
}

function fmt_bytes($bytes): string {
    if ($bytes === null || $bytes === '') return '—';
    $bytes = (float)$bytes;
    $units = ['B','KB','MB','GB','TB'];
    $i = 0;
    while ($bytes >= 1024 && $i < count($units)-1) { $bytes /= 1024; $i++; }
    return number_format($bytes, $i === 0 ? 0 : 2) . ' ' . $units[$i];
}

function fmt_pct($n): string {
    if ($n === null || $n === '') return '—';
    return rtrim(rtrim(number_format((float)$n, 2, '.', ''), '0'), '.') . '%';
}

function cf_rest_get(string $url, string $token): array {
    $token = trim((string)$token);

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $token,
            'Accept: application/json',
            'Content-Type: application/json',
        ],
    ]);

    $raw  = curl_exec($ch);
    $err  = curl_error($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($raw === false) {
        return ['success' => false, 'errors' => [['message' => 'cURL error: ' . $err]], '_http_code' => $code];
    }

    $json = json_decode($raw, true);
    if (!is_array($json)) {
        return ['success' => false, 'errors' => [['message' => 'Invalid JSON. HTTP ' . $code]], '_http_code' => $code, '_raw' => substr((string)$raw, 0, 300)];
    }

    $json['_http_code'] = $code;
    return $json;
}

function cf_gql(string $query, array $vars, string $token): array {
    $token = trim((string)$token);

    $ch = curl_init("https://api.cloudflare.com/client/v4/graphql");
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 30,
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $token,
            'Content-Type: application/json',
            'Accept: application/json',
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'query' => $query,
            'variables' => $vars,
        ]),
    ]);

    $raw  = curl_exec($ch);
    $err  = curl_error($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($raw === false) {
        return ['_http_code' => $code, '_raw' => null, 'errors' => [['message' => 'cURL error: ' . $err]]];
    }

    $json = json_decode($raw, true);
    if (!is_array($json)) {
        return ['_http_code' => $code, '_raw' => substr((string)$raw, 0, 500), 'errors' => [['message' => 'Invalid JSON from GraphQL']]];
    }

    $json['_http_code'] = $code;
    return $json;
}

function extract_http_1h_groups(array $gql): array {
    $groups = $gql['data']['viewer']['zones'][0]['httpRequests1hGroups'] ?? [];
    $series = [];

    foreach ($groups as $row) {
        $series[] = [
            't' => $row['dimensions']['datetime'] ?? '',
            'requests' => $row['sum']['requests'] ?? null,
            'bytes' => $row['sum']['bytes'] ?? null,
            'cachedRequests' => $row['sum']['cachedRequests'] ?? null,
            'cachedBytes' => $row['sum']['cachedBytes'] ?? null,
            'threats' => $row['sum']['threats'] ?? null,
            'pageViews' => $row['sum']['pageViews'] ?? null,
            'uniques' => $row['uniq']['uniques'] ?? null,
        ];
    }

    return $series;
}

function totals_from_series(array $series): array {
    $tot = [
        'requests' => 0,
        'bytes' => 0,
        'cachedRequests' => 0,
        'cachedBytes' => 0,
        'threats' => 0,
        'pageViews' => 0,
        'uniques' => null,
        'cacheHitPct' => null,
    ];

    $maxUniques = null;

    foreach ($series as $r) {
        $tot['requests'] += (int)($r['requests'] ?? 0);
        $tot['bytes'] += (int)($r['bytes'] ?? 0);
        $tot['cachedRequests'] += (int)($r['cachedRequests'] ?? 0);
        $tot['cachedBytes'] += (int)($r['cachedBytes'] ?? 0);
        $tot['threats'] += (int)($r['threats'] ?? 0);
        $tot['pageViews'] += (int)($r['pageViews'] ?? 0);

        if ($r['uniques'] !== null) {
            $u = (int)$r['uniques'];
            $maxUniques = ($maxUniques === null) ? $u : max($maxUniques, $u);
        }
    }

    $tot['uniques'] = $maxUniques;
    $tot['cacheHitPct'] = ($tot['requests'] > 0) ? round(($tot['cachedRequests'] / $tot['requests']) * 100, 2) : null;

    return $tot;
}

function sparkline(array $values, int $w=220, int $h=40): string {
    $vals = array_values(array_filter($values, fn($v) => $v !== null && $v !== ''));
    if (count($vals) < 2) return '<span class="text-muted">—</span>';

    $min = min($vals);
    $max = max($vals);
    $range = ($max - $min) ?: 1;

    $pts = [];
    $n = count($vals);
    for ($i=0; $i<$n; $i++) {
        $x = ($w - 4) * ($i / ($n - 1)) + 2;
        $y = ($h - 4) * (1 - (($vals[$i] - $min) / $range)) + 2;
        $pts[] = round($x,2) . ',' . round($y,2);
    }

    $poly = implode(' ', $pts);

    return '<svg width="'.$w.'" height="'.$h.'" viewBox="0 0 '.$w.' '.$h.'" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="trend" style="color:#111;">
      <polyline fill="none" stroke="currentColor" stroke-width="2" points="'.$poly.'"/>
    </svg>';
}

$now   = time();
$until = iso_utc($now);

$since24 = iso_utc($now - 86400);
$since72 = iso_utc($now - 86400 * 3);

$GQL = <<<'GQL'
query($zoneTag: String!, $since: DateTime!, $until: DateTime!) {
  viewer {
    zones(filter: { zoneTag: $zoneTag }) {
      httpRequests1hGroups(
        limit: 1000
        filter: { datetime_geq: $since, datetime_leq: $until }
      ) {
        dimensions { datetime }
        sum {
          requests
          bytes
          cachedRequests
          cachedBytes
          threats
          pageViews
        }
        uniq { uniques }
      }
    }
  }
}
GQL;

$g24 = $g72 = $verify = null;
$s24 = $s72 = [];
$t24 = $t72 = [];
$errorMsg = null;

if (!$tokenMissing) {
    $vars24 = ['zoneTag' => $CF_ZONE_ID, 'since' => $since24, 'until' => $until];
    $vars72 = ['zoneTag' => $CF_ZONE_ID, 'since' => $since72, 'until' => $until];

    $g24 = cf_gql($GQL, $vars24, $CF_API_TOKEN);
    $g72 = cf_gql($GQL, $vars72, $CF_API_TOKEN);

    $verify = cf_rest_get("https://api.cloudflare.com/client/v4/user/tokens/verify", $CF_API_TOKEN);

    if (!empty($g24['errors'])) {
        $errorMsg = $g24['errors'][0]['message'] ?? 'GraphQL error (24h).';
    } elseif (!empty($g72['errors'])) {
        $errorMsg = $g72['errors'][0]['message'] ?? 'GraphQL error (72h).';
    } else {
        $s24 = extract_http_1h_groups($g24);
        $s72 = extract_http_1h_groups($g72);

        $t24 = totals_from_series($s24);
        $t72 = totals_from_series($s72);
    }
}

if (isset($_GET['debug']) && ($_SESSION['role'] ?? '') === 'admin') {
    header('Content-Type: text/plain; charset=utf-8');

    echo "=== SYSTEM ===\n";
    echo "curl loaded: " . (extension_loaded('curl') ? "YES\n" : "NO\n");
    echo "openssl loaded: " . (extension_loaded('openssl') ? "YES\n" : "NO\n");
    echo "token missing: " . ($tokenMissing ? "YES\n" : "NO\n");
    echo "token length: " . strlen($CF_API_TOKEN) . "\n";
    echo "contains whitespace: " . (preg_match('/\s/', $CF_API_TOKEN) ? "YES\n" : "NO\n");
    echo "zone id length: " . strlen($CF_ZONE_ID) . "\n";
    echo "token preview: " . ($CF_API_TOKEN ? (substr($CF_API_TOKEN, 0, 4) . "..." . substr($CF_API_TOKEN, -4)) : "—") . "\n";
    echo "config file exists: " . (file_exists($configPath) ? "YES\n" : "NO\n") . "\n";
    echo "config path: " . $configPath . "\n\n";

    echo "=== TOKEN VERIFY (REST) ===\n";
    echo "HTTP Code: " . ($verify['_http_code'] ?? 'none') . "\n";
    echo json_encode($verify, JSON_PRETTY_PRINT) . "\n\n";

    echo "=== GRAPHQL 24H ===\n";
    echo "HTTP Code: " . ($g24['_http_code'] ?? 'none') . "\n";
    echo "errors: " . json_encode($g24['errors'] ?? [], JSON_PRETTY_PRINT) . "\n";
    echo "first row: " . json_encode(($g24['data']['viewer']['zones'][0]['httpRequests1hGroups'][0] ?? null), JSON_PRETTY_PRINT) . "\n\n";

    echo "=== GRAPHQL 72H ===\n";
    echo "HTTP Code: " . ($g72['_http_code'] ?? 'none') . "\n";
    echo "errors: " . json_encode($g72['errors'] ?? [], JSON_PRETTY_PRINT) . "\n";
    echo "first row: " . json_encode(($g72['data']['viewer']['zones'][0]['httpRequests1hGroups'][0] ?? null), JSON_PRETTY_PRINT) . "\n\n";

    echo "=== TOTALS (24H) ===\n";
    echo json_encode($t24, JSON_PRETTY_PRINT) . "\n\n";
    echo "=== TOTALS (72H) ===\n";
    echo json_encode($t72, JSON_PRETTY_PRINT) . "\n";

    exit;
}
include __DIR__ . '/includes/admin_header.php';
?>

<main class="admin-main">
  <div class="admin-page-header">
    <div>
      <h1>Cloudflare Web Stats</h1>
      <p class="admin-subtitle">Almost Heaven Radio</p>
    </div>
    <div class="admin-actions">
      <a href="/admin/index.php" class="admin-btn secondary">← Back to Dashboard</a>
    </div>
  </div>

  <?php if ($tokenMissing): ?>
    <section class="content-card">
      <h3>⚠️ Cloudflare API token missing or invalid</h3>
      <p>Secrets should be stored in:</p>
      <p><code><?php echo htmlspecialchars($configPath); ?></code></p>
      <p>Make sure the file contains <code>zone_id</code> and <code>api_token</code> and the token has no spaces or line breaks.</p>
    </section>

  <?php elseif ($errorMsg): ?>
    <section class="content-card">
      <h3>⚠️ Could not load Cloudflare stats</h3>
      <p><?php echo htmlspecialchars($errorMsg); ?></p>
      <p><small>Tip: open <code>?debug=1</code> while logged in as an admin if you need token verify and GraphQL diagnostics.</small></p>
    </section>

  <?php else: ?>

    <div class="admin-grid cols-3">
      <section class="content-card">
        <h3>Last 24h Requests</h3>
        <div class="admin-kpi"><?php echo fmt_int($t24['requests'] ?? null); ?></div>
        <div class="mt-2"><?php echo sparkline(array_map(fn($r)=>$r['requests'], $s24)); ?></div>
      </section>

      <section class="content-card">
        <h3>Last 24h Bandwidth</h3>
        <div class="admin-kpi"><?php echo fmt_bytes($t24['bytes'] ?? null); ?></div>
        <div class="mt-2"><?php echo sparkline(array_map(fn($r)=>$r['bytes'], $s24)); ?></div>
      </section>

      <section class="content-card">
        <h3>Last 24h Uniques</h3>
        <div class="admin-kpi"><?php echo fmt_int($t24['uniques'] ?? null); ?></div>
        <div class="mt-2"><?php echo sparkline(array_map(fn($r)=>$r['uniques'], $s24)); ?></div>
      </section>

      <section class="content-card">
        <h3>Last 24h Pageviews</h3>
        <div class="admin-kpi"><?php echo fmt_int($t24['pageViews'] ?? null); ?></div>
        <div class="mt-2"><?php echo sparkline(array_map(fn($r)=>$r['pageViews'], $s24)); ?></div>
      </section>

      <section class="content-card">
        <h3>Last 24h Threats</h3>
        <div class="admin-kpi"><?php echo fmt_int($t24['threats'] ?? null); ?></div>
        <div class="mt-2"><?php echo sparkline(array_map(fn($r)=>$r['threats'], $s24)); ?></div>
      </section>

      <section class="content-card">
        <h3>24h Cache Hit</h3>
        <div class="admin-kpi"><?php echo fmt_pct($t24['cacheHitPct'] ?? null); ?></div>
        <div class="mt-2">
          <?php echo sparkline(array_map(function($r){
            $req = (int)($r['requests'] ?? 0);
            $c   = (int)($r['cachedRequests'] ?? 0);
            return $req > 0 ? round(($c/$req)*100, 2) : null;
          }, $s24)); ?>
        </div>
      </section>

      <section class="content-card">
        <h3>Last 72h Requests</h3>
        <div class="admin-kpi"><?php echo fmt_int($t72['requests'] ?? null); ?></div>
        <div class="mt-2"><?php echo sparkline(array_map(fn($r)=>$r['requests'], $s72)); ?></div>
      </section>

      <section class="content-card">
        <h3>Last 72h Bandwidth</h3>
        <div class="admin-kpi"><?php echo fmt_bytes($t72['bytes'] ?? null); ?></div>
        <div class="mt-2"><?php echo sparkline(array_map(fn($r)=>$r['bytes'], $s72)); ?></div>
      </section>
    </div>

  <?php endif; ?>
</main>

<?php include __DIR__ . '/includes/admin_footer.php'; ?>
