<?php

namespace App\Services\Mail;

use App\Models\Instance;
use App\Models\Mailbox;
use App\Support\Settings;

/**
 * Welche Mailwerte die Nextcloud eines Kunden bekommt — und wann keine.
 *
 * Server und Zugangsdaten liegen in diesem Projekt an ZWEI Orten, und das ist
 * Absicht: der Server steht einmal in `app_settings` (ein Anbieterwechsel ist
 * dann eine Karte, nicht fuenf Zeilen), die Zugangsdaten je Absender am
 * Mailbox-Datensatz. Diese Klasse ist die einzige Stelle, die beides
 * zusammenlegt.
 *
 * `available() === false` heisst: NICHTS schreiben. Eine halb eingetragene
 * Mailkonfiguration ist schlimmer als keine — Nextcloud versucht dann bei
 * jedem Versand zu senden und scheitert still, ohne dass jemand es merkt.
 *
 * DIE NAHTSTELLE fuer spaeter: `mailboxFor()` gibt heute das gemeinsame
 * Versandkonto zurueck. Ein Konto je Kunde (ueber die mailcow-Schnittstelle
 * angelegt) haengt genau hier — der Bereitstellungsschritt und alles darueber
 * aendern sich dafuer nicht um eine Zeile.
 */
final class GuestMailConfig
{
    /** Der Schluessel des gemeinsamen Versandkontos in `mailboxes`. */
    public const RELAY_KEY = 'instance-relay';

    private function __construct(
        private readonly ?string $problem,
        /** @var array<string, string> */
        private readonly array $values,
        private readonly string $password,
    ) {}

    public static function for(Instance $instance): self
    {
        $host = trim((string) Settings::get('mail.host', ''));
        $port = (int) Settings::get('mail.port', 0);

        if ($host === '' || $port < 1) {
            return new self('no_server', [], '');
        }

        $box = self::mailboxFor($instance);

        if ($box === null || ! $box->isConfigured()) {
            return new self('no_mailbox', [], '');
        }

        // Die Adresse zerfaellt in die beiden Werte, die Nextcloud getrennt
        // fuehrt: den linken Teil als Absender, den rechten als Maildomain.
        [$local, $domain] = array_pad(explode('@', $box->address, 2), 2, '');

        // Ein Postfach OHNE Anmeldung ist erlaubt (Mailbox::isConfigured() —
        // ein vertrauenswuerdiges lokales Relay verlangt kein Passwort). Ein
        // fest verdrahtetes 'true' wuerde Nextcloud trotzdem zur Anmeldung
        // zwingen, sie versucht es dann mit einem leeren Passwort, und JEDER
        // Versand scheitert — still, denn der Fehler zeigt sich erst beim
        // naechsten Mailversand des Kunden, nie bei dieser Konfiguration.
        return new self(null, [
            'mail_smtpmode' => 'smtp',
            'mail_smtphost' => $host,
            'mail_smtpport' => (string) $port,
            'mail_smtpsecure' => (string) Settings::get('mail.encryption', 'tls'),
            'mail_smtpauth' => $box->authenticates ? 'true' : 'false',
            'mail_smtpname' => $box->smtpUsername(),
            'mail_from_address' => $local,
            'mail_domain' => $domain,
        ], (string) $box->password);
    }

    /**
     * Heute fuer jede Instanz dasselbe Konto. Siehe Klassenkopf — hier haengt
     * die spaetere Fassung mit einem Konto je Kunde.
     */
    private static function mailboxFor(Instance $instance): ?Mailbox
    {
        return Mailbox::findByKey(self::RELAY_KEY);
    }

    public function available(): bool
    {
        return $this->problem === null;
    }

    /** `no_server`, `no_mailbox` oder null. */
    public function problem(): ?string
    {
        return $this->problem;
    }

    /**
     * Alles ausser dem Passwort. Das geht einen eigenen Weg, damit es beim
     * Bauen der Befehle nicht versehentlich mitwandert.
     *
     * @return array<string, string>
     */
    public function values(): array
    {
        return $this->values;
    }

    public function password(): string
    {
        return $this->password;
    }
}
