<?php

namespace App\Support;

/**
 * Die Hostnamen, die diese Installation ohnehin schon bedient.
 *
 * Abgeleitet, nie getippt. Genau das war der Fehler in der ersten Fassung der
 * Hostnamen-Seite: sie war ein leeres Register und zeigte deshalb nichts —
 * während die Installation längst ein halbes Dutzend Namen bediente, deren
 * Zertifikate der Betreiber gerade sehen wollte. Eine Übersicht, die erst
 * befüllt werden muss, beantwortet die Frage „was habe ich" nicht.
 *
 * Die Quellen sind dieselben, aus denen `routes/web.php` seine Domain-Bindungen
 * baut. Steht ein Name in der Umgebung, antwortet die Anwendung darauf — und
 * dann gehört er in diese Liste, ohne dass jemand ihn ein zweites Mal einträgt.
 */
final class KnownHostnames
{
    /**
     * @return array<string, string> hostname => Zweck ('public' | 'console')
     */
    public static function all(): array
    {
        $found = [];

        foreach ((array) config('admin_access.site_hosts', []) as $host) {
            self::add($found, $host, 'public');
        }

        foreach ([
            'admin_access.app_host',
            'admin_access.status_host',
            'admin_access.files_host',
        ] as $key) {
            self::add($found, (string) config($key, ''), 'public');
        }

        // Die Konsole und ihre Ausweichnamen. `console`, weil sie hinter der
        // Netzsperre liegen — ein Name mit dem falschen Zweck sähe hier aus wie
        // eine funktionierende Seite und wäre eine offene Konsole.
        foreach ((array) config('admin_access.hosts', []) as $host) {
            self::add($found, $host, 'console');
        }

        ksort($found);

        return $found;
    }

    private static function add(array &$found, string $host, string $purpose): void
    {
        $host = strtolower(trim($host));

        // Kein Schema, kein Pfad, kein Port — und mindestens ein Punkt. Ein
        // blanker Name wie `localhost` ist ein Entwicklungsrest und hat kein
        // Zertifikat, das jemand überwachen wollte.
        if ($host === '' || ! str_contains($host, '.') || preg_match('/[^a-z0-9.\-]/', $host)) {
            return;
        }

        // KEINE IP-Adressen.
        //
        // `ADMIN_HOSTS` darf ausdrücklich eine nackte IP führen — sie ist der
        // Wiederzugang zur Konsole, wenn ein Name einmal nicht auflöst. Aber für
        // eine IP stellt Let's Encrypt kein Zertifikat aus, also steht sie in
        // einer Zertifikatsübersicht nur als dauerhaft roter Eintrag, den
        // niemand beheben kann.
        //
        // Der erste Filter verlangte bloß einen Punkt — `127.0.0.1` hat drei.
        // Deshalb hier beides: die Adressprüfung UND die Bedingung, dass die
        // letzte Marke keine Zahl ist. Eine Endung ist nie numerisch, und das
        // fängt auch die Formen ab, die `FILTER_VALIDATE_IP` durchlässt.
        if (filter_var($host, FILTER_VALIDATE_IP) !== false) {
            return;
        }

        $letzteMarke = substr($host, strrpos($host, '.') + 1);
        if ($letzteMarke === '' || ctype_digit($letzteMarke)) {
            return;
        }

        // Konsole gewinnt gegen öffentlich, wenn ein Name in beiden Listen
        // steht: die engere Aussage ist die richtige.
        if (($found[$host] ?? null) !== 'console') {
            $found[$host] = $purpose;
        }
    }
}
