<?php
/**
 * /api/sports/* — World Cup / Football fixtures proxy.
 *
 * Endpoints:
 *   GET  /api/sports/fixtures       — list of fixtures (mobile app)
 *   GET  /api/sports/standings      — group / league standings
 *   GET  /api/sports/teams/{id}/logo  — passthrough to bypass CORS on TV
 *   POST /api/admin/sports/test     — admin "validate API key" button
 *   POST /api/admin/sports/clear-cache — admin force-refresh
 *
 * Architecture:
 *   • Mobile / TV client hits THIS server. The API key never leaves
 *     the backend — important because RapidAPI keys leak quota the
 *     moment they appear in a JS bundle or an unminified APK.
 *   • Responses are cached in `sports_cache` (a tiny JSON table) for
 *     `sports_cache_ttl` seconds (default 300 = 5 minutes). This is
 *     mandatory: api-football's free tier caps at ~100 requests/day,
 *     which dies in 30 seconds without a cache.
 *   • Schema for the `sports_cache` table is created lazily inside
 *     `ensure_sports_schema()` so admins don't need to run a SQL
 *     migration manually — the first request creates the table.
 *
 * Upstream (api-football):
 *   Base URL : https://v3.football.api-sports.io  (direct, recommended)
 *              OR https://api-football-v1.p.rapidapi.com/v3 (via RapidAPI)
 *   Header   : x-apisports-key: <KEY>     (direct)
 *              OR x-rapidapi-key + x-rapidapi-host (RapidAPI)
 *
 * The admin pastes their key in the admin panel → Sports tab. We
 * auto-detect whether it's a RapidAPI key (longer / different shape)
 * by checking the `sports_provider_host` setting; if blank, we use
 * the direct api-sports.io endpoint.
 */

if (!function_exists('ensure_sports_schema')) {
function ensure_sports_schema(): void {
    static $done = false;
    if ($done) return;
    db()->exec(
        'CREATE TABLE IF NOT EXISTS sports_cache (
            cache_key   VARCHAR(160) PRIMARY KEY,
            payload     LONGTEXT     NOT NULL,
            fetched_at  INT          NOT NULL,
            expires_at  INT          NOT NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4'
    );
    $done = true;
}}

if (!function_exists('sports_settings')) {
function sports_settings(): array {
    $s = get_settings_doc();
    $host = trim((string)($s['sports_provider_host'] ?? ''));
    // Empty host → use the direct api-sports endpoint.
    if ($host === '') $host = 'v3.football.api-sports.io';
    return [
        'provider'  => (string)($s['sports_provider']  ?? 'api-football'),
        'host'      => $host,
        'api_key'   => trim((string)($s['sports_api_key']   ?? '')),
        'league_id' => (int)   ($s['sports_league_id'] ?? 1),
        'season'    => (int)   ($s['sports_season']    ?? (int)date('Y')),
        'cache_ttl' => max(60, (int)($s['sports_cache_ttl'] ?? 300)),
        'enabled'   => (bool)  ($s['world_cup_enabled'] ?? false),
    ];
}}

if (!function_exists('sports_cache_get')) {
function sports_cache_get(string $key): ?array {
    ensure_sports_schema();
    $stmt = db()->prepare(
        'SELECT payload, expires_at FROM sports_cache WHERE cache_key = ?'
    );
    $stmt->execute([$key]);
    $row = $stmt->fetch();
    if (!$row) return null;
    if ((int)$row['expires_at'] < time()) return null;
    $decoded = json_decode($row['payload'], true);
    return is_array($decoded) ? $decoded : null;
}}

if (!function_exists('sports_cache_set')) {
function sports_cache_set(string $key, array $payload, int $ttl): void {
    ensure_sports_schema();
    $now = time();
    $stmt = db()->prepare(
        'INSERT INTO sports_cache (cache_key, payload, fetched_at, expires_at)
         VALUES (?, ?, ?, ?)
         ON DUPLICATE KEY UPDATE payload = VALUES(payload),
                                 fetched_at = VALUES(fetched_at),
                                 expires_at = VALUES(expires_at)'
    );
    $stmt->execute([$key, json_encode($payload), $now, $now + $ttl]);
}}

/**
 * Build the upstream HTTP headers for either the direct api-sports
 * endpoint or the RapidAPI proxy — keyed off the host.
 */
if (!function_exists('sports_http_headers')) {
function sports_http_headers(array $cfg): array {
    $host = $cfg['host'];
    $key  = $cfg['api_key'];
    if (strpos($host, 'rapidapi.com') !== false) {
        return [
            'x-rapidapi-key: ' . $key,
            'x-rapidapi-host: ' . $host,
            'Accept: application/json',
        ];
    }
    return [
        'x-apisports-key: ' . $key,
        'Accept: application/json',
    ];
}}

/**
 * Low-level HTTP fetch against api-football. Returns the decoded
 * JSON body, or throws on transport / HTTP error.
 *
 * `$endpoint` is the path-with-querystring (e.g. "fixtures?league=1&season=2026").
 */
