<?php

namespace Drupal\component_builder;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\CategorizingPluginManagerTrait;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
use Drupal\Core\Plugin\Discovery\YamlDiscoveryDecorator;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Traversable;

/**
 * Plugin manager for navigation components.
 */
class ComponentBuilderManager extends DefaultPluginManager implements ComponentBuilderManagerInterface {

  use CategorizingPluginManagerTrait;

  /**
   * Constructs a new ComponentBuilderManager object.
   */
  public function __construct(\Traversable $namespaces, CacheBackendInterface $cacheBackend, ModuleHandlerInterface $moduleHandler) {
    parent::__construct(
      'Plugin/ComponentBuilder',
      $namespaces,
      $moduleHandler,
      'Drupal\component_builder\ComponentBuilderPluginInterface',
      'Drupal\component_builder\Annotation\ComponentBuilder',
    );
    $this->alterInfo('component_builder_info');
    $this->setCacheBackend($cacheBackend, 'component_builder_plugins');
  }

  /**
   * {@inheritdoc}
   */
  public function processDefinition(&$definition, $pluginId): void {
    parent::processDefinition($definition, $pluginId);
    $this->processDefinitionGroup($definition);
  }

  /**
   * Ensure that every component belong to a group.
   */
  protected function processDefinitionGroup(&$definition): void {
    if (empty($definition['group'])) {
      // Default to the human readable module name if the provider is a module;
      // otherwise the provider machine name is used.
      $definition['group'] = $this->getProviderName($definition['provider']);
    }

    if (is_array($definition)) {
      if (empty($definition['class'])) {
        $definition['class'] = 'Drupal\component_builder\ComponentBuilderBase';
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  protected function getDiscovery() {
    if (!$this->discovery) {
      $discovery = new AnnotatedClassDiscovery(
        $this->subdir,
        $this->namespaces,
        $this->pluginDefinitionAnnotationName,
        $this->additionalAnnotationNamespaces
      );

      $module_directories = $this->moduleHandler->getModuleDirectories();
      // Also allow modules to declare components
      // in mymodule.component_builder.yml files.
      $discovery = new YamlDiscoveryDecorator($discovery, 'component_builder', $module_directories);
      // Plugins defined in YML files are scalar text strings,
      // therefore not translated yet.
      $discovery->addTranslatableProperty('label', 'component_builder');
      $discovery->addTranslatableProperty('group', 'component_builder');

      $discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
      $this->discovery = $discovery;
    }

    return $this->discovery;
  }

  public function getFallbackPluginId($plugin_id, array $configuration = []) {
    return 'broken';
  }

  /**
   * Gets an instance of component plugin using template name.
   */
  public function getInstanceByTemplateName(string $template = ''): ?ComponentBuilderPluginInterface {
    $definitions = $this->getDefinitions();

    foreach ($definitions as $plugin_id => $definition) {
      if ($definition['template'] === $template) {
        return $this->createInstance($plugin_id);
      }
    }
    return NULL;
  }

  /**
   * Gets plugins by group.
   */
  public function getGroupCollectionWithPlugin(string $plugin_id = NULL) {
    $definitions = $this->getDefinitions();
    $groups = [];
    foreach ($definitions as $config) {
      $group = $config['group'] ? $config['group']->getUntranslatedString() : '';
      $group_translation = $config['group'] ? $config['group']->__toString() : '';
      $label = $config['label'] ? $config['label']->__toString() : '';
      $groups[$config['id']] = [
        'group' => $group,
        'group_trans' => $group_translation,
        'label' => $label,
      ];
    }

    if ($plugin_id) {
      return $groups[$plugin_id];
    }

    return $groups;
  }

}
