<?php

namespace Drupal\modeler\Hook;

use Drupal\Core\Hook\Attribute\Hook;
use Drupal\Core\Htmx\Htmx;
use Drupal\Core\Routing\RouteProviderInterface;
use Drupal\modeler_api\Plugin\ModelerPluginManager;

/**
 * Implements link hooks for the Modeler module.
 */
class LinkHooks {

  /**
   * The modeler API controller class.
   */
  protected const string MODELER_API_CONTROLLER = 'Drupal\modeler_api\Controller\ModelerApi';

  /**
   * Constructs a new LinkHooks object.
   */
  public function __construct(
    protected RouteProviderInterface $routeProvider,
    protected ModelerPluginManager $pluginManager,
  ) {}

  /**
   * Implements hook_link_alter().
   *
   * Applies HTMX attributes to links pointing to modeler_api edit or view
   * routes, enabling inline content loading within the workflow modeler.
   */
  #[Hook('link_alter')]
  public function linkAlter(array &$variables): void {
    /** @var \Drupal\Core\Url $url */
    $url = $variables['url'];
    if (!$url->isRouted()) {
      return;
    }
    if (count($this->pluginManager->getDefinitions()) !== 2) {
      // Let's only apply the hook if this modeler and the fallback is
      // available, otherwise the user still needs the choice.
      return;
    }
    $routeName = $url->getRouteName();
    if (!$this->isModelerApiEditOrViewRoute($routeName)) {
      return;
    }
    // Build a temporary render array to apply HTMX attributes.
    $item = [
      '#url' => $url,
    ];
    (new Htmx())
      ->get($item['#url'])
      ->onlyMainContent()
      ->select('#workflow-modeler-wrapper')
      ->target('.page-wrapper')
      ->swap('afterbegin')
      ->applyTo($item);
    // Merge the HTMX attributes back into the link variables.
    $variables['options']['attributes'] = array_merge(
      $variables['options']['attributes'] ?? [],
      $item['#attributes'] ?? [],
    );
    if (!empty($item['#attached']) && is_array($item['#attached'])) {
      $variables['options']['attached'] = array_merge(
        $variables['options']['attached'] ?? [],
        $item['#attached'],
      );
    }
  }

  /**
   * Checks if a route is a modeler_api edit or view route.
   *
   * @param string $routeName
   *   The route name to check.
   *
   * @return bool
   *   TRUE if the route uses the modeler_api edit or view controller.
   */
  protected function isModelerApiEditOrViewRoute(string $routeName): bool {
    try {
      $route = $this->routeProvider->getRouteByName($routeName);
    }
    catch (\Exception) {
      return FALSE;
    }
    $controller = $route->getDefault('_controller');
    if ($controller === NULL) {
      return FALSE;
    }
    return $controller === self::MODELER_API_CONTROLLER . '::edit'
      || $controller === self::MODELER_API_CONTROLLER . '::view';
  }

}
