<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

/**
 * Ein Hostname, den der Reverse Proxy für diese Installation bedienen soll.
 *
 * Die Zeile ist der Wunsch, nicht der Zustand. Ob der Name wirklich bedient
 * wird und ob er ein Zertifikat hat, steht nicht hier — das wird gemessen, an
 * dem, was ein Besucher tatsächlich bekommt (siehe CertificateInspector).
 */
class ProxyHost extends Model
{
    protected $fillable = ['hostname', 'purpose', 'note', 'source'];

    /**
     * Aus der Umgebung abgeleitet — nicht von Hand angelegt, also auch nicht
     * von Hand zu entfernen: beim nächsten Abgleich käme er wieder, und ein
     * Knopf, der nichts bewirkt, ist schlimmer als keiner.
     */
    public function isFromConfig(): bool
    {
        return $this->source === 'config';
    }

    /** Hat die Prüfung ein gültiges Zertifikat gefunden? */
    public function hasCertificate(): bool
    {
        return $this->certificate_expires_at !== null && $this->certificate_error === null;
    }

    protected function casts(): array
    {
        return [
            'certificate_expires_at' => 'datetime',
            'certificate_checked_at' => 'datetime',
        ];
    }

    /**
     * Wofür der Name da ist — und damit, was er darf.
     *
     * `public`  — Website, Portal, Status, Dateien. Vom offenen Internet
     *             erreichbar, Zertifikat über die HTTP-Prüfung.
     * `console` — der Adminbereich. Er bekommt die Netzsperre und ist NICHT
     *             öffentlich; ein Zertifikat holt er trotzdem.
     *
     * Getrennt, weil ein Name mit dem falschen Zweck genau die Sorte Fehler
     * ist, die niemand sieht: die Konsole öffentlich zu stellen sieht aus wie
     * eine funktionierende Seite.
     */
    public const PURPOSES = ['public', 'console'];

    public function isConsole(): bool
    {
        return $this->purpose === 'console';
    }

    /** Tage bis zum Ablauf, oder null wenn nichts gemessen wurde. */
    public function daysLeft(): ?int
    {
        if ($this->certificate_expires_at === null) {
            return null;
        }

        // Aufgerundet auf ganze Tage und nie negativ dargestellt: „noch -3 Tage"
        // ist keine Auskunft, „abgelaufen" ist eine.
        return max(0, (int) now()->diffInDays($this->certificate_expires_at, false));
    }

    /**
     * Läuft es bald ab?
     *
     * Dreißig Tage, weil Let's Encrypt bei neunzig Tagen Laufzeit nach dreißig
     * verbleibenden längst hätte erneuern müssen. Was darunter steht, ist kein
     * knappes Zertifikat mehr, sondern eine Erneuerung, die nicht stattfindet.
     */
    public function isExpiringSoon(): bool
    {
        $days = $this->daysLeft();

        return $days !== null && $days <= 30;
    }
}
