<?php

namespace Drupal\nodehive_public_api\Service;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Menu\MenuLinkTreeInterface;
use Drupal\Core\Menu\MenuTreeParameters;
use Drupal\Core\Session\AccountProxyInterface;
use Psr\Log\LoggerInterface;

/**
 * Service for building menu trees with access control.
 */
class MenuTreeBuilderService {

  /**
   * The menu link tree service.
   *
   * @var \Drupal\Core\Menu\MenuLinkTreeInterface
   */
  protected $menuLinkTree;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountProxyInterface
   */
  protected $currentUser;

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The language manager.
   *
   * @var \Drupal\Core\Language\LanguageManagerInterface
   */
  protected $languageManager;

  /**
   * The logger.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected $logger;

  /**
   * Constructs a MenuTreeBuilderService object.
   *
   * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_link_tree
   *   The menu link tree service.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
   *   The entity type manager.
   * @param \Drupal\Core\Session\AccountProxyInterface $current_user
   *   The current user.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
   *   The language manager.
   * @param \Psr\Log\LoggerInterface $logger
   *   The logger.
   */
  public function __construct(
    MenuLinkTreeInterface $menu_link_tree,
    EntityTypeManagerInterface $entity_type_manager,
    AccountProxyInterface $current_user,
    ModuleHandlerInterface $module_handler,
    LanguageManagerInterface $language_manager,
    LoggerInterface $logger,
  ) {
    $this->menuLinkTree = $menu_link_tree;
    $this->entityTypeManager = $entity_type_manager;
    $this->currentUser = $current_user;
    $this->moduleHandler = $module_handler;
    $this->languageManager = $language_manager;
    $this->logger = $logger;
  }

  /**
   * Builds a menu tree as an array.
   *
   * @param string $menu_name
   *   The menu name.
   * @param string $language
   *   The language code.
   *
   * @return array
   *   The menu tree data.
   */
  public function buildMenuTree(string $menu_name, string $language): array {
    // Set up menu tree parameters.
    $parameters = new MenuTreeParameters();
    $parameters->onlyEnabledLinks();

    // Load the menu tree.
    $tree = $this->menuLinkTree->load($menu_name, $parameters);

    // Apply access checks and transformations.
    $manipulators = [
      ['callable' => 'menu.default_tree_manipulators:checkAccess'],
      ['callable' => 'menu.default_tree_manipulators:generateIndexAndSort'],
    ];
    $tree = $this->menuLinkTree->transform($tree, $manipulators);

    // Build the tree structure.
    return $this->buildTreeData($tree, $language);
  }

  /**
   * Recursively builds the menu tree data structure.
   *
   * @param array $tree
   *   The menu tree elements.
   * @param string $language
   *   The language code.
   *
   * @return array
   *   The processed menu tree data.
   */
  protected function buildTreeData(array $tree, string $language): array {
    $items = [];

    foreach ($tree as $element) {
      // Skip if access is denied.
      if (!$element->access->isAllowed()) {
        continue;
      }

      /** @var \Drupal\Core\Menu\MenuLinkInterface $link */
      $link = $element->link;

      // Build the item data.
      $item = [
        'title' => $link->getTitle(),
        'url' => $this->getUrl($link, $language),
        'description' => $link->getDescription(),
        'enabled' => $link->isEnabled(),
        'expanded' => $link->isExpanded(),
        'weight' => $link->getWeight(),
      ];

      // Attributes are only available with the menu_link_attributes module.
      $options = $link->getOptions();
      if (!empty($options['attributes'])) {
        $item['attributes'] = $options['attributes'];
      }

      // Add children if they exist.
      if ($element->subtree) {
        $item['children'] = $this->buildTreeData($element->subtree, $language);
      }

      $items[] = $item;
    }

    return $items;
  }

  /**
   * Gets the URL for a menu link.
   *
   * @param \Drupal\Core\Menu\MenuLinkInterface $link
   *   The menu link.
   * @param string $language
   *   The language code.
   *
   * @return string|null
   *   The URL string or NULL if not available.
   */
  protected function getUrl($link, string $language): ?string {
    try {
      $url = $link->getUrlObject();
      if ($url->isRouted()) {
        $url->setOption('language', $this->getLanguageFromCode($language));
        return $url->toString();
      }
      elseif ($url->isExternal()) {
        return $url->toString();
      }
    }
    catch (\Exception $e) {
      $this->logger->error(
        'Error generating URL for menu link @link: @message',
        ['@link' => $link->getTitle(), '@message' => $e->getMessage()]
      );
    }

    return NULL;
  }

  /**
   * Gets a language object from language code.
   *
   * @param string $langcode
   *   The language code.
   *
   * @return \Drupal\Core\Language\LanguageInterface|null
   *   The language object or NULL.
   */
  protected function getLanguageFromCode(string $langcode): ?LanguageInterface {
    $languages = $this->languageManager->getLanguages();
    return $languages[$langcode] ?? NULL;
  }

}
