<?php

namespace Drupal\Tests\acquia_contenthub\Kernel;

use Acquia\ContentHubClient\CDFDocument;
use Acquia\ContentHubClient\ContentHubClient;
use Acquia\ContentHubClient\ContentHubLoggingClient;
use Acquia\ContentHubClient\Settings;
use Acquia\ContentHubClient\Syndication\SyndicationStatus;
use Drupal\acquia_contenthub\Client\ClientFactory;
use Drupal\acquia_contenthub_subscriber\CdfImporter;
use Drupal\acquia_contenthub_subscriber\Plugin\QueueWorker\ContentHubImportQueueWorker;
use Drupal\acquia_contenthub_subscriber\SubscriberTracker;
use Drupal\Core\Entity\Entity\EntityViewMode;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\acquia_contenthub\Kernel\Stubs\DrupalVersion;
use Drupal\Tests\acquia_contenthub\Kernel\Traits\CdfDocumentCreatorTrait;
use Drupal\Tests\acquia_contenthub\Kernel\Traits\MetricsUpdateTrait;
use GuzzleHttp\Psr7\Response;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;

/**
 * Tests that entities are properly unserialized.
 *
 * @group acquia_contenthub
 *
 * @package Drupal\Tests\acquia_contenthub\Kernel
 */
class UnserializationTest extends EntityKernelTestBase {

  use DrupalVersion;
  use MetricsUpdateTrait;
  use CdfDocumentCreatorTrait;
  use ProphecyTrait;

  /**
   * Sample View Mode UUID.
   */
  const CLIENT_UUID_1 = 'fefd7eda-4244-4fe4-b9b5-b15b89c61aa8';

  /**
   * Sample Taxonomy Term UUID.
   */
  const CLIENT_UUID_2 = 'de9606dc-56fa-4b09-bcb1-988533edc814';

