<?php
declare(strict_types=1);

/**
 * Twitch Helix helper med performance:
 * - Persistent cURL + HTTP/2 + gzip
 * - Fælles auth headers fra .env
 * - Retry/backoff på 429/5xx (respekterer Retry-After)
 * - Simpel filcache + ETag/Last-Modified
 * - Pagineret GET helper
 */

const HELIX_BASE         = 'https://api.twitch.tv/helix';
const DEFAULT_TIMEOUT    = 5;        // sek (kort for hurtigere side-loads)
const DEFAULT_RETRIES    = 2;        // ekstra forsøg udover første
const DEFAULT_BACKOFF_MS = 250;      // start-backoff i ms
const MAX_BACKOFF_MS     = 1500;     // maks backoff i ms

/* ----------------------- ENV + AUTH ----------------------- */

function env_load(string $file): array {
    $env = [];
    if (!file_exists($file)) return $env;
    foreach (file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
        if (strpos(ltrim($line), '#') === 0) continue;
        [$k, $v] = array_pad(explode('=', $line, 2), 2, null);
        if ($k !== null && $v !== null) $env[trim($k)] = trim($v);
    }
    return $env;
}

/** Returnerer [ok(bool), headers(array), err(array|null)] */
function twitch_auth_headers(): array {
    $env    = env_load(dirname(__DIR__) . '/.env');
    $token  = preg_replace('/^oauth:/i', '', $env['TWITCH_OAUTH'] ?? '');
    $client = $env['TWITCH_CLIENT_ID'] ?? '';
    if ($token === '' || $client === '') {
        return [false, [], ['http' => 0, 'data' => ['error' => 'missing env: TWITCH_OAUTH / TWITCH_CLIENT_ID']]];
    }
    return [true, [
        'Client-ID: ' . $client,
        'Authorization: Bearer ' . $token,
        'Accept: application/json',
    ], null];
}

/* ----------------------- CACHE ----------------------- */

function cache_dir(): string {
    $dir = dirname(__DIR__) . '/data/cache';
    if (!is_dir($dir)) @mkdir($dir, 0777, true);
    return $dir;
}

function cache_path(string $key): string {
    return cache_dir() . '/' . preg_replace('/[^a-z0-9_.-]/i', '_', $key) . '.json';
}

function cache_meta_path(string $key): string {
    return cache_dir() . '/' . preg_replace('/[^a-z0-9_.-]/i', '_', $key) . '.meta.json';
}

/* ----------------------- CORE REQUEST ----------------------- */

function _curl_handle() {
    static $ch = null;
    if ($ch === null) {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING       => '', // gzip/deflate/br
            CURLOPT_HTTP_VERSION   => CURL_HTTP_VERSION_2TLS,
            CURLOPT_TIMEOUT        => DEFAULT_TIMEOUT,
            CURLOPT_HEADER         => true, // vi splitter headers/body selv
        ]);
    }
    return $ch;
}

/**
 * Helix request med retry/backoff + ETag.
 * @return array ['http'=>int, 'data'=>array, 'raw'=>string, 'headers'=>array]
 */
