<?php
/**
 * Pure-PHP Reddit post fetcher for dispelled.ca.
 *
 * Uses Reddit's public multi-sub RSS (Atom) endpoint with a
 * browser User-Agent.
 * Reddit blocks generic PHP user-agents but permits Firefox UA requests.
 * Results are cached for 30 minutes and kept as a stale fallback if Reddit
 * temporarily rate-limits an anonymous RSS refresh.
 */
function publicRedditUrl(string $url): string
{
    return preg_replace('#^https?://(?:old\.)?reddit\.com/#i', 'https://www.reddit.com/', $url) ?? $url;
}

function normalizeRedditItems(array $items): array
{
    return array_map(function (array $item): array {
        if (!empty($item['url'])) {
            $item['url'] = publicRedditUrl($item['url']);
        }
        return $item;
    }, $items);
}

function sortAndLimitRedditItems(array $items): array
{
    usort($items, function (array $a, array $b): int {
        $aTime = strtotime($a['publishedAt'] ?? '') ?: 0;
        $bTime = strtotime($b['publishedAt'] ?? '') ?: 0;
        return $bTime <=> $aTime;
    });

    $limited = [];
    $perSubreddit = [];
    foreach ($items as $item) {
        $subreddit = $item['subreddit'] ?? '';
        if (($perSubreddit[$subreddit] ?? 0) >= 20) continue;
        $limited[] = $item;
        $perSubreddit[$subreddit] = ($perSubreddit[$subreddit] ?? 0) + 1;
    }

    return $limited;
}

function mergeRedditItems(array $staleItems, array $freshItems): array
{
    $refreshedSubreddits = array_unique(array_column($freshItems, 'subreddit'));
    $retainedItems = array_filter(
        $staleItems,
        fn(array $item): bool => !in_array($item['subreddit'] ?? '', $refreshedSubreddits, true)
    );

    $merged = [];
    foreach (array_merge($retainedItems, $freshItems) as $item) {
        $merged[$item['id']] = $item;
    }

    return sortAndLimitRedditItems(array_values($merged));
}

