<?php

namespace App\Support;

/**
 * The navigation of both shells, in one place.
 *
 * The sidebar and the breadcrumb describe the same structure, and when each
 * built its own copy the breadcrumb quietly said "Übersicht" on every page —
 * the layout had no way to know which entry was current, so it fell back to the
 * first one. One definition, two readers.
 *
 * Each entry is [route name, icon, translation key, capability or null].
 */
final class Navigation
{
    /** @return array<int, array{label: ?string, items: array<int, array{0:string,1:string,2:string,3:?string}>}> */
    public static function portal(): array
    {
        return [
            ['label' => null, 'items' => [
                ['dashboard', 'gauge', 'overview', null],
                ['cloud', 'cloud', 'cloud', null],
                // The only portal entry with a condition. Start carries no
                // custom domain and cannot buy one, so the tab would lead
                // straight to the 403 its page raises.
                ['domain', 'globe', 'domain', 'use-custom-domain'],
                ['users', 'users', 'users', null],
                ['backups', 'database', 'backups', null],
            ]],
            ['label' => __('dashboard.nav_group.contract'), 'items' => [
                ['billing', 'box', 'billing', null],
                ['invoices', 'receipt', 'invoices', null],
                ['settings', 'settings', 'settings', null],
                ['support', 'life-buoy', 'support', null],
            ]],
        ];
    }

    /** @return array<int, array{label: ?string, items: array<int, array{0:string,1:string,2:string,3:?string}>}> */
    public static function console(): array
    {
        return [
            ['label' => null, 'items' => [
                ['admin.overview', 'gauge', 'overview', null],
                ['admin.customers', 'users', 'customers', null],
                ['admin.instances', 'box', 'instances', null],
                ['admin.hosts', 'server', 'hosts', null],
                ['admin.datacenters', 'database', 'datacenters', null],
                ['admin.plans', 'tag', 'plans', 'plans.manage'],
            ]],
            ['label' => __('admin.nav_group.operations'), 'items' => [
                ['admin.provisioning', 'activity', 'provisioning', null],
                ['admin.maintenance', 'alert-triangle', 'maintenance', null],
                ['admin.incidents', 'bell', 'incidents', null],
                ['admin.capacity', 'database', 'capacity', 'hosts.manage'],
                ['admin.revenue', 'trending-up', 'revenue', null],
                // Its own entry, not a section of Settings: what is set here
                // appears on a legal document, and burying it beside the
                // site-visibility switch invites somebody to change one in passing.
                ['admin.finance', 'receipt', 'finance', 'site.manage'],
                ['admin.invoices', 'file-text', 'invoices', 'site.manage'],
                // What customers wrote to us, and what we sent them. Under
                // Betrieb rather than System: both are read while answering
                // somebody, not while configuring a machine.
                ['admin.inbox', 'mail', 'inbox', 'customers.manage'],
                ['admin.mail-log', 'send', 'mail_log', 'customers.manage'],
                // The answers, written once. Beside the two mail pages because
                // that is where they are used, not under System — nothing about
                // a template configures the installation.
                ['admin.templates', 'file-text', 'templates', 'customers.manage'],
            ]],
            ['label' => __('admin.nav_group.system'), 'items' => [
                ['admin.mail', 'mail', 'mail', 'mail.manage'],
                // System, not Betrieb: the tunnel is how the console reaches
                // the estate at all. Nothing is provisioned, monitored or
                // updated through anything else, so it belongs beside the other
                // things that have to work before any of the day's work can.
                ['admin.vpn', 'shield', 'vpn', null],
                // Merged from the former admin.secrets + admin.infrastructure
                // pages, which split the same subject by storage mechanism
                // instead of by what it configures. Reachable with EITHER
                // capability — see App\Livewire\Admin\Integrations — so the
                // entry stays visible to exactly who could reach at least one
                // of the two before: an array here means "any of these",
                // never "all of these" (contrast a single string elsewhere in
                // this file, which x-shell.nav checks with plain ->can()).
                ['admin.integrations', 'plug', 'integrations', ['hosts.manage', 'secrets.manage']],
                // Two-factor enrolment used to sit here as an entry of its
                // own. It is a tab of Settings now — it is a setting, and it
                // was the one place an operator had to leave the settings page
                // to change something about their own access. The standalone
                // page stays reachable at its route, because that is where
                // RequireOperatorTwoFactor sends somebody who may not open
                // anything else yet, Settings included.
                ['admin.settings', 'settings', 'settings', null],
            ]],
        ];
    }

    /**
     * The label of the entry the current request belongs to.
     *
     * Matched on the route rather than the path, because the console's path
     * changes between host-bound and fallback mode while its route names do
     * not. Null when the page is not in the navigation at all — a detail view,
     * a modal — and the caller then supplies its own title.
     */
    public static function currentLabel(bool $console = false): ?string
    {
        $prefix = $console ? 'admin.nav.' : 'dashboard.nav.';

        foreach ($console ? self::console() : self::portal() as $group) {
            foreach ($group['items'] as [$route, , $key]) {
                $matches = $console
                    ? AdminArea::routeIs($route)
                    : request()->routeIs($route);

                if ($matches) {
                    return __($prefix.$key);
                }
            }
        }

        return null;
    }
}
