<?php

/*
 * The readiness check (App\Support\Readiness). Every `*_breaks` sentence
 * answers not "what is missing" but "what happens if it stays missing" — see
 * the header comment of App\Support\Readiness\Check.
 */
return [
    'page_title' => 'Readiness',
    'page_sub' => 'Every required field of this installation in one place — until it says "Ready for live mode" at the top, something here is still open.',
    'ready_test' => 'Ready for test mode',
    'ready_live' => 'Ready for live mode',
    'not_ready' => '{1} Not ready yet — :n blocking item open|[2,*] Not ready yet — :n blocking items open',

    'group' => [
        'billing' => 'Billing',
        'onboarding' => 'Host onboarding',
        'provisioning' => 'Provisioning',
        'delivery' => 'Delivery',
        'operation' => 'Operation',
        'security' => 'Security',
    ],

    'status' => [
        'satisfied' => 'Satisfied',
        'blocking' => 'Blocking',
        'warning' => 'Warning',
    ],

    'run_check' => 'Check',
    'check_ok' => 'Checked: OK',
    'check_failed' => 'Checked: not OK',
    'check_key_mode' => 'This key belongs to the :mode account.',
    'check_key_mode_conflict' => 'This installation is running in :mode — the key is in the wrong slot.',
    'fix_link' => 'Fix',
    'last_heartbeat' => 'Last heartbeat: :when',

    'mode' => [
        'test' => 'Test mode',
        'live' => 'Live mode',
    ],

    'billing' => [
        'stripe_secret' => 'Stripe key (:mode)',
        'stripe_secret_breaks' => 'Without it the checkout accepts no order at all — no order is ever created.',
        // Two separate sentences, because the consequences are opposite ones:
        // real money moving that should not, and no money moving that should.
        'stripe_secret_test_slot_breaks' => 'The test slot holds a live key. Every order charges real money while this page and the console say "test mode" — a trial purchase is a real purchase, on a real card.',
        'stripe_secret_live_slot_breaks' => 'The live slot holds a test key. The customer completes checkout, no money is ever taken, and the order still looks paid — provisioning runs, the invoice is issued, and nothing arrives in the account.',
        'stripe_webhook_secret' => 'Stripe signing secret',
        'stripe_webhook_secret_breaks' => 'Without it a payment is never recorded: the customer pays, and nothing happens.',
        'company_details' => 'Company details complete',
        'company_details_breaks' => 'IssueInvoice refuses to issue the invoice. Provisioning still goes through — the customer gets a running cloud with no invoice.',
        'invoice_series' => 'Invoice series for every document kind',
        'invoice_series_breaks' => 'IssueInvoice aborts while issuing whenever a series is missing or switched off. Provisioning still goes through — the customer gets a running cloud with no invoice, and the failure is only written to the log.',
        'catalogue_synced' => 'Stripe catalogue in sync',
        'catalogue_synced_breaks' => 'A verified EU business customer cannot check out, because their net price is missing at Stripe.',
        // Says what happens and what actually helps, including the awkward
        // half: stripe:sync-catalogue skips every row that already carries an
        // id, so it does not repair this on its own.
        // The third state, deliberately WITHOUT a clear-it-out instruction: it
        // arises without any account change, and the objects belong to the
        // account in force.
        'catalogue_origin_breaks' => 'Nothing records which account the stored Stripe ids were created in — a sync may have died part-way through, or an order may have minted its own missing price. Until that is settled this page cannot say whether the checkout can sell with them. Running stripe:sync-catalogue takes up what is already there, creates only what is missing, and records the account — nothing has to be deleted for it.',
        'catalogue_account_breaks' => 'The stored Stripe product and price ids were created in a different account from the one now in force (:mode) — a Stripe object belongs to the account that issued it. Every order then fails at the checkout with "No such price": no order, no payment. Re-running stripe:sync-catalogue is not enough on its own, because it skips every row that already carries an id; only after clearing plan_families.stripe_product_id and plan_prices.stripe_price_id as well as the stripe_plan_prices and stripe_addon_prices registers does it recreate the catalogue in the active account.',
    ],

    'onboarding' => [
        'ssh_key' => 'SSH key for hosts',
        'ssh_key_breaks' => 'EstablishSshTrust only deploys the public half onto the host; every step after it — and every Traefik change on a host already live — connects with this private key. Without it, onboarding stalls at the second step, and no routing update reaches a host that is already running.',
        'secrets_key' => 'Vault key (SECRETS_KEY)',
        'secrets_key_breaks' => 'Without it, nothing can be stored or read in this installation — not the Stripe key, not the DNS token, no host token. The console shows every one of these fields as empty, no matter what was ever entered.',
        'vpn_config_key' => 'VPN config key (VPN_CONFIG_KEY)',
        'vpn_config_key_breaks' => 'Without it, ConfigVault refuses to store any VPN config rather than write it in the clear — an issued access can then not be looked up anywhere again.',
        'wg_hub' => 'WireGuard hub fully configured',
        'wg_hub_breaks' => 'Two of three values are not half a tunnel, they are none: ConfigureWireguard writes the peer config from all three at once. Missing one leaves the new host with no management tunnel, and nobody can reach it.',
        'datacenter' => 'At least one datacenter',
        'datacenter_breaks' => 'Without a datacenter the host-create form has nothing to offer, and ValidateHostInput aborts as the very first step of the chain — before an SSH connection is even attempted.',
    ],

    'provisioning' => [
        'dns_token' => 'Hetzner DNS token',
        'dns_token_breaks' => 'Without it, ConfigureDnsAndTls fails to create the A record for the new instance — in the middle of provisioning, after the machine is already running and the customer has already paid.',
        'dns_zone' => 'DNS zone',
        'dns_zone_breaks' => 'Empty, and HttpHetznerDnsClient asks Hetzner for a zone with no name: zoneId() finds none, throws "DNS zone not found", and ConfigureDnsAndTls fails at exactly the point the missing DNS token already breaks — in the middle of provisioning, after the customer has already paid.',
        'traefik_path' => 'Traefik config path',
        'traefik_path_breaks' => 'Empty, and SshTraefikWriter writes the route file at the filesystem root instead of Traefik\'s own directory. The step still reports success — Traefik simply never sees the new route, and the customer\'s cloud stays unreachable at its address.',
        'usable_host' => 'At least one usable host',
        'usable_host_breaks' => 'An active host with no readable token is still picked by ReserveResources for a new order. The order then dies in CloneVirtualMachine — after the customer has already paid.',
        'vm_template' => 'VM template for every published version',
        'vm_template_breaks' => 'A published version with no template_vmid makes CloneVirtualMachine fail instantly with "template_missing" — for every order of that plan, after the customer has already paid.',
        'monitoring_token' => 'Monitoring token',
        'monitoring_token_breaks' => 'Without it, RegisterMonitoring fails. With `monitoring.required` unset (the default), the cloud still gets delivered, just without monitoring, until the key is added. With it set, the run aborts instead, and delivery stalls until the key is there.',
    ],

    'delivery' => [
        'mailer_not_log' => 'Mail transport actually delivers',
        'mailer_not_log_breaks' => 'With mail.default=log, =array, or no default configured at all, the machine runs, the invoice is issued, and the mail announcing the finished cloud never leaves the server — it lands in a file or vanishes without a trace. The customer never learns they paid for something that is running.',
        'mailbox' => 'Sending mailbox can actually send',
        'mailbox_breaks' => 'No usable mailbox is mapped to the [system] purpose — it is missing, switched off, or it authenticates with no password stored. Every system mail then dies in the queue: password reset, address confirmation, new-device warning. The customer reads "check your inbox", and the outbox shows nothing, because nothing was ever sent.',
        'mail_templates' => 'At least one reply template',
        'mail_templates_breaks' => 'On the customer page, the template picker sits empty where it would otherwise offer ready answers to click — the support agent notices immediately on opening it, finds nothing to insert, and types every reply from scratch. Two customers asking the same question can then easily get two different answers, with no error appearing anywhere.',
        'inbound_password' => 'Inbound mail password',
        'inbound_password_breaks' => 'Without it, fetching mail returns an empty list every time. The schedule keeps running quietly, but no customer reply ever reaches the console inbox, and no error appears anywhere.',
        'mail_lanes_moving' => 'Mail lanes are moving',
        'mail_lanes_moving_breaks' => 'A stalled lane looks, from the outside, exactly like an empty one: the worker raises no error, nothing appears anywhere. If a lane\'s oldest job has sat for more than an hour, it is jammed — depending on the lane, an invoice, a maintenance notice, or even a password reset goes undelivered until a customer calls in asking why.',
    ],

    'operation' => [
        'scheduler' => 'Scheduler is running',
        'scheduler_breaks' => 'Without it, not a single schedule in routes/console.php runs — not invoice archiving, not the contract check, not this heartbeat itself. A paid order waits on TickProvisioning, which is never invoked, and the console shows the same calm as a day with no orders at all.',
        'queue_provisioning' => 'Provisioning worker is running',
        'queue_provisioning_breaks' => 'The scheduler can be running and putting jobs on the provisioning queue without anyone ever picking them up. A paid order then just sits there — no error, no notice, simply nothing, until someone happens to look.',
    ],

    'restart_workers' => 'Restart the workers',
    'restart_requested' => 'Restart requested — scheduler, queues and Reverb come back in a few seconds.',
    'restart_busy' => 'A request is already waiting for the agent. Give it a moment and try again.',
    'restart_no_agent' => 'No agent is running on this server to carry out the restart — here only restarting the containers by hand helps.',
    'env_only' => 'This value deliberately has no field: it encrypts everything else stored here. Anyone able to set it from the console could replace the protection over every credential. It lives in the server file — "Fix" opens its editor.',
    'zone_wanted' => 'The token is valid — it fetched the zone list. It looked for the zone :zone.',
    'zone_available' => 'This account holds: :zones. Either adjust CLUPILOT_DNS_ZONE or create the zone at Hetzner.',
    'zone_none' => 'This account holds no zones at all. The token probably belongs to a different Hetzner project.',
    'zone_list_unclear' => 'Hetzner did not return the zone list (HTTP :status). That says nothing about this account\'s zones — the token may still be correct.',
    'zone_list_unreadable' => 'The response came back with HTTP :status but was not a zone list. Something else answered — a portal, a filter, a proxy. Start of the response: :body',
    'write_failed' => 'The write attempt was rejected with HTTP :status. That says nothing about the token\'s permissions — those come back as 401 or 403. Hetzner says: :detail',

    'security' => [
        'console_restricted' => 'The console is not reachable by everyone',
        'console_restricted_breaks' => 'Without the restriction, any address in the world can see the console sign-in page and try passwords. With it, they get a 404 and never learn it exists.',
        'trusted_ranges' => 'TRUSTED_RANGES holds only what belongs there',
        'trusted_ranges_breaks' => 'Anything beyond that was written into the .env by hand — and the console shows it as "not removable". At the next change of connection it cannot be changed. Such addresses belong in the "Additionally allowed" list, which you maintain yourself.',
        'ports_loopback' => 'The application is not exposed to the internet directly',
        'ports_loopback_breaks' => 'Docker inserts published ports BEFORE the firewall: a service on 0.0.0.0 is reachable from outside even when ufw looks closed. Reaching it directly skips the reverse proxy — and with it that proxy\'s access list and TLS. APP_PORT and REVERB_HOST_PORT belong on 127.0.0.1.',
    ],
];