function getRedditPosts(?string $requestedSub = null): array
{
    $cacheFile = CACHE_DIR . '/reddit.json';
    $staleItems = [];
    $allSubIds = [
        'linux', 'programming', 'netsec', 'sysadmin', 'opensource',
        'raspberry_pi', 'embedded', 'meshtastic', 'python', 'openbsd',
        'pfsense', 'selfhosted', 'homelab',
    ];
    $cacheIsFresh = false;
    $missingSubIds = [];

    if (file_exists($cacheFile)) {
        $cached = json_decode(file_get_contents($cacheFile), true);
        if (is_array($cached) && count($cached) > 0) {
            $staleItems = sortAndLimitRedditItems(normalizeRedditItems($cached));
            if ($requestedSub !== null) {
                $cachedForSub = array_values(array_filter(
                    $staleItems,
                    fn(array $item): bool => ($item['subreddit'] ?? '') === $requestedSub
                ));
                if ($cachedForSub !== []) return $cachedForSub;
            } else {
                $cacheIsFresh = (time() - filemtime($cacheFile)) < 1800;
                $cachedSubIds = array_unique(array_column($staleItems, 'subreddit'));
                $missingSubIds = array_values(array_diff($allSubIds, $cachedSubIds));
                if ($cacheIsFresh && $missingSubIds === []) {
                    return $staleItems;
                }
            }
        }
    }

    $subs = $requestedSub
        ?? ($cacheIsFresh && $missingSubIds !== [] ? $missingSubIds : $allSubIds);
    if (is_array($subs)) $subs = implode('+', $subs);
    $url  = "https://reddit.com/r/{$subs}.rss?limit=100";

    $ctx = stream_context_create(['http' => [
        'method'  => 'GET',
        'header'  => implode("\r\n", [
            'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:125.0) Gecko/20100101 Firefox/125.0',
            'Accept: application/rss+xml, application/xml, text/xml;q=0.9, */*;q=0.8',
            'Accept-Language: en-US,en;q=0.5',
        ]) . "\r\n",
        'timeout' => 12,
    ]]);

    $raw = @file_get_contents($url, false, $ctx);
    if (!$raw) return $requestedSub !== null ? [] : $staleItems;

    libxml_use_internal_errors(true);
    $xml = simplexml_load_string($raw);
    libxml_clear_errors();
    if (!$xml) return $requestedSub !== null ? [] : $staleItems;

    $labelMap = [
        'linux'       => 'r/linux',
        'programming' => 'r/programming',
        'netsec'      => 'r/netsec',
        'sysadmin'    => 'r/sysadmin',
        'opensource'  => 'r/opensource',
        'raspberry_pi'=> 'r/raspberry_pi',
        'embedded'    => 'r/embedded',
        'meshtastic'  => 'r/meshtastic',
        'python'      => 'r/python',
        'openbsd'     => 'r/openbsd',
        'pfsense'     => 'r/pfSense',
        'selfhosted'  => 'r/selfhosted',
        'homelab'     => 'r/homelab',
    ];

    $items = [];
    foreach ($xml->entry as $entry) {
        // Subreddit from <category term="linux">
        $sub = null;
        foreach ($entry->category as $cat) {
            $term = strtolower((string)$cat['term']);
            if (isset($labelMap[$term])) { $sub = $term; break; }
        }
        if (!$sub) continue;

        // URL from <link rel="alternate" href="...">
        $postUrl = '';
        foreach ($entry->link as $link) {
            if ((string)$link['rel'] === 'alternate') {
                $postUrl = (string)$link['href'];
                break;
            }
        }
        if (!$postUrl) $postUrl = (string)($entry->link['href'] ?? '');
        $postUrl = publicRedditUrl($postUrl);

        // Description: strip HTML content, remove Reddit boilerplate
        $desc = '';
        if (isset($entry->content)) {
            $desc = strip_tags((string)$entry->content);
            $desc = html_entity_decode($desc, ENT_QUOTES | ENT_HTML5, 'UTF-8');
            $desc = preg_replace('/\s+/', ' ', trim($desc));
            $desc = preg_replace('/\s*submitted by\s+\/u\/\S+(\s+\[link\])?(\s+\[comments\])?/i', '', $desc);
            $desc = preg_replace('/\s*\[link\]\s*(\[comments\])?/i', '', $desc);
            $desc = trim($desc);
            if (mb_strlen($desc) < 15) $desc = '';
            elseif (mb_strlen($desc) > 300) $desc = mb_substr($desc, 0, 300) . '…';
        }

        $idRaw = (string)$entry->id;

        $items[] = [
            'id'             => 'reddit-' . ($idRaw ?: md5($postUrl)),
            'title'          => html_entity_decode((string)$entry->title, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
            'url'            => $postUrl,
            'description'    => $desc !== '' ? $desc : null,
            'subreddit'      => $sub,
            'subredditLabel' => $labelMap[$sub],
            'publishedAt'    => (string)$entry->updated,
        ];
    }

    if ($requestedSub === null && !$cacheIsFresh && $items !== []) {
        $combinedBeforeHydration = array_merge($staleItems, $items);
        $loadedSubIds = array_unique(array_column($combinedBeforeHydration, 'subreddit'));
        $stillMissingSubIds = array_values(array_diff($allSubIds, $loadedSubIds));

        if ($stillMissingSubIds !== []) {
            $hydratedItems = getRedditPosts(implode('+', $stillMissingSubIds));
            $items = array_merge($items, $hydratedItems);
        }
    }

    if (count($items) > 0) {
        if (!is_dir(CACHE_DIR)) mkdir(CACHE_DIR, 0755, true);
        $mergedItems = mergeRedditItems($staleItems, $items);
        file_put_contents($cacheFile, json_encode($mergedItems));
        return $requestedSub !== null ? sortAndLimitRedditItems($items) : $mergedItems;
    }

    return $items ?: ($requestedSub !== null ? [] : $staleItems);
}