function helix_request(string $method, string $path, array $params = [], $payload = null, array $opts = []): array {
    [$ok, $authHeaders, $err] = twitch_auth_headers();
    if (!$ok) return $err;

    $ch   = _curl_handle();
    $url  = HELIX_BASE . $path . ($params ? '?' . http_build_query($params) : '');
    $ttl  = (int)($opts['ttl'] ?? 0);
    $ckey = $opts['cache_key'] ?? ($path . '?' . http_build_query($params));
    $useCache = (bool)($opts['use_cache'] ?? false);

    // ETag/Last-Modified support
    $metaFile = cache_meta_path('helix_meta_' . md5($ckey));
    $meta = file_exists($metaFile) ? (json_decode((string)file_get_contents($metaFile), true) ?: []) : [];

    $headers = $authHeaders;
    if (!empty($meta['etag']))          $headers[] = 'If-None-Match: ' . $meta['etag'];
    if (!empty($meta['last_modified'])) $headers[] = 'If-Modified-Since: ' . $meta['last_modified'];

    // Sæt request
    $set = [
        CURLOPT_URL           => $url,
        CURLOPT_HTTPHEADER    => $headers,
        CURLOPT_CUSTOMREQUEST => strtoupper($method),
        CURLOPT_POSTFIELDS    => null,
    ];
    if (strtoupper($method) === 'POST') {
        $set[CURLOPT_HTTPHEADER] = array_merge($headers, ['Content-Type: application/json']);
        $set[CURLOPT_POSTFIELDS] = json_encode($payload ?? [], JSON_UNESCAPED_UNICODE);
    }
    curl_setopt_array($ch, $set);

    $attempts = 1 + DEFAULT_RETRIES;
    $backoff  = DEFAULT_BACKOFF_MS;

    do {
        $response = curl_exec($ch);
        $errno    = curl_errno($ch);
        $error    = curl_error($ch);
        $status   = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);

        if ($response === false) {
            if (--$attempts > 0) { usleep(min($backoff, MAX_BACKOFF_MS) * 1000); $backoff *= 2; continue; }
            return ['http' => 0, 'data' => ['error' => 'curl_error', 'code' => $errno, 'message' => $error]];
        }

        $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $rawHeaders = substr($response, 0, $headerSize);
        $rawBody    = substr($response, $headerSize);

        $hdrLines = preg_split('/\r\n/', trim($rawHeaders));
        $hdrAssoc = [];
        foreach ($hdrLines as $h) {
            $p = strpos($h, ':');
            if ($p !== false) {
                $k = strtolower(trim(substr($h, 0, $p)));
                $v = trim(substr($h, $p + 1));
                $hdrAssoc[$k] = $v;
            }
        }

        // Retry på 429/5xx
        if ($status === 429 || ($status >= 500 && $status <= 599)) {
            $retryAfter = (int)($hdrAssoc['retry-after'] ?? 0);
            $sleepMs = max($retryAfter * 1000, min($backoff, MAX_BACKOFF_MS));
            if (--$attempts > 0) { usleep($sleepMs * 1000); $backoff *= 2; continue; }
        }

        // 304 = brug cache hvis mulig
        if ($status === 304 && $useCache) {
            $cacheFile = cache_path('helix_' . md5($ckey) . '_ttl' . $ttl);
            if (file_exists($cacheFile)) {
                $cached = json_decode((string)file_get_contents($cacheFile), true);
                if (is_array($cached)) {
                    $cached['headers'] = $hdrAssoc;
                    $cached['http']    = 200; // lever data som 200 fra cache
                    return $cached;
                }
            }
            return ['http' => 304, 'data' => [], 'raw' => '', 'headers' => $hdrAssoc];
        }

        $data = json_decode($rawBody, true);
        if (!is_array($data)) $data = [];

        // Gem ETag/LM til senere
        $etag = $hdrAssoc['etag'] ?? null;
        $lm   = $hdrAssoc['last-modified'] ?? null;
        if ($etag || $lm) {
            @file_put_contents($metaFile, json_encode(['etag' => $etag, 'last_modified' => $lm]));
        }

        $out = ['http' => $status, 'data' => $data, 'raw' => $rawBody, 'headers' => $hdrAssoc];

        // Cache ved success
        if ($useCache && $ttl > 0 && $status === 200) {
            @file_put_contents(cache_path('helix_' . md5($ckey) . '_ttl' . $ttl), json_encode($out, JSON_UNESCAPED_UNICODE));
        }

        return $out;
    } while ($attempts > 0);
}

/* ----------------------- PUBLIC WRAPPERS ----------------------- */

function helix_get(string $path, array $params = []): array {
    return helix_request('GET', $path, $params, null, ['use_cache' => false]);
}

function helix_post(string $path, array $payload): array {
    return helix_request('POST', $path, [], $payload, ['use_cache' => false]);
}

/** Cachet GET – samme signatur som før. */
function helix_get_cached(string $path, array $params = [], int $ttl = 30): array {
    $ckey = $path . '?' . http_build_query($params);
    $file = cache_path('helix_' . md5($ckey) . '_ttl' . $ttl);
    if (file_exists($file) && (time() - filemtime($file) < $ttl)) {
        $cached = json_decode((string)file_get_contents($file), true);
        if (is_array($cached)) return $cached;
    }
    return helix_request('GET', $path, $params, null, [
        'ttl'       => $ttl,
        'cache_key' => $ckey,
        'use_cache' => true,
    ]);
}

/** Pagineret helper – samler alle sider i ét array. */
function helix_get_paginated(string $path, array $params = [], int $maxPages = 10, int $ttl = 0): array {
    $all = [];
    $cursor = null;
    $pages = 0;
    $http = 200;

    do {
        $pages++;
        $p = $params;
        if ($cursor) $p['after'] = $cursor;

        $res = $ttl > 0
            ? helix_get_cached($path, $p, $ttl)
            : helix_get($path, $p);

        $http = $res['http'] ?? 0;
        if ($http !== 200) break;

        $chunk = $res['data']['data'] ?? [];
        if (is_array($chunk)) $all = array_merge($all, $chunk);

        $cursor = $res['data']['pagination']['cursor'] ?? null;
    } while ($cursor && $pages < $maxPages);

    return ['http' => $http, 'data' => $all, 'pages' => $pages];
}

/** Slår login op → Twitch user_id (cachet i 5 min) */
function get_user_id(string $login): ?string {
    $r = helix_get_cached('/users', ['login' => $login], 300);
    if (($r['http'] ?? 0) === 200 && !empty($r['data']['data'][0]['id'])) {
        return (string)$r['data']['data'][0]['id'];
    }
    return null;
}
