<?php

use App\Provisioning\Steps\Customer;
use App\Provisioning\Steps\Host;

$wgSubnet = env('CLUPILOT_WG_SUBNET', '10.66.0.0/24');
[$wgNetwork, $wgBits] = array_pad(explode('/', $wgSubnet), 2, '24');
$wgBase = ip2long($wgNetwork) & (0xFFFFFFFF << (32 - (int) $wgBits)) & 0xFFFFFFFF;
$wgHubDefault = long2ip($wgBase + 1); // first usable address in the CIDR

/**
 * Read a secret from a FILE path if given, else from the inline env var.
 * SSH keys are multi-line PEM, which a plain .env cannot hold — pointing at a
 * file keeps the key out of .env entirely (and off the environment).
 * Note: after `config:cache`, changing the file needs `php artisan config:clear`.
 */
$secretFrom = function (string $pathVar, string $inlineVar): string {
    $path = (string) env($pathVar, '');
    if ($path !== '' && is_readable($path)) {
        return (string) file_get_contents($path);
    }

    return (string) env($inlineVar, '');
};

return [
    /*
    | Ordered step classes per pipeline. ::class is textual — listing steps that
    | are built incrementally does not autoload them until the pipeline runs.
    */
    'pipelines' => [
        'host' => [
            Host\ValidateHostInput::class,
            Host\EstablishSshTrust::class,
            Host\PrepareBaseSystem::class,
            Host\ConfigureWireguard::class,
            Host\InstallProxmoxVe::class,
            Host\RebootIntoPveKernel::class,
            Host\ConfigureProxmox::class,
            Host\CreateAutomationToken::class,
            Host\VerifyProxmoxApi::class,
            Host\RegisterHostDns::class,
            Host\RegisterCapacity::class,
            Host\SecureHostFirewall::class,
            Host\CompleteHostOnboarding::class,
        ],
        'customer' => [
            Customer\ValidateOrder::class,
            Customer\ReserveResources::class,
            Customer\CloneVirtualMachine::class,
            Customer\ConfigureCloudInit::class,
            Customer\StartVirtualMachine::class,
            Customer\WaitForGuestAgent::class,
            Customer\ConfigureNetwork::class,
            Customer\DeployApplicationStack::class,
            Customer\ConfigureNextcloud::class,
            Customer\CreateCustomerAdmin::class,
            Customer\ConfigureDnsAndTls::class,
            Customer\RegisterBackup::class,
            Customer\RegisterMonitoring::class,
            Customer\RunAcceptanceChecks::class,
            Customer\CompleteProvisioning::class,
        ],
    ],

    // The one currency the catalogue is priced in. Plan prices carry no currency
    // of their own, so a payment in anything else cannot be frozen onto a
    // contract without the contract and the payment disagreeing forever.
    'currency' => env('CLUPILOT_CURRENCY', 'EUR'),

    /*
    | The plan catalogue does NOT live here any more. It lives in
    | plan_families / plan_versions / plan_prices, because the owner has to be
    | able to create and schedule plans from the console, and because a config
    | array cannot hold a version's history.
    |
    | Nothing falls back to a plans array here. A fallback would resurrect a
    | plan the owner had just switched off, and would let commerce read one
    | source while provisioning read another — the split-brain this replaced.
    | See App\Services\Billing\PlanCatalogue.
    |
    | quota_gb/disk_gb/ram_mb/cores drive infrastructure placement and stay
    | ADMIN-ONLY. Customers compare seats, storage, performance class and
    | features — never raw vCPU/RAM (see docs/specs/2026-07-25-portal-d-*).
    */

    // Default branding applied when a customer has not set their own (resolved by
    // Customer::brandingResolved(); NULL customer fields fall back to these).
    'branding_defaults' => [
        'display_name' => 'CluPilot Cloud',
        'logo_path' => null, // null → CluPilot logo used by the provisioner
        'primary_color' => '#f97316',
        'accent_color' => '#c2560a',
    ],

    /*
    | Traffic policy. Outbound is what counts: inbound is free at our providers,
    | and it is egress that Hetzner's 20 TB per server applies to.
    |
    | Running out throttles rather than blocks — a slow Nextcloud gets a customer
    | to buy more traffic, a dead one gets a support ticket and a cancellation.
    */
    'traffic' => [
        'warn_percent' => [80, 95],
        'throttle_kbps' => (int) env('CLUPILOT_TRAFFIC_THROTTLE_KBPS', 2048), // ~2 Mbit/s
        'addon' => ['gb' => 1000, 'price_cents' => 500],
        // Sampling interval of the collector, in minutes. Proxmox counters are
        // cumulative, so a missed run costs accuracy only if the VM restarts
        // in between.
        'sample_minutes' => 15,
    ],

    /*
    | Prices in this config are NET. The rate is configurable because it follows
    | the seller's country (20 % in Austria), and every price shown to a
    | customer has to say which it is — a figure without "netto" or "brutto" is
    | the single most common billing complaint there is.
    */
    'tax' => [
        'rate_percent' => (float) env('CLUPILOT_TAX_PERCENT', 20),
        // Ours. A customer whose VAT ID belongs to another EU country is billed
        // under reverse charge — see App\Services\Billing\TaxTreatment.
        'seller_country' => env('CLUPILOT_TAX_COUNTRY', 'AT'),
    ],

    // Extra storage add-on (per unit) and the add-on catalogue (labels in lang/*/billing.php).
    'storage_addon' => ['gb' => 100, 'price_cents' => 1000],
    'addons' => [
        'extra_backups' => ['price_cents' => 500],
        'priority_support' => ['price_cents' => 2900],
        'collabora_pro' => ['price_cents' => 1900],
        // The owner's commercial figure, to be adjusted once the first ones are
        // sold. Listed here because the public sheet has to answer what a plan
        // without an own domain costs to give one — an unpriced feature can only
        // be shown as absent, and it was being shown that way to the two plans
        // we would most like to sell it to.
        'custom_domain' => [
            'price_cents' => 900,
            // The packages on which an own domain is not possible AT ALL: not
            // bookable, not upgradable, not even offered. Named by plan key,
            // and named HERE, because the catalogue cannot answer this. Plan
            // versions model what a package HAS — its features and its size —
            // and nothing in the catalogue models what a package may BUY, so
            // the only catalogue-derived answers would be proxies, and both
            // available ones are dishonest:
            //
            //  - "the lowest tier on sale" quietly hands the right to a
            //    grandfathered Start customer the day the owner stops selling
            //    Start or adds a cheaper package underneath it.
            //  - reading it off `branding` ties two unrelated decisions
            //    together, so letting Start upload a logo would also sell it
            //    a domain.
            //
            // A rule this commercial belongs where the owner can see and
            // change it, next to the price it gates. Read only by
            // App\Services\Billing\CustomDomainAccess.
            'unavailable_on' => ['start'],
        ],
    ],

    // Feature bullets moved onto plan_versions.features, so that what a version
    // promises is frozen with the version that promised it. Labels still live in
    // lang/*/billing.php → billing.feature.<key>.

    'dns' => [
        'provider' => 'hetzner',
        'token' => env('HETZNER_DNS_TOKEN', ''),
        // Customer instances live on their own registrable domain, NOT on the
        // one that serves the portal and the console. A Nextcloud is
        // third-party software that strangers sign in to; on the same
        // registrable domain as the portal it shares cookie scope,
        // document.domain and CAA with it. Same reasoning as
        // googleusercontent.com or vercel.app.
        'zone' => env('CLUPILOT_DNS_ZONE', 'clupilot.cloud'),

        // Internal, WireGuard-only host names (fsn-01.node.…) — the vpn-dns
        // container's dnsmasq --hostsdir. NOT the public Hetzner zone above:
        // a host's tunnel address must never be resolvable outside the tunnel,
        // see RegisterHostDns. Shared with vpn-dns via the dns-hosts volume.
        'hosts_dir' => env('CLUPILOT_DNS_HOSTS_DIR', '/etc/clupilot/dns-hosts'),
    ],

    'traefik' => [
        'dynamic_path' => env('TRAEFIK_DYNAMIC_PATH', '/etc/traefik/dynamic'),
    ],

    'monitoring' => [
        // false: a monitoring outage does NOT block delivering a customer's cloud
        // (the step retries, then continues degraded with a visible event).
        // true: monitoring registration must succeed or the run fails.
        'required' => (bool) env('MONITORING_REQUIRED', false),
        'attempts' => (int) env('MONITORING_ATTEMPTS', 2),
    ],

    // CluPilot VM acts as the WireGuard hub; hosts join it during onboarding.
    'wireguard' => [
        // The hub's own address inside the tunnel — what a client uses as its
        // resolver when the console is reachable there.
        'hub_address' => env('CLUPILOT_WG_HUB_ADDRESS', '10.66.0.1'),
        'subnet' => $wgSubnet,
        'hub_ip' => env('CLUPILOT_WG_HUB_IP', $wgHubDefault), // handshake target
        'endpoint' => env('CLUPILOT_WG_ENDPOINT', ''),        // host:port reachable by peers
        'hub_public_key' => env('CLUPILOT_WG_HUB_PUBKEY', ''),
        'config_path' => env('CLUPILOT_WG_CONFIG_PATH', '/etc/wireguard/wg0.conf'),
    ],

    // SSH identity CluPilot deploys to each host after first password login.
    'ssh' => [
        // Prefer *_PATH (a file on disk); fall back to the inline env value.
        'public_key' => $secretFrom('CLUPILOT_SSH_PUBLIC_KEY_PATH', 'CLUPILOT_SSH_PUBLIC_KEY'),
        'private_key' => $secretFrom('CLUPILOT_SSH_PRIVATE_KEY_PATH', 'CLUPILOT_SSH_PRIVATE_KEY'),
        // Per-command SSH timeout; below the job timeout (2100s) but far above
        // the phpseclib ~10s default so apt full-upgrade etc. can finish.
        'command_timeout' => (int) env('CLUPILOT_SSH_COMMAND_TIMEOUT', 2000),
    ],

    // Proxmox automation role/user created on each host.
    'proxmox' => [
        'role_id' => 'CluPilotAutomation',
        'role_privs' => 'VM.Allocate,VM.Clone,VM.Config.Disk,VM.Config.CPU,VM.Config.Memory,VM.Config.Network,VM.Config.Options,VM.Config.Cloudinit,VM.PowerMgmt,VM.Monitor,VM.Audit,VM.Backup,VM.GuestAgent.Audit,VM.GuestAgent.Unrestricted,Datastore.AllocateSpace,Datastore.Audit,Sys.Audit',
        'user' => 'automation@pve',
        'token_name' => 'clupilot',
    ],
];
