<?php

namespace Drupal\nodehive_core\Render\Element;

use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Url;

/**
 * Pre-render callback for the NodeHive toolbar.
 *
 * Replaces the standard admin menu with hook-based NodeHive nav items,
 * rendered as a proper Drupal menu tree compatible with Gin toolbar.
 * For UID=1, appends the standard admin menu after the NodeHive items.
 */
class NodeHiveToolbar implements TrustedCallbackInterface {

  /**
   * {@inheritdoc}
   */
  public static function trustedCallbacks() {
    return ['preRender'];
  }

  /**
   * Pre-render callback for the administration tray.
   *
   * Collects items from hook_nodehive_toolbar_nav_items(), builds them
   * as a Drupal menu render array, and optionally merges the standard
   * admin menu for UID=1.
   *
   * @param array $element
   *   The toolbar administration tray element.
   *
   * @return array
   *   The modified element.
   */
  public static function preRender(array $element) {
    $current_user = \Drupal::currentUser();
    $module_handler = \Drupal::moduleHandler();
    $access_manager = \Drupal::service('access_manager');
    $route_name = \Drupal::routeMatch()->getRouteName();

    // Collect nav items from all modules.
    $nav_items = $module_handler->invokeAll('nodehive_toolbar_nav_items');
    $module_handler->alter('nodehive_toolbar_nav_items', $nav_items);

    // Filter by role.
    $user_roles = $current_user->getRoles();
    $nav_items = array_filter($nav_items, function ($item) use ($user_roles) {
      if (empty($item['roles'])) {
        return TRUE;
      }
      return !empty(array_intersect($item['roles'], $user_roles));
    });

    // Filter by route access.
    $nav_items = array_filter($nav_items, function ($item) use ($access_manager, $current_user) {
      if (empty($item['route'])) {
        return TRUE;
      }
      try {
        return $access_manager->checkNamedRoute($item['route'], $item['route_parameters'] ?? [], $current_user);
      }
      catch (\Exception $e) {
        return FALSE;
      }
    });

    // Sort by weight.
    usort($nav_items, fn($a, $b) => ($a['weight'] ?? 0) <=> ($b['weight'] ?? 0));

    // Build #items compatible with menu--toolbar--gin.html.twig.
    $current_path = \Drupal::request()->getRequestUri();
    $items = [];
    foreach ($nav_items as $item) {
      $id = $item['id'];
      $in_active_trail = in_array($route_name, $item['routes'] ?? []);
      // Support path_match for pattern-based active state (e.g. '/space/\d+').
      if (!$in_active_trail && !empty($item['path_match'])) {
        $in_active_trail = (bool) preg_match('#' . $item['path_match'] . '#', strtok($current_path, '?'));
      }

      // Build children.
      $below = [];
      if (!empty($item['children'])) {
        $children = array_filter($item['children'], function ($child) use ($access_manager, $current_user) {
          if (empty($child['route'])) {
            return TRUE;
          }
          try {
            return $access_manager->checkNamedRoute($child['route'], $child['route_parameters'] ?? [], $current_user);
          }
          catch (\Exception $e) {
            return FALSE;
          }
        });
        usort($children, fn($a, $b) => ($a['weight'] ?? 0) <=> ($b['weight'] ?? 0));

        foreach ($children as $child) {
          $child_id = $child['id'];
          $child_active = in_array($route_name, $child['routes'] ?? []);
          if ($child_active) {
            $in_active_trail = TRUE;
          }
          $child_system_path = ltrim($child['url'], '/');
          $url = Url::fromUserInput($child['url']);
          $url->setOption('set_active_class', TRUE);
          $url->setOption('attributes', [
            'class' => ['toolbar-icon', 'toolbar-icon-' . $child_id],
            'data-drupal-link-system-path' => $child_system_path,
          ]);
          $below[$child_id] = [
            'title' => $child['label'],
            'url' => $url,
            'attributes' => new Attribute(),
            'is_expanded' => FALSE,
            'is_collapsed' => FALSE,
            'in_active_trail' => $child_active,
            'gin_id' => $child_id,
            'below' => [],
          ];
        }
      }

      $has_children = !empty($below);
      $system_path = ltrim($item['url'], '/');
      $url = Url::fromUserInput($item['url']);
      $url->setOption('set_active_class', TRUE);
      $url->setOption('attributes', [
        'class' => ['toolbar-icon', 'toolbar-icon-' . $id],
        'data-drupal-link-system-path' => $system_path,
      ]);
      $items[$id] = [
        'title' => $item['label'],
        'url' => $url,
        'attributes' => new Attribute(),
        'is_expanded' => $has_children,
        'is_collapsed' => FALSE,
        'in_active_trail' => $in_active_trail,
        'gin_id' => $id,
        'below' => $below,
      ];
    }

    // Build the menu render array — same structure as MenuLinkTree::build().
    $element['administration_menu'] = [
      '#sorted' => TRUE,
      '#theme' => 'menu__toolbar__gin',
      '#items' => $items,
      '#cache' => [
        'contexts' => ['user.roles', 'route', 'user.permissions'],
      ],
    ];

    // For UID=1, add "Administration" item with Drupal admin menu as children.
    if ($current_user->id() == 1) {
      $toolbar_menu_tree = \Drupal::service('toolbar.menu_tree');
      $active_trail = $module_handler->moduleExists('gin_toolbar')
        ? \Drupal::service('gin_toolbar.active_trail')
        : \Drupal::service('menu.active_trail');

      $parameters = new MenuTreeParameters();
      $parameters->onlyEnabledLinks();
      $parameters->setMinDepth(1);
      $parameters->setRoot('system.admin');
      $parameters->setActiveTrail($active_trail->getActiveTrailIds('admin'));

      if ($module_handler->moduleExists('admin_toolbar')) {
        $admin_toolbar_settings = \Drupal::config('admin_toolbar.settings');
        $max_depth = $admin_toolbar_settings->get('menu_depth') ?? 4;
        $parameters->setMaxDepth($max_depth);
      }

      $tree = $toolbar_menu_tree->load('admin', $parameters);
      $manipulators = [
        ['callable' => 'menu.default_tree_manipulators:checkAccess'],
        ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
        ['callable' => 'toolbar_menu_navigation_links'],
      ];
      if ($module_handler->moduleExists('gin_toolbar')) {
        $manipulators[] = ['callable' => 'gin_toolbar_tools_menu_navigation_links'];
      }
      $tree = $toolbar_menu_tree->transform($tree, $manipulators);
      $admin_menu = $toolbar_menu_tree->build($tree);

      // Wrap admin menu items under an "Administration" parent item.
      if (!empty($admin_menu['#items'])) {
        $admin_url = Url::fromRoute('system.admin');
        $admin_url->setOption('set_active_class', TRUE);
        $admin_url->setOption('attributes', [
          'class' => ['toolbar-icon', 'toolbar-icon-nodehive-administration'],
          'data-drupal-link-system-path' => 'admin',
        ]);
        $items['nodehive-administration'] = [
          'title' => t('Administration'),
          'url' => $admin_url,
          'attributes' => new Attribute(),
          'is_expanded' => TRUE,
          'is_collapsed' => FALSE,
          'in_active_trail' => FALSE,
          'gin_id' => 'nodehive-administration',
          'below' => $admin_menu['#items'],
        ];
        $element['administration_menu']['#items'] = $items;
      }
      $element['#cache']['contexts'][] = 'route.menu_active_trails:admin';
    }

    return $element;
  }

}
