<?php

namespace App\Provisioning\Steps\Customer;

use App\Models\ProvisioningRun;
use App\Provisioning\StepResult;
use App\Services\Mail\GuestMailConfig;
use App\Services\Proxmox\ProxmoxClient;
use App\Support\NextcloudOcc;
use Illuminate\Support\Facades\Log;

/**
 * Bringt der Nextcloud eines Kunden bei, Mail zu verschicken.
 *
 * Bis hierher konnte sie es NICHT — weder die Bereitstellung noch die
 * Compose-Datei der Vorlage setzten je einen Versand. Damit verschickte jede
 * Kundeninstanz gar nichts: keine Freigabe-Benachrichtigung, keine
 * Aktivitaetsmeldung, kein "Passwort vergessen". Still, ohne Fehlermeldung.
 *
 * Das ist die Voraussetzung fuer die Mitarbeiterverwaltung: die Einladung an
 * einen Mitarbeiter verschickt NEXTCLOUD, nicht CluPilot — nur so entsteht
 * das Passwort dort, wo niemand sonst es zu sehen bekommt.
 *
 * Ohne Server oder Absenderpostfach wird NICHTS geschrieben. Eine halb
 * eingetragene Mailkonfiguration waere schlimmer als keine.
 *
 * Das ist aber KEIN Grund, den Lauf scheitern zu lassen: dieser Schritt steht
 * in der PFLICHT-Pipeline `customer`, und ein Kunde, der bezahlt hat, bekommt
 * seine Cloud auch dann, wenn der Mailversand noch nicht eingerichtet ist —
 * genau wie RegisterMonitoring die Bereitstellung nie an einer nicht
 * erreichbaren Ueberwachung scheitern laesst (siehe deren Kopfkommentar). Der
 * Schritt geht in diesem Fall mit `advance()` weiter; nachgeholt wird es ueber
 * `clupilot:configure-instance-mail` (Aufgabe 4), sobald Server und Postfach
 * stehen.
 *
 * Die Rueckmeldung dazu geht an ZWEI Stellen, nicht nur einer — genau wie bei
 * RegisterMonitoring. Ein Lauf-Ereignis (`outcome: info`) haengt sich an den
 * Lauf, den der Betreiber in der Konsole sieht; `Log::warning` bleibt daneben
 * fuer die Nachschau ausserhalb der Konsole. Das Lauf-Ereignis ist hier nicht
 * optional: solange der Mailserver noch nicht steht, tritt dieser Zweig bei
 * JEDER einzelnen Bestellung ein, und die einzige Rueckmeldung darf dann
 * nicht in einer Logdatei verschwinden, in die niemand schaut. Mailversand
 * ist eine Nebenfunktion: fehlt er, funktioniert die Cloud vollstaendig, nur
 * Einladungen und Freigabe-Benachrichtigungen gehen nicht.
 *
 * Nicht eingerichtet ist etwas anderes als kaputt: sobald Server und Postfach
 * da sind, wird geschrieben, und ein Fehler beim Schreiben selbst (occ liefert
 * einen Fehlercode, der Gast antwortet nicht) bleibt ein Fehlschlag wie jeder
 * andere Gastbefehl in dieser Pipeline — CustomerStep::guest() wirft dafuer,
 * und der Runner macht daraus einen Retry.
 *
 * WO DAS SMTP-PASSWORT UEBERALL SICHTBAR IST — ehrlich aufgeschrieben, weil
 * ein erster Entwurf es zu verstecken versuchte und das Verstecken nichts
 * brachte:
 *
 *  - im Argv des `/bin/sh -c`, das der Gastagent fuer diesen Aufruf startet
 *    (guestExec baut die ganze Zeile so). Debians `dash` behaelt diesen
 *    Prozess fuer die volle Dauer des Aufrufs;
 *  - im Argv von `php occ` im Container;
 *  - und DAUERHAFT im Klartext in /opt/nextcloud … config/config.php, wo
 *    Nextcloud es selbst ablegt.
 *
 * Der dritte Punkt erledigt die ersten beiden: wer die Datei lesen kann, muss
 * gar nicht erst in eine Prozessliste schauen. Ein Versteck, das dieselbe
 * Person nicht aussperrt, ist keines — und ein Kommentar, der eines
 * behauptet, haelt den Naechsten vom Nachsehen ab.
 *
 * Die Eingrenzung liegt deshalb in der Infrastruktur, nicht hier: das
 * Versandkonto kann nur senden, der Versandport nimmt nur die eigenen
 * Hostadressen an, und der Mailserver drosselt je Konto. Was hier gilt und
 * geprueft wird, ist etwas anderes: der Wert wird maskiert, damit er keinen
 * zweiten Befehl starten kann.
 */
class ConfigureInstanceMail extends CustomerStep
{
    public function __construct(private ProxmoxClient $pve) {}

    public function key(): string
    {
        return 'configure_instance_mail';
    }

    public function maxDuration(): int
    {
        return 120;
    }

    public function execute(ProvisioningRun $run): StepResult
    {
        $instance = $this->instance($run);

        if ($instance === null) {
            return StepResult::fail('instance_missing');
        }

        $config = GuestMailConfig::for($instance);

        if (! $config->available()) {
            // Nicht eingerichtet, nicht kaputt — siehe Kopfkommentar. Die Cloud
            // wird trotzdem ausgeliefert. available() === false heisst hier
            // immer, dass problem() einen Grund liefert (siehe GuestMailConfig)
            // — kein Rueckfall auf einen Fall, der nicht eintreten kann.
            $grund = $config->problem();

            $run->events()->create([
                'step' => $this->key(),
                'attempt' => $run->attempt,
                'outcome' => 'info',
                'message' => 'Mailversand uebersprungen (nicht eingerichtet): '.$grund,
            ]);
            Log::warning('Mailversand fuer Instanz uebersprungen (nicht eingerichtet)', [
                'instance' => $instance->uuid,
                'reason' => $grund,
            ]);

            return StepResult::advance();
        }

        $pve = $this->pve->forHost($instance->host);

        // Der Schluessel ist ein fester occ-Name aus GuestMailConfig::values(),
        // keine Benutzereingabe — genauso unmaskiert wie 'files default_quota'
        // in ApplyStorageQuota. Maskiert wird nur der WERT, siehe Kopfkommentar.
        foreach ($config->values() as $schluessel => $wert) {
            $this->guest($pve, $run, NextcloudOcc::command(
                'config:system:set '.$schluessel.' --value='.escapeshellarg($wert)
            ));
        }

        // Das Passwort wie jeder andere Wert. Verstecken laesst es sich auf
        // dieser Maschine nicht — siehe Kopfkommentar.
        $this->guest($pve, $run, NextcloudOcc::command(
            'config:system:set mail_smtppassword --value='.escapeshellarg($config->password())
        ));

        return StepResult::advance();
    }
}
