<?php
// Front-Controller: zentrale Einstiegstelle für alle HTTP-Requests.

// Basis-Setup: Config, Session, Fehler-Handling.
$config = require __DIR__ . "/../app/bootstrap.php";

// Routen-Tabelle laden (enthält die Handler-Funktionen).
$routes = require __DIR__ . "/../app/routes.php";

// Gemeinsame Hilfsfunktionen für Response, Views, Auth und Logging.
require_once __DIR__ . "/../app/response.php";
require_once __DIR__ . "/../app/logger.php";
require_once __DIR__ . "/../app/view.php";
require_once __DIR__ . "/../app/request.php";
require_once __DIR__ . "/../app/auth.php";
require_once __DIR__ . "/../app/laender.php";
require_once __DIR__ . "/../app/registrierungs_schutz.php";
require_once __DIR__ . "/../app/csrf_schutz.php";
require_once __DIR__ . "/../app/registrar.php";
require_once __DIR__ . "/../app/stripe.php";
// Automatische Kartenbelastung (Auftrag Z5): Einwilligung im Panel, Belastungslogik.
require_once __DIR__ . "/../app/kartenbelastung.php";

// Sicherheitskopfzeilen für jede Antwort setzen (vor jeder Ausgabe).
sicherheitskopfzeilen_ausliefern();

header("Content-Type: text/html; charset=utf-8");

// Pfad aus der URL ermitteln und passenden Handler suchen.
$path = parse_url($_SERVER["REQUEST_URI"] ?? "/", PHP_URL_PATH) ?? "/";

// WebFTP (Auftrag W): auf dem eigenen Hostnamen nur die WebFTP-Seiten (mit
// strengen Kopfzeilen), WebFTP-Adressen auf anderen Hostnamen leiten dorthin.
$path = webftp_bereich_erzwingen((string)$path, $config);
$webftp_anfrage = webftp_ist_webftp_pfad($path);

// Adminbereich nur unter der konfigurierten eigenen Domain zulassen.
admin_bereich_hostnamen_erzwingen($path, $config);

// Adressen mit Kennung im Pfad (z.B. /passwort-festlegen/<token>) laufen über
// den Handler ihrer Grundadresse (ROUTEN_MIT_PFADKENNUNG). Im Protokoll steht
// statt der Kennung "***", weil sie ein Geheimnis ist.
$handler = $routes[$path] ?? $routes[route_grundadresse_ermitteln($path)] ?? null;
$protokoll_pfad = pfad_fuers_protokoll($path);

// Endpunkte, die KEINE Browser-Sitzung haben und daher von der CSRF-Pruefung
// ausgenommen sind (Server-zu-Server). Der Stripe-Webhook kommt von Stripe,
// nicht aus einem Formular, und wird ueber die Signatur abgesichert. Der
// Brevo-Webhook (Auftrag Z7) hat das Geheimnis im Pfad (/brevo/webhook/<geheimnis>),
// deshalb wird auch die Grundadresse geprueft. Der Start des Support-Zugriffs
// (Auftrag A1) kommt per Formular aus dem workdesk und ist über seine
// Einmal-Berechtigung abgesichert. Die Meldung des Mailservers (Auftrag D2) ist
// über HMAC, Zeitstempel und Schutz gegen Wiederholung abgesichert.
$csrf_ausgenommene_pfade = ["/stripe/webhook", "/brevo/webhook", SUPPORT_ZUGRIFF_ADRESSE, SICHERUNG_MAILSERVER_MELDUNG_ADRESSE];
$csrf_ausgenommen = in_array($path, $csrf_ausgenommene_pfade, true)
  || in_array(route_grundadresse_ermitteln($path), $csrf_ausgenommene_pfade, true);

try {
  // Unbekannte oder ausgeblendete Adresse: 404 noch vor der CSRF-Prüfung, damit
  // ein POST auf eine ausgeblendete Panel-Funktion gleich endet wie ein GET.
  if (!$handler) {
    antwort_nicht_gefunden_senden($protokoll_pfad);
  }

  // CSRF-Schutz: jede veraendernde POST-Anfrage muss ein gueltiges Token
  // mitschicken. Reine GET-Anfragen und die ausgenommenen Endpunkte sind frei.
  if (
    request_method() === "POST"
    && !$csrf_ausgenommen
    && !csrf_token_pruefen()
  ) {
    log_info("csrf_abgewiesen pfad=" . $protokoll_pfad . " ip=" . request_client_ip());
    // Bei einer Anmeldung zusätzlich der einheitliche Eintrag mit Grundcode. Ob
    // das Konto existiert, wird hier nicht geprüft: nur die Domain protokollieren.
    $anmelde_bereich = anmeldung_bereich_fuer_pfad($path);
    if ($anmelde_bereich !== null) {
      anmeldung_fehlschlag_protokollieren($anmelde_bereich, "csrf_ungueltig", (string)request_post("email"), false, request_client_ip());
    }
    // WebFTP: zurück zum Ordner oder zur Anmeldung (Hinweis auch bei zu grossem
    // Hochladen, bei dem PHP das Formular samt Token verwirft).
    if ($webftp_anfrage) {
      webftp_csrf_abgewiesen_senden();
    }
    http_response_code(403);
    header("Content-Type: text/html; charset=utf-8");
    echo csrf_fehlerseite_html();
    exit;
  }

  // Support-Zugriff (Auftrag A1): Ablauf, gesperrte Aktionen und Protokoll.
  support_zugriff_anfrage_pruefen($path);

  // Handler ausführen und Antworttyp bestimmen.
  $body = $handler();
  $body_trim = ltrim($body);
  $is_html = str_starts_with($body_trim, "<!doctype html>") || stripos($body, "<html") !== false;
  if ($is_html) {
    header("Content-Type: text/html; charset=utf-8");
    http_response_code(200);
    echo $body;
  } else {
    text_response($body, 200);
  }
  log_info("200 " . $protokoll_pfad);
  exit;
} catch (Throwable $e) {
  // Unerwarteter Fehler -> 500, ohne Details an den Client.
  log_info("500 " . $protokoll_pfad . " " . $e->getMessage());
  text_response("Server Error", 500);
}
