<?php

namespace App\Services\Terminal;

use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Log;
use Throwable;

/**
 * Was der Wirt über sich selbst hinterlassen hat, als er eingerichtet wurde.
 *
 * Zwei Werte, und keiner von beiden darf aus dem Code kommen:
 *
 * - **Die Adresse**, unter der der Container den Wirt erreicht — das Gateway
 *   des compose-Netzes. Auf dieser Installation 172.18.0.1, auf der nächsten
 *   eine andere. Ein fester Wert im Code wäre auf jeder zweiten Maschine falsch.
 * - **Der Fingerabdruck** des Hostschlüssels, in CluPilots Form gebildet
 *   (`SHA256:` base64 über `"<algorithmus> <base64-blob>"`, NICHT der
 *   OpenSSH-Fingerabdruck — siehe docker/terminal/bridge.py). Er ist der Grund,
 *   warum eine Sitzung überhaupt sicher ist: ohne ihn meldet sich die Brücke
 *   mit einem Root-Schlüssel bei irgendwem an, der gerade auf Port 22 antwortet.
 *
 * Geschrieben wird die Datei von `/usr/local/sbin/clupilot-host-step
 * install-server-terminal`, also von der root-eigenen Hälfte, die das
 * Dienstkonto nicht anfassen kann. Gelesen wird sie hier.
 */
final readonly class ServerTerminalSetup
{
    public function __construct(
        public string $address,
        public string $fingerprint,
        public ?string $installedAt,
    ) {}

    public static function path(): string
    {
        return storage_path('app/deploy/server-terminal.json');
    }

    /**
     * Die Einrichtung, oder null, wenn es keine brauchbare gibt.
     *
     * Fehlt die Datei, ist das der Normalfall vor dem ersten
     * `sudo bash deploy/install-agent.sh` — dazu gibt es nichts zu sagen. Ist
     * sie aber DA und trotzdem unbrauchbar, wird das protokolliert: sonst sehen
     * „nie eingerichtet" und „Einrichtung kaputt" von der Seite aus gleich aus,
     * und die Anleitung, die dann dort steht, hilft im zweiten Fall nicht.
     */
    public static function read(): ?self
    {
        $path = self::path();

        if (! File::exists($path)) {
            return null;
        }

        try {
            $data = json_decode(File::get($path), true, flags: JSON_THROW_ON_ERROR);
        } catch (Throwable $e) {
            Log::warning('Die Einrichtung des Server-Terminals ist nicht lesbar.', [
                'path' => $path,
                'error' => $e->getMessage(),
            ]);

            return null;
        }

        $address = is_array($data) ? (string) ($data['address'] ?? '') : '';
        $fingerprint = is_array($data) ? (string) ($data['fingerprint'] ?? '') : '';

        // Geprüft, nicht geglaubt. Eine leere Adresse ergäbe ein Fenster, das
        // nie verbindet; ein leerer Fingerabdruck ist das Gegenteil von
        // harmlos — die naheliegendste Lesart davon („nichts zu prüfen") wäre
        // eine ungepinnte Root-Sitzung.
        if (filter_var($address, FILTER_VALIDATE_IP) === false || ! str_starts_with($fingerprint, 'SHA256:')) {
            Log::warning('Die Einrichtung des Server-Terminals ist unvollständig.', ['path' => $path]);

            return null;
        }

        return new self(
            address: $address,
            fingerprint: $fingerprint,
            installedAt: ($data['installed_at'] ?? null) ? (string) $data['installed_at'] : null,
        );
    }
}