if (!function_exists('sports_http_get')) {
function sports_http_get(string $endpoint): array {
    $cfg = sports_settings();
    if ($cfg['api_key'] === '') {
        throw new RuntimeException('Sports API key not configured');
    }
    $url = 'https://' . $cfg['host'] . '/' . ltrim($endpoint, '/');
    $ch  = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 12,
        CURLOPT_HTTPHEADER     => sports_http_headers($cfg),
        CURLOPT_FOLLOWLOCATION => true,
        // Some shared hosts ship with stale CA bundles. Verify the
        // peer when possible but fall back to disabling if curl
        // returns the classic CURLE_SSL_CACERT — the alternative is
        // a hard 500 every World Cup matchday.
        CURLOPT_SSL_VERIFYPEER => true,
    ]);
    $body = curl_exec($ch);
    $err  = curl_errno($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($err === CURLE_SSL_CACERT || $err === 60) {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $body = curl_exec($ch);
        $err  = curl_errno($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    }
    curl_close($ch);
    if ($err) {
        throw new RuntimeException('Upstream cURL error: ' . curl_strerror($err));
    }
    if ($code >= 400) {
        throw new RuntimeException(
            'Upstream returned HTTP ' . $code . ': ' . substr((string)$body, 0, 240)
        );
    }
    $json = json_decode((string)$body, true);
    if (!is_array($json)) {
        throw new RuntimeException('Upstream returned non-JSON payload');
    }
    return $json;
}}

/**
 * GET /api/sports/fixtures?league=1&season=2026
 *
 * Returns the cached list of fixtures for the configured tournament.
 * Query params are OPTIONAL — when omitted we use the admin defaults.
 * The mobile client (worldCupData.ts) accepts the api-football JSON
 * shape directly so we forward it verbatim.
 */
function route_sports_fixtures(): void {
    $cfg = sports_settings();
    if (!$cfg['enabled']) {
        send_json(['enabled' => false, 'response' => []]);
        return;
    }
    $league = (int)($_GET['league'] ?? $cfg['league_id']);
    $season = (int)($_GET['season'] ?? $cfg['season']);
    $cacheKey = "fixtures:$league:$season";

    $cached = sports_cache_get($cacheKey);
    if ($cached) {
        $cached['cached'] = true;
        send_json($cached);
        return;
    }
    try {
        $payload = sports_http_get("fixtures?league=$league&season=$season");
    } catch (Throwable $e) {
        json_error(502, $e->getMessage());
        return;
    }
    sports_cache_set($cacheKey, $payload, $cfg['cache_ttl']);
    $payload['cached'] = false;
    send_json($payload);
}

/** GET /api/sports/standings?league=1&season=2026 */
function route_sports_standings(): void {
    $cfg = sports_settings();
    if (!$cfg['enabled']) {
        send_json(['enabled' => false, 'response' => []]);
        return;
    }
    $league = (int)($_GET['league'] ?? $cfg['league_id']);
    $season = (int)($_GET['season'] ?? $cfg['season']);
    $cacheKey = "standings:$league:$season";

    $cached = sports_cache_get($cacheKey);
    if ($cached) {
        $cached['cached'] = true;
        send_json($cached);
        return;
    }
    try {
        $payload = sports_http_get("standings?league=$league&season=$season");
    } catch (Throwable $e) {
        json_error(502, $e->getMessage());
        return;
    }
    sports_cache_set($cacheKey, $payload, $cfg['cache_ttl']);
    $payload['cached'] = false;
    send_json($payload);
}

/**
 * POST /api/admin/sports/test
 *   Body: { "api_key": "...", "host": "..." (optional) }
 *
 * Validates an API key WITHOUT saving it. The admin clicks "Test"
 * before "Save". We send a minimal "status" call upstream; the
 * api-football account endpoint returns the subscription / quota
 * info so the admin can see how many requests they have left.
 */
function route_admin_sports_test(): void {
    require_admin();
    $body = require_json_body();
    $apiKey = trim((string)($body['api_key'] ?? ''));
    if ($apiKey === '') json_error(400, 'api_key is required');
    $host = trim((string)($body['host'] ?? 'v3.football.api-sports.io'));

    // Reuse sports_http_get by faking a temporary cfg via globals.
    $url = 'https://' . $host . '/status';
    $headers = strpos($host, 'rapidapi.com') !== false
        ? ['x-rapidapi-key: ' . $apiKey, 'x-rapidapi-host: ' . $host, 'Accept: application/json']
        : ['x-apisports-key: ' . $apiKey, 'Accept: application/json'];

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 12,
        CURLOPT_HTTPHEADER     => $headers,
        CURLOPT_SSL_VERIFYPEER => true,
    ]);
    $body = curl_exec($ch);
    $err  = curl_errno($ch);
    $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($err === CURLE_SSL_CACERT || $err === 60) {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        $body = curl_exec($ch);
        $err  = curl_errno($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    }
    curl_close($ch);
    if ($err) {
        json_error(502, 'cURL: ' . curl_strerror($err));
        return;
    }
    $json = json_decode((string)$body, true);
    if (!is_array($json)) {
        json_error(502, 'Non-JSON response from upstream (HTTP ' . $code . ')');
        return;
    }
    // api-football returns { "errors": {...} } when the key is bad —
    // surface that to the admin so they don't save a broken key.
    if (!empty($json['errors'])) {
        json_error(401, 'API rejected key: ' . json_encode($json['errors']));
        return;
    }
    send_json([
        'ok'       => true,
        'http'     => $code,
        'response' => $json['response'] ?? null,
        'requests' => $json['requests'] ?? null,
        'account'  => $json['account']  ?? null,
    ]);
}

/** POST /api/admin/sports/clear-cache — wipe the in-DB cache. */
function route_admin_sports_clear_cache(): void {
    require_admin();
    ensure_sports_schema();
    db()->exec('DELETE FROM sports_cache');
    send_json(['ok' => true, 'cleared' => true]);
}
