<?php

namespace Apexl\Io\Menu\Services;

use Apexl\Io\Display\Components\Menu;
use Apexl\Io\HookManager;
use Apexl\Io\Menu\Entities\MenuEntity;
use Apexl\Io\Menu\MenuItem;
use Apexl\Io\User\Entities\UserEntity;
use UnitEnum;

/**
 * Class Menu
 * @package apexl\Io\modules\menu\services
 * //@todo this needs caching
 */
final class MenuManager
{
    final public const string HOOK__ADD_TO_MENU__ROUTE_NAME = 'menu.addToMenu.routeName';
    final public const string HOOK__ADD_TO_MENU__CONFIG = 'menu.addToMenu.config';
    /** Public — Phase 5 (quiet mode) consumers (e.g. NavGroupsController)
     *  reference this so "the default sidebar menu" isn't a re-typed
     *  magic string in multiple packages. */
    public const string DEFAULT_MENU = 'NAVIGATION';

    /**
     * @var MenuEntity[]
     */
    protected array $menus = [];

    public function __construct(
        private readonly HookManager     $hookManager,
        private readonly NavFilterRegistry $navFilterRegistry,
    ) {}

    public function addToMenu(
        string $routeName,
        array $config = [],
        string|UnitEnum $menu = self::DEFAULT_MENU,
    ): void {
        $menu = $this->resolveMenuName($menu);

        if (!isset($this->menus[$menu])) {
            $this->menus[$menu] = new MenuEntity();
        }

        $routeName = $this->hookManager->processHook(self::HOOK__ADD_TO_MENU__ROUTE_NAME, $routeName, $menu, $config);
        $config = $this->hookManager->processHook(self::HOOK__ADD_TO_MENU__CONFIG, $config, $menu, $routeName);

        $this->menus[$menu]->addToMenu(
            $routeName,
            $config['title'] ?? '',
            $config['icon'] ?? '',
            $config['weight'] ?? 0,
            $config['parent'] ?? false,
            $config['replacements'] ?? []
        );
    }

    private function resolveMenuName(UnitEnum|string $menu): string
    {
        if (!is_string($menu)) {
            $menu = $menu->name;
        }

        return strtoupper($menu);
    }

    public function getMenuItem($routeName, string|UnitEnum $menu = self::DEFAULT_MENU): ?MenuItem
    {
        if (isset($this->menus[$menu])) {
            return $this->menus[$menu]->getMenuItem($routeName);
        }

        return null;
    }

    public function generateBreadcrumbs(string $route, string|UnitEnum $menu = self::DEFAULT_MENU): array
    {
        return $this->menus[$menu]->generateBreadcrumbs($route);
    }

    /**
     * This method allows us to take all registered routes and build a menu with them. We register routes as their own dynamic entity
     *
     * @param bool $applyNavFilters Phase 5 (quiet mode) — run the permission-
     *        checked result through every hook-collected NavFilterInterface
     *        (menu.collectNavFilters, see NavFilterRegistry), passing the
     *        RESOLVED menu key (e.g. 'NAVIGATION') so a filter can tell
     *        which logical menu this is — menuEntity instances all carry
     *        the same hardcoded ->name regardless of which menu key they're
     *        stored under, so that discrimination can only happen here.
     *        Defaults true so existing callers (sidebar/shell rendering)
     *        pick up nav-allowlist filtering with zero call-site changes.
     *        Pass false for a catalogue endpoint that must NOT have
     *        already had a filter narrow its result (e.g. building an
     *        allowlist picker's options).
     */
    public function getAllItemsAsMenu(
        UserEntity $user,
        string|UnitEnum $menu = self::DEFAULT_MENU,
        bool $filterByPermission = true,
        bool $applyNavFilters = true,
    ): array {
        $menuKey = $this->resolveMenuName($menu);

        if (!isset($this->menus[$menuKey])) {
            return [];
        }

        //We cache generated menu items so that the order can be controlled by admin users.
        $items = $this->menus[$menuKey]->getAllItemsAsMenu($user, $filterByPermission);

        if ($applyNavFilters) {
            foreach ($this->navFilterRegistry->all()->all() as $navFilter) {
                $items = $navFilter->filter($items, $user, $menuKey);
            }
        }

        return $items;
    }

    public function getMenu(string|UnitEnum $menu = self::DEFAULT_MENU): MenuEntity
    {
        $menu = $this->resolveMenuName($menu);

        return $this->menus[$menu] ?? \Apexl\Io\container()->get(MenuEntity::class);
    }

    /**
     * Get the name of the Menu containing the current route.
     * Return first menu if no match found
     * @return $menu (object)
     */
    public function getActiveMenu(): ?Menu
    {
        foreach ($this->menus as $menu) {
            $firstMenu = $menu;
            if ($menu->isCurrentMenu()) {
                return $menu;
            }
        }

        return $firstMenu ?? null;
    }
}
