<?php

return [

    /*
    | Hostnames the operator console (/admin) may be served on.
    |
    | The Proxmox hosts and the admin console must never be publicly reachable.
    | The primary control belongs in the reverse proxy (only expose the public
    | hostnames); this is defence in depth INSIDE the app, so a proxy
    | misconfiguration cannot silently expose the console on a public domain.
    |
    | Comma-separated in ADMIN_HOSTS. EMPTY = no host restriction (dev default),
    | so nobody gets locked out by simply upgrading.
    |
    | Example (production):
    |   ADMIN_HOSTS=admin.clupilot.com
    */
    // Lowercased: DNS names are case-insensitive and Symfony's getHost() always
    // returns lowercase, so ADMIN_HOSTS=Admin.Example.com must still match.
    'hosts' => array_values(array_filter(array_map(
        fn ($host) => strtolower(trim($host)),
        explode(',', (string) env('ADMIN_HOSTS', ''))
    ))),

    /*
    | Whether the console's hostname serves ONLY the console.
    |
    | Off by default, and deliberately separate from having a hostname at all: a
    | development machine lists its own IP in ADMIN_HOSTS so the console works
    | without DNS, and that same address still has to serve the portal. Switch
    | it on only where the console really has a hostname to itself.
    */
    'exclusive' => (bool) env('ADMIN_HOST_EXCLUSIVE', false),

    /*
    | Hostname the public status page lives on, e.g. status.clupilot.com.
    |
    | Empty means it stays on every host, as it does today. Set, it becomes the
    | ONLY place /status answers — everywhere else redirects there, so a link
    | that already exists keeps working instead of hitting the 404 that strict
    | separation would otherwise produce.
    */
    'status_host' => strtolower(trim((string) env('STATUS_HOST', ''))),

    /*
    | Hostname the customer portal lives on, e.g. app.clupilot.com.
    |
    | Empty means the portal answers on every host, which is the default and
    | what every development machine reached by a bare IP depends on.
    |
    | Set, every portal route — sign-in, registration, the dashboard, Fortify's
    | own POST actions through fortify.domain — is bound to this host and does
    | not exist anywhere else. www.clupilot.com/dashboard is then a 404 rather
    | than a working page, which is the point: a route belongs to a hostname,
    | and a route that is not registered for the host being asked cannot answer.
    |
    | The first attempt at this bound only the landing page and left the portal
    | host-agnostic — so the website vanished from the portal, and the portal
    | stayed reachable from the website. Half a separation is no separation.
    */
    'app_host' => strtolower(trim((string) env('APP_HOST', ''))),

    /*
    | Hostname für Dateien zum Herunterladen, z. B. files.clupilot.com.
    |
    | Eine eigene Adresse, weil hier zweierlei liegt, das dieselben Ansprüche
    | hat: öffentlich, dauerhaft zitierbar, immer erreichbar. Rechtsdokumente
    | (AGB, AV, TOM) stehen in Verträgen und Rechnungen und müssen Jahre halten
    | — eine Adresse, die mit einem Umbau des Portals wandert, taugt dafür
    | nicht. Und das Bootstrap-Archiv, das ein Server im Rettungssystem holt,
    | bevor er irgendetwas anderes kann.
    |
    | Zwei Bereiche auf diesem Hostnamen, mit entgegengesetzten Regeln:
    |
    |  - ÖFFENTLICH: `storage/app/files/public/`. Anonym abrufbar, indexierbar.
    |    Wer die AGB sucht, soll sie finden.
    |  - PRIVAT: `/bootstrap.tar.gz`, nur mit gültigem Einmal-Code, und ohne den
    |    ein 404 statt eines 403 — dieselbe Regel wie bei der Konsole: ein
    |    Fremder soll nicht erfahren, dass es das hier gibt. Das Archiv liegt in
    |    keinem Verzeichnis; es wird bei Bedarf aus `deploy/bootstrap` gebaut.
    |
    | Leer heißt: das Archiv bleibt auf dem Portal-Hostnamen erreichbar, wie
    | bisher. So bricht nichts, solange die DNS-Einträge für den neuen Namen
    | noch nicht stehen.
    */
    'files_host' => strtolower(trim((string) env('FILES_HOST', ''))),

    /*
    | Hostnames the public website lives on, e.g. www.clupilot.com,clupilot.com.
    |
    | Comma-separated, and the FIRST is canonical: it serves the site, and every
    | other name in the list redirects to it permanently, path and all. An apex
    | domain and its www are two names for one site, and answering on both
    | without picking one splits the search rankings and the cookies between
    | them.
    |
    | Empty means the landing page answers on every host, which is what it did
    | — including on the portal's own hostname. Somebody who typed
    | app.clupilot.com got the shop window: marketing copy, a pricing table and
    | a "sign up" call to action, at the address where their servers are.
    |
    | Set, the website — landing page, robots.txt and the legal pages — exists
    | on these names and nowhere else. Every other host answers "/" with the
    | product instead. The console keeps its own "/": its routes are registered
    | first and win.
    |
    | Binding the legal pages is also what makes route('legal.impressum')
    | produce a canonical URL from inside a queued mail, where there is no
    | request to take a hostname from.
    */
    'site_hosts' => array_values(array_filter(array_map(
        fn ($host) => strtolower(trim($host)),
        explode(',', (string) env('SITE_HOST', ''))
    ))),

    /*
    | Key for VPN configs stored at rest (32 bytes, base64). Separate from
    | APP_KEY on purpose — see App\Services\Wireguard\ConfigVault. Empty means
    | storing configs is switched off, and the console says so rather than
    | quietly writing credentials in the clear.
    |
    | Generate with:  head -c 32 /dev/urandom | base64
    */
    'vpn_config_key' => env('VPN_CONFIG_KEY', ''),

    /*
    | The hostname the console answers on INSIDE the tunnel.
    |
    | Set, the VPN gateway and resolver are started (compose profile `vpn`) and
    | issued client configs point their DNS at the hub. Empty, none of that
    | exists and the VPN is only a management network — which is what an
    | installation without the extra services should be.
    |
    | Deliberately the SAME name the console already uses publicly: the
    | certificate is bound to the name, not to the address serving it, so
    | nothing new has to be issued and nothing about the internal network ends
    | up in a public DNS zone.
    */
    'vpn_internal_host' => strtolower(trim((string) env('VPN_INTERNAL_HOST', ''))),

    /*
    | Whether the tunnel-side gateway is actually READY, not merely wanted.
    |
    | The hostname alone is not enough: the deployment refuses to start the
    | gateway until a certificate for it has been found, and a client config
    | that names a resolver nobody started takes the device's whole name
    | resolution down for as long as the tunnel is up. The certificate path is
    | written by the same step that starts the services, so it is the honest
    | signal.
    */
    'vpn_ready' => strtolower(trim((string) env('VPN_INTERNAL_HOST', ''))) !== ''
        && filter_var(env('VPN_READY', false), FILTER_VALIDATE_BOOLEAN),

    /*
    | Key for credentials stored in the console (32 bytes, base64).
    |
    | Separate from APP_KEY on purpose: rotating APP_KEY is ordinary
    | maintenance, and it would otherwise render every stored credential
    | unreadable — discovered when Stripe stops answering. Empty means storing
    | credentials is switched off, and the console says so rather than falling
    | back to a key that gets rotated.
    |
    | Generate with:  head -c 32 /dev/urandom | base64
    */
    'secrets_key' => env('SECRETS_KEY', ''),

    /*
    | Networks that count as "us" while the public site is hidden
    | (PublicSiteGate). The WireGuard management subnet by default, so anyone on
    | the VPN sees the real site while the outside world sees a placeholder.
    |
    | Note this is compared against the CLIENT address, which is only correct
    | because TrustProxies is configured — behind an untrusted proxy every
    | request would look like it came from the proxy.
    */
    'trusted_ranges' => array_values(array_filter(array_map(
        'trim',
        explode(',', (string) env('TRUSTED_RANGES', '10.66.0.0/24,127.0.0.1')),
    ))),

    /*
    | Die zwei Einträge, die STRUKTURELL sind: das Management-Netz und der
    | eigene Rechner. Nur die dürfen in der Konsole als „nicht entfernbar"
    | dastehen.
    |
    | Alles andere, was jemand in TRUSTED_RANGES geschrieben hat, ist eine
    | Eintragung von Hand — und die gehört in die Liste, die der Betreiber in
    | der Konsole selbst pflegen kann. Der Unterschied war unsichtbar: eine
    | Büroadresse, die einmal in der .env gelandet war, stand in der Oberfläche
    | neben dem VPN-Netz mit demselben Vermerk „nicht entfernbar" — und der
    | Betreiber hätte sie beim nächsten Umzug nicht mehr ändern können, ohne zu
    | wissen, dass sie überhaupt aus einer Datei kommt.
    */
    'structural_ranges' => ['10.66.0.0/24', '127.0.0.1'],

];
