<?php

/*
 +--------------------------------------------------------------------+
 | Copyright CiviCRM LLC. All rights reserved.                        |
 |                                                                    |
 | This work is published under the GNU AGPLv3 license with some      |
 | permitted exceptions and without any warranty. For full license    |
 | and copyright information, see https://civicrm.org/licensing       |
 +--------------------------------------------------------------------+
 */

/**
 *
 * @package CRM
 * @copyright CiviCRM LLC https://civicrm.org/licensing
 */

use Civi\Core\Event\EventScanner;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\Config\FileLocator;

class CRM_Api4_Services {

  /**
   * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
   */
  public static function hook_container($container) {
    $loader = new XmlFileLoader($container, new FileLocator(dirname(dirname(__DIR__))));
    $loader->load('Civi/Api4/services.xml');

    self::loadServices('Civi\Api4\Service\Spec\Provider', 'spec_provider', $container);
    self::loadServices('Civi\Api4\Event\Subscriber', 'event_subscriber', $container);

    $container->getDefinition('civi_api_kernel')->addMethodCall(
      'registerApiProvider',
      [new Reference('action_object_provider')]
    );

    // add event subscribers$container->get(
    $dispatcher = $container->getDefinition('dispatcher');
    $subscribers = $container->findTaggedServiceIds('event_subscriber');

    foreach (array_keys($subscribers) as $subscriber) {
      $listenerMap = EventScanner::findListeners($container->findDefinition($subscriber)->getClass());
      $dispatcher->addMethodCall('addSubscriberServiceMap', [$subscriber, $listenerMap]);
    }

    // add spec providers
    $providers = $container->findTaggedServiceIds('spec_provider');
    $gatherer = $container->getDefinition('spec_gatherer');

    foreach (array_keys($providers) as $provider) {
      $gatherer->addMethodCall(
        'addSpecProvider',
        [new Reference($provider)]
      );
    }
  }

  /**
   * Load all services in a given directory
   *
   * @param string $namespace
   * @param string $tag
   * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
   */
  public static function loadServices($namespace, $tag, $container) {
    $namespace = \CRM_Utils_File::addTrailingSlash($namespace, '\\');
    $locations = array_merge([\Civi::paths()->getPath('[civicrm.root]/Civi.php')],
      array_column(\CRM_Extension_System::singleton()->getMapper()->getActiveModuleFiles(), 'filePath')
    );
    foreach ($locations as $location) {
      $path = \CRM_Utils_File::addTrailingSlash(dirname($location)) . str_replace('\\', DIRECTORY_SEPARATOR, $namespace);
      if (!file_exists($path) || !is_dir($path)) {
        $resource = new \Symfony\Component\Config\Resource\FileExistenceResource($path);
        $container->addResource($resource);
      }
      else {
        $resource = new \Symfony\Component\Config\Resource\DirectoryResource($path, ';\.php$;');
        foreach (glob("$path*.php") as $file) {
          $matches = [];
          preg_match('/(\w*)\.php$/', $file, $matches);
          $serviceName = $namespace . array_pop($matches);
          $serviceClass = new \ReflectionClass($serviceName);
          if ($serviceClass->isInstantiable()) {
            $definition = $container->register(str_replace('\\', '_', $serviceName), $serviceName);
            $definition->addTag($tag);
            $definition->setPublic(TRUE);
          }
        }
        $container->addResource($resource);
      }
    }
  }

}
