<?php

namespace App\Services\Terminal;

use App\Models\Operator;
use App\Services\Secrets\SecretVault;
use Illuminate\Support\Facades\Log;
use RuntimeException;

/**
 * Die Eintrittskarte für eine Root-Sitzung auf dem CluPilot-Server selbst.
 *
 * Dieselbe Form wie {@see TerminalTicket}, dieselbe Brücke, dieselben dreißig
 * Sekunden — nur eine andere Herkunft der vier Felder: Adresse und
 * Fingerabdruck aus der Einrichtungsdatei des Wirts, Benutzer `root`, und der
 * Schlüssel aus einem EIGENEN Tresor-Eintrag.
 *
 * `host_uuid` trägt den festen Wert `server`. Die Brücke schreibt dieses Feld
 * in ihre Log-Zeile; ein leeres Feld hätte sie unlesbar gemacht, ein neues Feld
 * hätte sie geändert. So bleibt sie unverändert und das Log bleibt lesbar.
 *
 * WAS HIER NICHT GEPRÜFT WIRD: von welcher Adresse aus jemand fragt. Das ist
 * eine Eigenschaft der Anfrage, nicht der Maschine, und steht deshalb in der
 * Seite (App\Livewire\Admin\ServerTerminal) — vor dem einzigen Aufruf von
 * issue(), den es gibt.
 */
final class ServerTicket
{
    /**
     * Was gerade im Weg steht — oder null, wenn nichts im Weg steht.
     *
     * Es gibt diese Methode aus demselben Grund wie beim Host-Terminal: die
     * Seite will die Antwort VORHER wissen, um sie in ihrer eigenen Sprache
     * hinzuschreiben, statt sie als 500er weiterzureichen. Zurück kommt ein
     * Merkwort, kein Satz.
     */
    public static function blocker(): ?string
    {
        if (! ServerIdentity::exists()) {
            return 'no_key';
        }

        // Ohne die Einrichtung fehlen Adresse UND Fingerabdruck. Beide kommen
        // aus derselben Datei und werden von derselben Zeile geschrieben, also
        // gibt es auch nur ein Merkwort dafür — und genau eine Anleitung.
        if (ServerTerminalSetup::read() === null) {
            return 'not_installed';
        }

        return null;
    }

    public static function issue(Operator $for): string
    {
        match (self::blocker()) {
            'no_key' => throw new RuntimeException('Für den Server ist keine SSH-Kennung erzeugt — ohne sie kann keine Terminalsitzung entstehen.'),
            'not_installed' => throw new RuntimeException('Der Server ist für das Terminal noch nicht eingerichtet — es fehlt storage/app/deploy/server-terminal.json.'),
            default => null,
        };

        // blocker() hat die Datei eben gelesen; dass sie zwischen den beiden
        // Zeilen verschwindet, wäre ein Wettlauf mit dem Einrichtungsskript.
        // Die Prüfung steht trotzdem da: die Alternative wäre ein Zugriff auf
        // null — also eine Fehlerseite in einem Terminalfenster.
        $setup = ServerTerminalSetup::read()
            ?? throw new RuntimeException('Die Einrichtung des Server-Terminals ist zwischen Prüfung und Ausstellung verschwunden.');

        // Wer eine Root-Konsole in die Steuerungsebene aufmacht, steht mit
        // Namen im Log. Die Brücke kennt nur die Nummer des Betreibers und
        // nicht die Adresse, von der aus gefragt wurde — beides weiß allein
        // diese Seite der Anwendung, und ohne diese Zeile wäre es nirgends
        // festgehalten. Der INHALT der Sitzung wird bewusst nicht
        // mitgeschrieben: er enthält zwangsläufig Passwörter und Schlüssel im
        // Klartext, und ein Mitschnitt davon wäre ein zweites Geheimnislager.
        Log::warning('Root-Konsole zum CluPilot-Server angefordert.', [
            'operator' => $for->name,
            'operator_id' => $for->id,
            'from' => request()->ip(),
        ]);

        return TicketStore::issue([
            'operator_id' => $for->id,
            'host_uuid' => 'server',
            'ip' => $setup->address,
            'user' => 'root',
            'private_key' => (string) app(SecretVault::class)->get(ServerIdentity::VAULT_KEY),
            'fingerprint' => $setup->fingerprint,
        ]);
    }
}
