<?php

declare(strict_types=1);

// php -S Router-Modus: existierende Dateien (Assets) direkt ausliefern.
if (PHP_SAPI === 'cli-server') {
    $file = __DIR__ . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
    if (is_file($file)) {
        return false;
    }
    // Eigenständige Apps unter public/ mit eigener index.php (z. B. /dateien/, die
    // Dateiablage). Muss VOR der Trailing-Slash-Kanonisierung unten stehen, sonst
    // schaukeln sich unser 301 und der des Dev-Servers gegenseitig auf.
    // Docroot selbst ausschließen: für "/" löst sich $file auf public/ auf, und
    // public/index.php (diese Datei) existiert immer — sonst würde die Startseite
    // permanent an den Dev-Server durchgereicht und leer bleiben.
    if (is_dir($file) && rtrim($file, '/') !== rtrim(__DIR__, '/') && is_file(rtrim($file, '/') . '/index.php')) {
        // Verzeichnisse brauchen den Slash am Ende, sonst lösen die relativen URLs der
        // App eine Ebene zu hoch auf: /dateien + "index.php?action=files" wird zu
        // /index.php?action=files und landet hier im Front Controller — die App bekommt
        // dann unsere Startseite als HTML statt JSON und meldet nur „Fehler".
        // Apache erledigt das per mod_dir (DirectorySlash), der Dev-Server nicht.
        $dirPath = (string) parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
        if (!str_ends_with($dirPath, '/')) {
            $query = $_SERVER['QUERY_STRING'] ?? '';
            header('Location: ' . $dirPath . '/' . ($query !== '' ? '?' . $query : ''), true, 301);
            exit;
        }
        return false;
    }
}

require dirname(__DIR__) . '/app/bootstrap.php';

$path = (string) parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH);

// Kanonisch ohne trailing slash (Root ausgenommen).
if ($path !== '/' && str_ends_with($path, '/')) {
    $query = $_SERVER['QUERY_STRING'] ?? '';
    header('Location: ' . rtrim($path, '/') . ($query !== '' ? '?' . $query : ''), true, 301);
    exit;
}

$slug = trim($path, '/');

// Formular-Submits.
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $slug === 'kontakt-senden') {
    require APP_PATH . '/actions/contact-submit.php';
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $slug === 'mitglied-werden-senden') {
    require APP_PATH . '/actions/membership-submit.php';
    exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $slug === 'sportheimbuchung-senden') {
    require APP_PATH . '/actions/sportheimbuchung-senden.php';
    exit;
}

$routes = require APP_PATH . '/routes.php';

