<?php

declare(strict_types=1);

namespace Drupal\Tests\acquia_dam\Kernel;

use Drupal\acquia_dam\Plugin\media\acquia_dam\AssetMediaSourceManager;
use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;

/**
 * Tests \Drupal\acquia_dam\Plugin\media\acquia_dam\AssetMediaSourceManager.
 *
 * @group acquia_dam_kernel
 */
final class AssetMediaSourceManagerTest extends AcquiaDamKernelTestBase {

  /**
   * Instance of the service being tested, instantiated by the container.
   *
   * @var null|\Drupal\Component\Plugin\PluginManagerInterface
   */
  protected $assetMediaSourceManager;

  /**
   * {@inheritdoc}
   */
  protected static $modules = ['acquia_dam_test'];

  /**
   * Set up the test.
   */
  public function setUp(): void {
    parent::setUp();
    $this->assetMediaSourceManager = new AssetMediaSourceManager(
      $this->container->get('container.namespaces'),
      $this->container->get('cache.discovery'),
      $this->container->get('module_handler')
    );
  }

  /**
   * Test if the plugin manager complies to the basic requirements.
   */
  public function testCodeContract(): void {
    $this->assertInstanceOf(AssetMediaSourceManager::class, $this->assetMediaSourceManager);
    $this->assertInstanceOf(PluginManagerInterface::class, $this->assetMediaSourceManager);
    $this->assertInstanceOf(DefaultPluginManager::class, $this->assetMediaSourceManager);
    $this->assertInstanceOf(CachedDiscoveryInterface::class, $this->assetMediaSourceManager);
  }

  /**
   * Test the plugins we expect to be available.
   *
   * @dataProvider definitionsProvider
   */
  public function testDefinitions(string $plugin_id, array $expectedFields): void {
    $definitions = $this->assetMediaSourceManager->getDefinitions();

    // Assert the plugin definition exists.
    $this->assertArrayHasKey($plugin_id, $definitions);
    $md = $definitions[$plugin_id];

    // Assert all fields in the definition are expected.
    foreach ($md as $field => $value) {
      $this->assertTrue(in_array($field, $expectedFields));
    }

    // Assert all expected fields are present in the definition.
    foreach ($expectedFields as $field) {
      $this->assertTrue(isset($md[$field]));
    }
  }

  /**
   * Test that no unexpected plugins are present.
   */
  public function testDefinitionsContainOnlyExpectedPlugins(): void {
    $definitions = $this->assetMediaSourceManager->getDefinitions();
    $expectedPluginIds = array_map(
      fn($item) => $item[0],
      self::definitionsProvider()
    );
    $actualPluginIds = array_keys($definitions);

    // Assert that the actual plugin IDs match the expected plugin IDs, regardless of order.
    $this->assertEqualsCanonicalizing($expectedPluginIds, $actualPluginIds, 'Definitions contain only expected plugins.');
  }

  /**
   * Data provider for testDefinitions().
   *
   * Each entry contains a plugin ID and the expected fields in its definition.
   *
   * @return array[]
   *   Each item contains:
   *   - string: The bundled plugin in acquia_dam, including in the test module.
   *   - array: The expected fields in the plugin definition.
   */
  public static function definitionsProvider(): array {
    $annotationFields = [
      'provider',
      'class',
      'asset_class',
      'id',
      'label',
      'default_thumbnail_filename',
      'asset_search_key',
      'asset_search_value',
    ];

    return [
      // Only the image plugin has field_list_item_class and field_type_class fields.
      ['image', array_merge($annotationFields, ['field_list_item_class', 'field_type_class'])],
      ['archive', $annotationFields],
      ['audio', $annotationFields],
      ['documents', $annotationFields],
      ['generic', $annotationFields],
      ['pdf', $annotationFields],
      ['spinset', $annotationFields],
      ['video', $annotationFields],
      ['assettestmediasource', $annotationFields],
      ['metadataassettestmediasource', $annotationFields],
    ];
  }


}
