<?php

declare(strict_types=1);

namespace Drupal\flowdrop_workflow\Service;

use Drupal\Core\Access\AccessResultInterface;
use Drupal\Core\Menu\LocalActionManagerInterface;
use Drupal\Core\Menu\LocalTaskManagerInterface;
use Drupal\Core\Routing\RouteMatchInterface;

/**
 * Builds navbar items from a route's Drupal local tasks and local actions.
 *
 * The editor and playground pages render fullscreen and omit Drupal's tab and
 * action regions, so those links are surfaced in the FlowDrop navbar instead.
 * Any contrib module that adds a *.links.task.yml entry against the shared base
 * route, or a *.links.action.yml entry whose `appears_on` lists one of these
 * routes, appears automatically.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
final class NavbarMenuBuilder {

  public function __construct(
    protected LocalTaskManagerInterface $localTaskManager,
    protected LocalActionManagerInterface $localActionManager,
    protected RouteMatchInterface $routeMatch,
  ) {}

  /**
   * Builds navbar items from the accessible local tasks of a route.
   *
   * The current page is excluded — the navbar is a switcher to other views,
   * so a link back to where the user already is would be noise.
   *
   * @param string $route_name
   *   The route whose local task set should be converted.
   *
   * @return array<int, array{label: string, href: string}>
   *   Navbar items in weight order.
   */
  public function localTasks(string $route_name): array {
    $tree = $this->localTaskManager->getLocalTasks($route_name, 0);
    return $this->convert($tree['tabs'], TRUE);
  }

  /**
   * Builds navbar items from the accessible local actions of a route.
   *
   * @param string $route_name
   *   The route the actions appear on.
   *
   * @return array<int, array{label: string, href: string}>
   *   Navbar items in weight order.
   */
  public function localActions(string $route_name): array {
    return $this->convert($this->localActionManager->getActionsForRoute($route_name), FALSE);
  }

  /**
   * Converts local task/action render elements into navbar items.
   *
   * @param array $elements
   *   Render elements keyed by plugin id, each with #link, #access and
   *   optionally #active and #weight (the shapes produced by the local task
   *   and local action managers).
   * @param bool $skip_active
   *   Whether to drop the element flagged as the active route.
   *
   * @return array<int, array{label: string, href: string}>
   *   Navbar items in weight order.
   */
  protected function convert(array $elements, bool $skip_active): array {
    uasort($elements, static fn ($a, $b) => ($a['#weight'] ?? 0) <=> ($b['#weight'] ?? 0));

    $items = [];
    foreach ($elements as $element) {
      // Skip non-link entries such as the #cache metadata the local action
      // manager attaches to the returned array.
      if (!is_array($element) || !isset($element['#link']['url'])) {
        continue;
      }

      if ($skip_active && !empty($element['#active'])) {
        continue;
      }

      $access = $element['#access'] ?? TRUE;
      $allowed = $access instanceof AccessResultInterface
        ? $access->isAllowed()
        : (bool) $access;
      if (!$allowed) {
        continue;
      }

      $link = $element['#link'];
      $items[] = [
        'label' => (string) $link['title'],
        'href' => $link['url']->toString(),
      ];
    }

    return $items;
  }

}
