<?php

namespace Drupal\Tests\acquia_contenthub_translations\Kernel;

use Acquia\ContentHubClient\CDF\CDFObject;
use Acquia\ContentHubClient\CDFAttribute;
use Acquia\ContentHubClient\CDFDocument;
use Acquia\ContentHubClient\ContentHubClient;
use Drupal\acquia_contenthub\Client\ClientFactory;
use Drupal\acquia_contenthub_translations\TranslatableEntityFilter;
use Drupal\acquia_contenthub_translations\UndesiredLanguageRegistry\UndesiredLanguageRegistryInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\Tests\acquia_contenthub\Unit\Helpers\LoggerMock;
use Psr\Log\LoggerInterface;

/**
 * Tests TranslatableEntityFilter service.
 *
 * @group acquia_contenthub_translations
 *
 * @requires module depcalc
 *
 * @package Drupal\Tests\acquia_contenthub_translations\Kernel
 *
 * @coversDefaultClass \Drupal\acquia_contenthub_translations\TranslatableEntityFilter
 */
class TranslatableEntityFilterTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'acquia_contenthub',
    'acquia_contenthub_translations',
    'acquia_contenthub_subscriber',
    'language',
    'depcalc',
    'system',
    'user',
  ];

  /**
   * The TranslatableEntityFilter service under test.
   *
   * @var \Drupal\acquia_contenthub_translations\TranslatableEntityFilter
   */
  protected TranslatableEntityFilter $filter;

  /**
   * Mock Content Hub client.
   *
   * @var \Acquia\ContentHubClient\ContentHubClient|\Prophecy\Prophecy\ObjectProphecy
   */
  protected $mockClient;

  /**
   * Mock client factory.
   *
   * @var \Drupal\acquia_contenthub\Client\ClientFactory|\Prophecy\Prophecy\ObjectProphecy
   */
  protected $mockClientFactory;

  /**
   * Mock undesired language registrar.
   *
   * @var \Drupal\acquia_contenthub_translations\UndesiredLanguageRegistry\UndesiredLanguageRegistryInterface|\Prophecy\Prophecy\ObjectProphecy
   */
  protected $mockRegistrar;

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

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

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->installConfig(['language']);
    $this->installEntitySchema('configurable_language');

    ConfigurableLanguage::createFromLangcode('es')->save();
    ConfigurableLanguage::createFromLangcode('fr')->save();
    ConfigurableLanguage::createFromLangcode('de')->save();

    $this->languageManager = $this->container->get('language_manager');

    $this->mockClient = $this->prophesize(ContentHubClient::class);
    $this->mockClientFactory = $this->prophesize(ClientFactory::class);
    $this->mockRegistrar = $this->prophesize(UndesiredLanguageRegistryInterface::class);
    $this->mockLogger = new LoggerMock();
    $this->mockClientFactory->getClient()->willReturn($this->mockClient->reveal());
    $this->mockRegistrar->getUndesiredLanguages()->willReturn([]);

    $this->filter = new TranslatableEntityFilter(
      $this->languageManager,
      $this->mockRegistrar->reveal(),
      $this->mockLogger,
      $this->mockClientFactory->reveal()
    );
  }

  /**
   * Tests filtering entities with foreign languages.
   *
   * @covers ::filterEntityUuidsBySubscriberLanguages
   */
  public function testFilterEntityUuidsBySubscriberLanguagesWithForeignLanguages(): void {
    $untracked_uuids = ['uuid1', 'uuid2', 'uuid3'];
    $all_uuids = ['uuid1', 'uuid2', 'uuid3', 'uuid4'];

    $cdf1 = $this->createMockCdfEntity('uuid1', 'node', ['ja']);
    $cdf2 = $this->createMockCdfEntity('uuid2', 'node', ['en']);
    $cdf3 = $this->createMockCdfEntity('uuid3', 'node', ['zh']);

    $mock_cdf_document = $this->prophesize(CDFDocument::class);
    $mock_cdf_document->getEntities()->willReturn([$cdf1, $cdf2, $cdf3]);

    $this->mockClient->getEntities($untracked_uuids)->willReturn($mock_cdf_document->reveal());

    $result = $this->filter->filterEntityUuidsBySubscriberLanguages($untracked_uuids, $all_uuids);

    $expected = ['uuid2', 'uuid4'];
    $this->assertEquals($expected, array_values($result));
    $info_messages = $this->mockLogger->getInfoMessages();
    $this->assertStringContainsString('were not added to import queue as these are in foreign languages', $info_messages[0]);
  }

  /**
   * Tests filtering entities with enabled languages only.
   *
   * @covers ::filterEntityUuidsBySubscriberLanguages
   */
  public function testFilterEntityUuidsBySubscriberLanguagesWithEnabledLanguagesOnly(): void {
    $untracked_uuids = ['uuid1', 'uuid2'];
    $all_uuids = ['uuid1', 'uuid2', 'uuid3'];

    $cdf1 = $this->createMockCdfEntity('uuid1', 'node', ['en']);
    $cdf2 = $this->createMockCdfEntity('uuid2', 'node', ['es', 'fr']);

    $mock_cdf_document = $this->prophesize(CDFDocument::class);
    $mock_cdf_document->getEntities()->willReturn([$cdf1, $cdf2]);

    $this->mockClient->getEntities($untracked_uuids)->willReturn($mock_cdf_document->reveal());

    $result = $this->filter->filterEntityUuidsBySubscriberLanguages($untracked_uuids, $all_uuids);
    $this->assertEquals($all_uuids, array_values($result));
  }

  /**
   * Tests filtering with undesired languages configured.
   *
   * @covers ::filterEntityUuidsBySubscriberLanguages
   */
  public function testFilterEntityUuidsBySubscriberLanguagesWithUndesiredLanguages(): void {
    $this->mockRegistrar->getUndesiredLanguages()->willReturn(['de']);

    $untracked_uuids = ['uuid1', 'uuid2', 'uuid3'];
    $all_uuids = ['uuid1', 'uuid2', 'uuid3'];
    $cdf1 = $this->createMockCdfEntity('uuid1', 'node', ['en']);
    // Should be filtered.
    $cdf2 = $this->createMockCdfEntity('uuid2', 'node', ['de']);
    $cdf3 = $this->createMockCdfEntity('uuid3', 'node', ['es']);

    $mock_cdf_document = $this->prophesize(CDFDocument::class);
    $mock_cdf_document->getEntities()->willReturn([$cdf1, $cdf2, $cdf3]);
    $this->mockClient->getEntities($untracked_uuids)->willReturn($mock_cdf_document->reveal());

    $result = $this->filter->filterEntityUuidsBySubscriberLanguages($untracked_uuids, $all_uuids);
    // Should exclude uuid2 (German - undesired).
    $expected = ['uuid1', 'uuid3'];
    $this->assertEquals($expected, array_values($result));
  }

  /**
   * Creates a mock CDF entity with specified languages.
   *
   * @param string $uuid
   *   The entity UUID.
   * @param string $entity_type
   *   The entity type.
   * @param array $languages
   *   The languages for this entity.
   *
   * @return \Acquia\ContentHubClient\CDF\CDFObject|\Prophecy\Prophecy\ObjectProphecy
   *   Mock CDF object.
   */
  protected function createMockCdfEntity(string $uuid, string $entity_type, array $languages) {
    $cdf = $this->prophesize(CDFObject::class);
    $cdf->getUuid()->willReturn($uuid);
    $cdf->getMetadata()->willReturn(['languages' => $languages]);

    $entity_type_attribute = $this->prophesize(CDFAttribute::class);
    $entity_type_attribute->getValue()->willReturn([LanguageInterface::LANGCODE_NOT_SPECIFIED => $entity_type]);
    $cdf->getAttribute('entity_type')->willReturn($entity_type_attribute->reveal());

    return $cdf->reveal();
  }

}