// Sitemap aus dem Routen-Register generieren.
if ($slug === 'sitemap.xml') {
    header('Content-Type: application/xml; charset=utf-8');
    echo '<?xml version="1.0" encoding="UTF-8"?>' . "\n";
    echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n";
    foreach ($routes as $routeSlug => $route) {
        if (empty($route['sitemap'])) {
            continue;
        }
        $lastmod = date('Y-m-d', (int) filemtime(APP_PATH . '/pages/' . $route['file']));
        echo "  <url><loc>" . e(abs_url($routeSlug)) . "</loc><lastmod>{$lastmod}</lastmod></url>\n";
    }
    // Stadionzeitung-Ausgaben sind keine routes.php-Einträge (dynamische Route,
    // siehe unten) — hier separat aus data/stadionzeitung.json ergänzen.
    foreach (json_load('stadionzeitung')['issues'] ?? [] as $issue) {
        // Entwürfe tauchen nicht in der Sitemap auf (status-Feld, noindex).
        if (empty($issue['slug']) || !stadionzeitung_is_published($issue)) {
            continue;
        }
        $loc = e(abs_url('stadionzeitung/' . $issue['slug']));
        $lastmod = e((string) ($issue['date'] ?? date('Y-m-d')));
        echo "  <url><loc>{$loc}</loc><lastmod>{$lastmod}</lastmod></url>\n";
    }
    // Veranstaltungen: zweite dynamische Route, gleiches Muster.
    // veranstaltungen_load() liefert bereits nur die veröffentlichten —
    // Entwürfe tragen noindex und gehören nicht in die Sitemap.
    //
    // lastmod kommt hier NICHT aus dem Termin (anders als bei der
    // Stadionzeitung, deren Ausgabe nach dem Veröffentlichen fixiert ist): eine
    // Fest-Seite ändert sich gerade NACH ihrem Datum noch, wenn Rückblick und
    // Bilder dazukommen. filemtime der Datenquelle ist damit die einzige
    // Angabe, die nie zu alt ist.
    $veranstaltungenLastmod = date('Y-m-d', (int) filemtime(DATA_PATH . '/veranstaltungen.json'));
    foreach (veranstaltungen_load() as $veranstaltung) {
        if (empty($veranstaltung['slug'])) {
            continue;
        }
        $loc = e(abs_url('veranstaltungen/' . $veranstaltung['slug']));
        echo "  <url><loc>{$loc}</loc><lastmod>{$veranstaltungenLastmod}</lastmod></url>\n";
    }
    // News-Artikel: dritte dynamische Route. news_load() liefert nur die
    // veröffentlichten, und ohne artikel[] existiert gar keine Seite.
    //
    // lastmod kommt hier aus dem Datum der Meldung (Stadionzeitungs-Muster,
    // nicht Veranstaltungs-Muster): ein Artikel ist mit dem Erscheinen fertig,
    // er wächst nicht wie eine Fest-Seite nach ihrem Datum weiter. filemtime
    // der Datenquelle hätte hier sogar einen Nachteil — jede neue Meldung würde
    // das lastmod ALLER Artikel zurücksetzen und Crawler auf unveränderte
    // Seiten schicken.
    foreach (news_load() as $item) {
        if (!news_has_article($item)) {
            continue;
        }
        $loc = e(abs_url('news/' . $item['slug']));
        $lastmod = e((string) ($item['date'] ?? date('Y-m-d')));
        echo "  <url><loc>{$loc}</loc><lastmod>{$lastmod}</lastmod></url>\n";
    }
    echo '</urlset>';
    exit;
}

// Die drei dynamischen Routen der Seite (Stadionzeitungs-Ausgaben,
// Veranstaltungen, News-Artikel). routes.php kennt nur exakte Slugs, deshalb
// hier gezielte Prefix-Matches VOR dem normalen Lookup — die Page-Datei
// ermittelt den Detail-Slug selbst wieder aus der URL und schlägt ihn in ihrer
// JSON nach. Bewusst drei benannte Präfixe, KEINE generelle Wildcard-Fähigkeit.
//
// 'news/' ist kein neues Muster, sondern das dritte Vorkommen desselben:
// Detailseite zu einem Eintrag in einer data/*.json. Die Alternative wäre ein
// exakter routes.php-Eintrag pro Meldung — damit stünden redaktionelle Inhalte
// im Routen-Register und die Single-Source-Regel wäre gebrochen.
// Der exakte Slug 'news' (Übersicht) beginnt nicht mit 'news/' und bleibt
// davon unberührt; '/news/' wird oben auf '/news' 301-t.
$prefixRoutes = [
    'stadionzeitung/'  => 'stadionzeitung-ausgabe.php',
    'veranstaltungen/' => 'veranstaltung.php',
    'news/'            => 'news-artikel.php',
];
foreach ($prefixRoutes as $prefix => $file) {
    if (!str_starts_with($slug, $prefix)) {
        continue;
    }
    [$meta, $content] = render_page(APP_PATH . '/pages/' . $file);
    // http_response_code() ohne Argument liest den Code zurück, den die Page-Datei
    // ggf. gesetzt hat (globaler Engine-State, kein $meta-artiges Scope-Problem) —
    // so bekommt meta.php für einen unbekannten Slug dieselbe 404-Behandlung wie
    // die echte 404-Route (noindex, kein Self-Canonical).
    $current = http_response_code() === 404 ? '404' : $slug;
    require APP_PATH . '/layout.php';
    exit;
}

if (isset($routes[$slug]) && $slug !== '404') {
    $current = $slug;
} else {
    http_response_code(404);
    $current = '404';
}

[$meta, $content] = render_page(APP_PATH . '/pages/' . $routes[$current]['file']);

require APP_PATH . '/layout.php';
