<?php
function fetchFeed(string $url, string $sourceId, string $sourceLabel): array
{
    $cacheFile = CACHE_DIR . '/feed_' . $sourceId . '.json';

    if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < CACHE_TTL_FEED) {
        $cached = json_decode(file_get_contents($cacheFile), true);
        if ($cached) return $cached;
    }

    $ctx = stream_context_create([
        'http' => [
            'method'  => 'GET',
            'header'  => "User-Agent: Mozilla/5.0 (compatible; dispelled.ca/2.0)\r\n",
            'timeout' => 8,
        ],
        'ssl' => [
            'verify_peer'      => true,
            'verify_peer_name' => true,
        ],
    ]);

    $raw = @file_get_contents($url, false, $ctx);
    if (!$raw) return [];

    libxml_use_internal_errors(true);
    $xml = @simplexml_load_string($raw);
    libxml_clear_errors();
    if (!$xml) return [];

    // RSS 1.0 (RDF) uses <rdf:RDF> root with top-level <item> in the RSS 1.0 namespace.
    // RSS 2.0 uses <rss><channel><item>.
    $rss1ns = 'http://purl.org/rss/1.0/';
    $isRss1 = $xml->getName() === 'RDF';

    $itemList = $isRss1
        ? ($xml->children($rss1ns)->item ?? [])
        : ($xml->channel->item ?? []);

    if (empty($itemList)) return [];

    $items = [];
    foreach ($itemList as $item) {
        if ($isRss1) {
            $rssEl   = $item->children($rss1ns);
            $dcEl    = $item->children('http://purl.org/dc/elements/1.1/');
            $title   = (string)($rssEl->title       ?? '');
            $link    = (string)($rssEl->link        ?? '');
            $desc    = (string)($rssEl->description ?? '');
            $pubDate = (string)($dcEl->date         ?? '');
        } else {
            $contentNs = $item->children('content', true);
            $title   = (string)($item->title       ?? '');
            $link    = (string)($item->link        ?? '');
            $desc    = isset($contentNs->encoded)
                ? (string)$contentNs->encoded
                : (string)($item->description ?? '');
            $pubDate = (string)($item->pubDate ?? '');
        }

        $desc = strip_tags($desc);
        $desc = html_entity_decode($desc, ENT_QUOTES | ENT_HTML5, 'UTF-8');
        $desc = preg_replace('/\s+/', ' ', $desc);
        $desc = trim(mb_substr($desc, 0, 400));
        if (strtolower($desc) === 'comments') { $desc = ''; }

        if ($title === '' && $link === '') continue;

        $items[] = [
            'id'          => $sourceId . '-' . md5($link),
            'title'       => html_entity_decode($title, ENT_QUOTES | ENT_HTML5, 'UTF-8'),
            'url'         => $link,
            'description' => $desc !== '' ? $desc : null,
            'source'      => $sourceId,
            'sourceLabel' => $sourceLabel,
            'publishedAt' => $pubDate,
        ];
    }

    if (!is_dir(CACHE_DIR)) mkdir(CACHE_DIR, 0755, true);
    file_put_contents($cacheFile, json_encode($items));

    return $items;
}

function getAllFeedItems(): array
{
    $sources = [
        ['id' => 'slashdot',  'label' => 'Slashdot',        'url' => 'http://rss.slashdot.org/Slashdot/slashdotMain'],
        ['id' => 'ars',       'label' => 'Ars Technica',     'url' => 'https://feeds.arstechnica.com/arstechnica/index'],
        ['id' => 'hn',        'label' => 'Hacker News',      'url' => 'https://news.ycombinator.com/rss'],
        ['id' => 'register',  'label' => 'The Register',     'url' => 'https://www.theregister.com/headlines.rss'],
        ['id' => 'phoronix',  'label' => 'Phoronix',         'url' => 'https://www.phoronix.com/rss.php'],
        ['id' => 'lwn',       'label' => 'LWN.net',          'url' => 'https://lwn.net/headlines/rss'],
        ['id' => 'eff',       'label' => 'EFF',              'url' => 'https://www.eff.org/rss/updates.xml'],
        ['id' => 'krebs',     'label' => 'Krebs on Security','url' => 'https://krebsonsecurity.com/feed/'],
    ];

    $all = [];
    foreach ($sources as $s) {
        $all = array_merge($all, fetchFeed($s['url'], $s['id'], $s['label']));
    }

    usort($all, fn($a, $b) => strtotime($b['publishedAt']) - strtotime($a['publishedAt']));

    return $all;
}

function timeAgo(string $dateStr): string
{
    $t = @strtotime($dateStr);
    if (!$t) return '';
    $diff = time() - $t;
    if ($diff < 60)    return 'just now';
    if ($diff < 3600)  return (int)($diff / 60) . ' min ago';
    if ($diff < 86400) return (int)($diff / 3600) . ' hr ago';
    return (int)($diff / 86400) . ' days ago';
}
