<?php
/*
** Copyright (C) 2021-2026 initMAX s.r.o.
**
** This program is free software: you can redistribute it and/or modify it under the terms of
** the GNU Affero General Public License as published by the Free Software Foundation, version 3.
**
** This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
** without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
** See the GNU Affero General Public License for more details.
**
** You should have received a copy of the GNU Affero General Public License along with this program.
** If not, see <https://www.gnu.org/licenses/>.
**/


namespace Modules\uxMAX\Includes;

use APP;
use CMenu;
use CMenuItem;
use CWebUser;
use Modules\uxMAX\Services\ModuleTranslator;

/**
 * uxMAX's two sidebar entries - written ONCE for both module trees.
 *
 * A module's init() runs on EVERY authenticated request, so anything that
 * fatals here blanks the whole frontend, not just uxMAX's own pages. Two
 * specific traps this guards against, both of which have bitten this fleet:
 *
 *  - `CMenu::find()` returns null when the section is absent (a renamed or
 *    reorganised menu, a restricted role). Calling ->getSubMenu() on null is a
 *    fatal on every page.
 *  - `CMenuItem::getSubMenu()` CREATES an empty submenu when there is none. So
 *    aiming at a LEAF does not fail loudly - it turns that leaf into an
 *    expandable section and throws away the action it used to link to. On
 *    Zabbix 6.0 through 7.0 "User settings" IS a leaf for any role without API
 *    token management (7.2 was the first to give it a submenu), so this is a
 *    live case across most of the supported range, not a theoretical one.
 */
final class MenuRegistrar {

    /** Both entries: the admin page and the per-user page. */
    public static function register(): void {
        self::addAdminEntry();
        self::addUserEntry();
    }

    /**
     * Admin entry only - used when the admin has switched per-user overrides
     * off, so "My uxMAX" would open a page that refuses the request anyway.
     */
    public static function registerAdminOnly(): void {
        self::addAdminEntry();
    }

    /** "uxMAX configuration" under Administration - super admins only. */
    private static function addAdminEntry(): void {
        $main = APP::Component()->get('menu.main');

        if (!$main instanceof CMenu) {
            return;
        }

        $administration = $main->find(_('Administration'));

        if (!$administration instanceof CMenuItem) {
            return;
        }

        self::addChild(
            $administration,
            ModuleTranslator::translate('menu.uxmax-configuration'),
            'mod.uxmax.form'
        );
    }

    /**
     * "My uxMAX" under User settings. Hidden for guest, and hidden entirely
     * when the admin has switched per-user overrides off - the page itself
     * refuses the same way, so the menu never points at a 403.
     */
    private static function addUserEntry(): void {
        if (CWebUser::isGuest()) {
            return;
        }

        $user_menu = APP::Component()->get('menu.user');

        if (!$user_menu instanceof CMenu) {
            return;
        }

        $user_settings = $user_menu->find(_('User settings'));

        if (!$user_settings instanceof CMenuItem) {
            return;
        }

        self::addChild(
            $user_settings,
            ModuleTranslator::translate('menu.uxmax-user-preferences'),
            'mod.uxmax.userprefs',
            // What the leaf linked to, if it turns out to be one: the profile
            // page. Zabbix calls that child "Profile" in its own submenu from
            // 7.2 on, so reusing the label keeps the menu reading the same on
            // every version instead of nesting "User settings" under itself.
            _('Profile')
        );
    }

    /**
     * Hang an entry off a menu section, converting a LEAF into a section first
     * rather than letting getSubMenu() silently manufacture an empty one.
     */
    private static function addChild(CMenuItem $parent, string $label, string $action,
            ?string $preserved_label = null): void {
        if (!$parent->hasSubMenu()) {
            $own_action = $parent->getAction();

            if ($own_action === null) {
                // A leaf with no action of its own: there is nothing to
                // preserve and nothing that would render. Leave it alone.
                return;
            }

            // Keep what the leaf pointed at as its first child - the shape
            // Zabbix itself uses where it ships a submenu. "User settings" is a
            // LEAF on 6.0 through 7.0 for any role without API token
            // management, so this path is live across most of the range, not a
            // defensive branch: without it getSubMenu() manufactures an empty
            // submenu and the profile link is simply gone.
            $parent->setSubMenu(new CMenu([
                (new CMenuItem($preserved_label !== null ? $preserved_label : $parent->getLabel()))
                    ->setAction($own_action)
            ]));
        }

        $parent->getSubMenu()->add((new CMenuItem($label))->setAction($action));
    }
}