  /**
   * Sample Vocabulary UUID.
   */
  const CLIENT_UUID_3 = '22dc8835-7b14-4b08-b25d-eae99e1d4d91';

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'user',
    'file',
    'node',
    'field',
    'taxonomy',
    'depcalc',
    'acquia_contenthub',
    'acquia_contenthub_subscriber',
  ];

  /**
   * Queue worker instance.
   *
   * @var \Drupal\acquia_contenthub_subscriber\Plugin\QueueWorker\ContentHubImportQueueWorker
   */
  protected $contentHubImportQueueWorker;

  /**
   * Client instance.
   *
   * @var \Acquia\ContentHubClient\ContentHubClient|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $contentHubClient;

  /**
   * Logging Client instance.
   *
   * @var \Acquia\ContentHubClient\ContentHubLoggingClient|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $contentHubLoggingClient;

  /**
   * Client settings.
   *
   * @var \Acquia\ContentHubClient\Settings
   */
  protected $settings;

  /**
   * Subscriber tracker mock.
   *
   * @var \Drupal\acquia_contenthub_subscriber\SubscriberTracker|\Prophecy\Prophecy\ObjectProphecy
   */
  protected $subscriberTrackerMock;

  /**
   * {@inheritdoc}
   *
   * @throws \Exception
   */
  protected function setup(): void {
    parent::setUp();

    $this->installEntitySchema('taxonomy_term');
    $this->installSchema('acquia_contenthub_subscriber', ['acquia_contenthub_subscriber_import_tracking']);

    $this->contentHubClient = $this->prophesize(ContentHubClient::class);
    $this->settings = $this->prophesize(Settings::class);
    $this->settings->getWebhook('uuid')->willReturn('foo');
    $this->settings->getName()->willReturn('foo');
    $this->settings->getUuid()->willReturn(self::CLIENT_UUID_1);
    $this->settings->toArray()->willReturn(['name' => 'foo']);

    $this->contentHubLoggingClient = $this->prophesize(ContentHubLoggingClient::class);
    $this->contentHubLoggingClient->sendLogs(Argument::any())->willReturn([]);
    $client_factory_mock = $this->prophesize(ClientFactory::class);
    $client_factory_mock->getLoggingClient()->willReturn($this->contentHubLoggingClient);
    $this->contentHubClient->getSettings()->willReturn($this->settings->reveal());
    $this->contentHubClient->getResponse()->willReturn(new Response(200));
    $this->contentHubClient->getInterestList(Argument::any(), Argument::any(), Argument::any([]))->willReturn([]);
    $this->mockMetricsCalls($this->contentHubClient);
    $client_factory_mock->getClient()->willReturn($this->contentHubClient);
    $this->container->set('acquia_contenthub.client.factory', $client_factory_mock->reveal());
    $this->subscriberTrackerMock = $this->prophesize(SubscriberTracker::class);
    $this->subscriberTrackerMock->getStatusByUuid(Argument::any())->willReturn(SubscriberTracker::AUTO_UPDATE_DISABLED);
    $this->subscriberTrackerMock->getEntityByRemoteIdAndHash(Argument::any())->willReturn([]);
    $this->subscriberTrackerMock->getEntitiesByRemoteIdAndHash(Argument::any())->willReturn([]);
    $this->subscriberTrackerMock->isTracked(Argument::any())->willReturn(FALSE);
    $this->subscriberTrackerMock->setStatusByUuids(Argument::any(), Argument::any());
    $this->subscriberTrackerMock->track(Argument::any(), Argument::any(), Argument::any())->willReturn();
    $this->container->set('acquia_contenthub_subscriber.tracker', $this->subscriberTrackerMock->reveal());
    $common = $this->getMockBuilder(CdfImporter::class)
      ->setConstructorArgs([
        $this->container->get('event_dispatcher'),
        $this->container->get('entity.cdf.serializer'),
        $this->container->get('acquia_contenthub.client.factory'),
        $this->container->get('acquia_contenthub_subscriber.logger_channel'),
        $this->container->get('acquia_contenthub_subscriber.tracker'),
      ])
      ->onlyMethods(['getUpdateDbStatus'])
      ->getMock();
    $this->container->set('acquia_contenthub_subscriber.cdf_importer', $common);

    $this->contentHubImportQueueWorker = $this->getMockBuilder(ContentHubImportQueueWorker::class)
      ->setConstructorArgs([
        $this->container->get('event_dispatcher'),
        $this->container->get('acquia_contenthub_subscriber.cdf_importer'),
        $this->container->get('acquia_contenthub.client.factory'),
        $this->container->get('acquia_contenthub_subscriber.tracker'),
        $this->container->get('acquia_contenthub.configuration'),
        $this->container->get('acquia_contenthub_subscriber.ch_logger'),
        $this->container->get('acquia_contenthub.cdf_metrics_manager'),
        [],
        'acquia_contenthub_subscriber_import',
        NULL,
        $this->container->get('acquia_contenthub.interest_list_storage'),
        $this->container->get('acquia_contenthub_subscriber.entity_import'),
      ])
      ->addMethods([])
      ->getMock();
  }

  /**
   * Tests configuration entity unserialization.
   *
   * @throws \Drupal\Core\Entity\EntityStorageException
   * @throws \ReflectionException
   */
  public function testConfigEntityUnserialization() {
    $cdf_document = $this->createCDFDocumentFromFixtureFile('view_modes.json');

    $this->contentHubClient->getEntities([self::CLIENT_UUID_1 => self::CLIENT_UUID_1])->willReturn($cdf_document);
    $get_il = [
      self::CLIENT_UUID_1 => [
        'status' => SyndicationStatus::IMPORT_SUCCESSFUL,
        'reason' => 'manual',
        'event_ref' => 'uuid',
      ],
    ];
    $this->contentHubClient->getInterestList(Argument::type('string'), Argument::type('string'), Argument::any([]))->willReturn($get_il);
    $this->contentHubClient->updateInterestListBySiteRole(Argument::type('string'), Argument::type('string'), Argument::type('array'))->willReturn(new Response());

    $this->initializeContentHubClientExpectation($cdf_document);
    $interest_list = [
      'uuids' => [self::CLIENT_UUID_1],
      'status' => SyndicationStatus::IMPORT_SUCCESSFUL,
      'reason' => 'manual',
    ];
    $this->contentHubClient->addEntitiesToInterestListBySiteRole("foo", 'subscriber', $interest_list)->willReturn(new Response());
    $query = [
      'uuids' => self::CLIENT_UUID_1,
      'disable_syndication' => TRUE,
    ];
    $this->contentHubClient->getInterestList("foo", 'subscriber', $query)->willReturn([]);

    $item = new \stdClass();
    $item->uuids = implode(', ', [self::CLIENT_UUID_1]);
    $this->contentHubImportQueueWorker->processItem($item);
    $view_mode = EntityViewMode::load('node.teaser');

    $this->assertNotEmpty($view_mode->id());
  }

  /**
   * Tests content entity unserialization.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   * @throws \ReflectionException
   *
   * @see _acquia_contenthub_publisher_enqueue_entity()
   */
  public function testTaxonomyTermUnserialization() {
    $cdf_document = $this->createCdfDocumentFromFixtureFile('taxonomy.json');

    $this->contentHubClient->getEntities([self::CLIENT_UUID_2 => self::CLIENT_UUID_2])->willReturn($cdf_document);
    $get_il = [
      self::CLIENT_UUID_2 => [
        'status' => SyndicationStatus::IMPORT_SUCCESSFUL,
        'reason' => 'manual',
        'event_ref' => 'uuid',
      ],
    ];
    $this->contentHubClient->getInterestList(Argument::type('string'), Argument::type('string'), Argument::any([]))->willReturn($get_il);
    $this->contentHubClient->updateInterestListBySiteRole(Argument::type('string'), Argument::type('string'), Argument::type('array'))->willReturn(new Response());

    $this->initializeContentHubClientExpectation($cdf_document);
    $interest_list = [
      'uuids' => [
        self::CLIENT_UUID_3,
        self::CLIENT_UUID_2,
      ],
      'status' => SyndicationStatus::IMPORT_SUCCESSFUL,
      'reason' => 'manual',
    ];
    $this->contentHubClient->addEntitiesToInterestListBySiteRole("foo", 'subscriber', $interest_list)->willReturn(new Response());
    $query = [
      'uuids' => self::CLIENT_UUID_3 . ',' . self::CLIENT_UUID_2,
      'disable_syndication' => TRUE,
    ];
    $this->contentHubClient->getInterestList("foo", 'subscriber', $query)->willReturn([]);
    $item = new \stdClass();
    $item->uuids = implode(', ', [self::CLIENT_UUID_2]);
    $this->contentHubImportQueueWorker->processItem($item);

    // Checks that vocabulary has been imported.
    $vocabulary = Vocabulary::load('tags');
    $this->assertNotEmpty($vocabulary->id());
    $this->assertEquals('Tags', $vocabulary->label());

    // Checks that taxonomy has been imported.
    /** @var \Drupal\taxonomy\Entity\Term[] $taxonomy_terms */
    $taxonomy_terms = \Drupal::entityTypeManager()
      ->getStorage('taxonomy_term')
      ->loadByProperties(['name' => 'tag1']);
    $this->assertNotEmpty($taxonomy_terms);

    $taxonomy_term = current($taxonomy_terms);
    $this->assertNotEmpty($taxonomy_term->id());
  }

  /**
   * Client expectation.
   *
   * @param \Acquia\ContentHubClient\CDFDocument $cdf_document
   *   CDF Document for client.
   *
   * @throws \Exception
   */
  private function initializeContentHubClientExpectation(CDFDocument $cdf_document): void {
    $this->contentHubClient->getSettings()->willReturn($this->settings);
    $cdf_entity = current($cdf_document->getEntities());
    $this->contentHubClient->getEntity(self::CLIENT_UUID_1)->willReturn($cdf_entity);
    $this->contentHubClient->putEntities($cdf_entity)->willReturn(NULL);
  }

}
