<?php

namespace Drupal\Tests\acquia_contenthub_subscriber\Kernel;

use Acquia\ContentHubClient\ContentHubClient;
use Acquia\ContentHubClient\Settings;
use Drupal\acquia_contenthub\Client\ClientFactory;
use Drupal\acquia_contenthub_subscriber\SubscriberTracker;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\Tests\acquia_contenthub\Kernel\Traits\AcquiaContentHubAdminSettingsTrait;
use Drupal\Tests\acquia_contenthub\Traits\QueueTestTrait;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
use Drupal\Tests\node\Traits\NodeCreationTrait;
use Drupal\Tests\taxonomy\Traits\TaxonomyTestTrait;
use GuzzleHttp\Psr7\Response;
use Prophecy\Argument;

/**
 * Tests for SubscriberRequeuer class.
 *
 * @group acquia_contenthub_subscriber
 *
 * @requires module depcalc
 *
 * @package Drupal\Tests\acquia_contenthub_subscriber\Kernel
 */
class SubscriberRequeuerTest extends EntityKernelTestBase {

  use AcquiaContentHubAdminSettingsTrait;
  use ContentTypeCreationTrait;
  use NodeCreationTrait;
  use TaxonomyTestTrait;
  use QueueTestTrait;

  /**
   * UUIDs for testing.
   */
  public const UUID1 = 'f065dc8c-09f6-4510-ac77-be6b548e3a70';
  public const UUID2 = 'f065dc8c-09f6-4510-ac77-be6b548e3a71';

  /**
   * The subscriber requeuer.
   *
   * @var \Drupal\acquia_contenthub_subscriber\SubscriberRequeuer
   */
  protected $subscriberRequeuer;

  /**
   * Subscriber tracker.
   *
   * @var \Drupal\acquia_contenthub_subscriber\SubscriberTracker
   */
  protected $tracker;

  /**
   * The subscriber import queue.
   *
   * @var \Drupal\Core\Queue\QueueInterface
   */
  protected $queue;

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

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

    $this->createAcquiaContentHubAdminSettings();
    $this->installSchema('acquia_contenthub_subscriber', [SubscriberTracker::IMPORT_TRACKING_TABLE]);
    $this->installEntitySchema('node');
    $this->installSchema('node', ['node_access']);
    $this->installEntitySchema('taxonomy_term');
    $this->installConfig([
      'filter',
      'node',
    ]);
    // ES query and response to get entities of type node.
    $query1 = [
      'query' => [
        'bool' => [
          'must' => [
            [
              'match' => [
                'data.attributes.entity_type.value.und' => 'node',
              ],
            ],
          ],
        ],
      ],
    ];
    $query1_response = [
      'hits' => [
        'hits' => [
          [
            '_source' => [
              'data' => [
                'uuid' => self::UUID1,
                'type' => 'drupal8_content_entity',
              ],
            ],
          ],
          [
            '_source' => [
              'data' => [
                'uuid' => self::UUID2,
                'type' => 'drupal8_content_entity',
              ],
            ],
          ],
        ],
      ],
    ];

    // ES query & response get entities of type node, article bundle and UUID1.
    $query2 = [
      'query' => [
        'bool' => [
          'must' => [
            [
              'match' => [
                'data.attributes.entity_type.value.und' => 'node',
              ],
            ],
            [
              'match' => [
                'data.uuid' => self::UUID1,
              ],
            ],
            [
              'match' => [
                'data.attributes.bundle.value.und' => 'article',
              ],
            ],
          ],
        ],
      ],
    ];
    $query2_response = [
      'hits' => [
        'hits' => [
          [
            '_source' => [
              'data' => [
                'uuid' => self::UUID1,
                'type' => 'drupal8_content_entity',
              ],
            ],
          ],
        ],
      ],
    ];

    // ES query & response get entities of type node and article bundle.
    $query3 = [
      'query' => [
        'bool' => [
          'must' => [
            [
              'match' => [
                'data.attributes.entity_type.value.und' => 'node',
              ],
            ],
            [
              'match' => [
                'data.attributes.bundle.value.und' => 'page',
              ],
            ],
          ],
        ],
      ],
    ];
    $query3_response = [
      'hits' => [
        'hits' => [
          [
            '_source' => [
              'data' => [
                'uuid' => self::UUID2,
                'type' => 'drupal8_content_entity',
              ],
            ],
          ],
        ],
      ],
    ];

    $queue_factory = $this->container->get('queue');
    $this->queue = $queue_factory->get('acquia_contenthub_subscriber_import');

    $settings = $this->prophesize(Settings::class);
    $settings->getApiKey()->willReturn('test-api-key');
    $settings->getSecretKey()->willReturn('test-secret-key');
    $settings->getUuid()->willReturn('test-uuid');
    $settings->getWebhook(Argument::any())->willReturn('w-uuid');

    $ch_client = $this->prophesize(ContentHubClient::class);
    $ch_client->getSettings()->willReturn($settings->reveal());
    $ch_client->getInterestList(Argument::any(), 'subscriber', Argument::any())->willReturn([]);
    $ch_client->addEntitiesToInterestListBySiteRole(Argument::any(), Argument::any(), Argument::any())->willReturn(new Response());
    $ch_client->getRemoteSettings()->willReturn([]);
    $ch_client->startScroll(
      Argument::any(),
      Argument::any(),
      $query1
    )->willReturn($query1_response);
    $ch_client->startScroll(
      Argument::any(),
      Argument::any(),
      $query2
    )->willReturn($query2_response);
    $ch_client->startScroll(
      Argument::any(),
      Argument::any(),
      $query3
    )->willReturn($query3_response);

    $client_factory = $this->prophesize(ClientFactory::class);
    $client_factory
      ->getClient()
      ->willReturn($ch_client->reveal());
    $this->container->set('acquia_contenthub.client.factory', $client_factory->reveal());

    $this->createContentType([
      'type' => 'article',
    ]);
    $this->createContentType([
      'type' => 'page',
    ]);

    $this->tracker = $this->container->get('acquia_contenthub_subscriber.tracker');
    $this->subscriberRequeuer = $this->container->get('acquia_contenthub_subscriber.requeuer');
    $this->ensureQueueTableExists();
  }

  /**
   * Tests exception in the re-queueing of entities by queued status.
   */
  public function testOnlyQueuedEntitiesOptionException(): void {
    $this->expectExceptionMessage('Cannot pass entity type or entity bundle name while passing only_queued_entities flag.');
    $this->subscriberRequeuer->reQueue('node', '', '', TRUE, FALSE);
  }

  /**
   * Tests the re-queueing of entities by queued status in the tracking table.
   */
  public function testOnlyQueuedEntitiesOption(): void {
    $uuid = 'cf3429f8-a20f-4fc4-b2f4-5494b950bd75';
    $this->tracker->queue($uuid);

    $this->subscriberRequeuer->reQueue('', '', '', TRUE, FALSE);
    $data = $this->queue->claimItem();
    $this->assertSame($uuid, $data->data->uuids);
  }

  /**
   * Tests the re-queueing of entities that are in subscriber tracking table.
   */
  public function testUseTrackingTableOption(): void {
    $node1 = $this->createNode([
      'type' => 'article',
    ]);
    $this->tracker->track($node1, 'random-hash');

    $node2 = $this->createNode([
      'type' => 'page',
    ]);
    $this->tracker->track($node2, 'random-hash');

    $vocabulary = $this->createVocabulary();
    $term = $this->createTerm($vocabulary);
    $this->tracker->track($term, 'random-hash');

    // Requeue tracked nodes of bundle article.
    $this->subscriberRequeuer->reQueue('node', 'article', '', FALSE, TRUE);
    $data = $this->queue->claimItem();
    $this->assertSame($node1->uuid(), $data->data->uuids);

    // Requeue all the tracked nodes.
    $this->queue->deleteQueue();
    $this->subscriberRequeuer->reQueue('node', '', '', FALSE, TRUE);
    $data = $this->queue->claimItem();
    $this->assertStringContainsString($node1->uuid(), $data->data->uuids);
    $this->assertStringContainsString($node2->uuid(), $data->data->uuids);
    $this->assertStringNotContainsString($term->uuid(), $data->data->uuids);

    // Requeue all tracked entities.
    $this->queue->deleteQueue();
    $this->subscriberRequeuer->reQueue('', '', '', FALSE, TRUE);
    $data = $this->queue->claimItem();
    $this->assertStringContainsString($node1->uuid(), $data->data->uuids);
    $this->assertStringContainsString($node2->uuid(), $data->data->uuids);
    $this->assertStringContainsString($term->uuid(), $data->data->uuids);
  }

  /**
   * Tests re-queueing of entities by specific entity type, bundle and uuid.
   */
  public function testEntityTypeOption(): void {
    // Re-enqueue entities of type node.
    $this->subscriberRequeuer->reQueue('node', '', '', FALSE, FALSE);
    $data = $this->queue->claimItem();
    $this->assertSame(self::UUID1 . ', ' . self::UUID2, $data->data->uuids);

    // Re-enqueue entities of type node, bundle article and UUID1.
    $this->queue->deleteQueue();
    $this->subscriberRequeuer->reQueue('node', 'article', self::UUID1, FALSE, FALSE);
    $data = $this->queue->claimItem();
    $this->assertSame(self::UUID1, $data->data->uuids);

    // Re-enqueue entities of type node and bundle page.
    $this->queue->deleteQueue();
    $this->subscriberRequeuer->reQueue('node', 'page', '', FALSE, FALSE);
    $data = $this->queue->claimItem();
    $this->assertSame(self::UUID2, $data->data->uuids);
  }

  /**
   * Tests validate option exception.
   */
  public function testValidateOptionException(): void {
    $this->expectExceptionMessage('Please specify at least one option.');
    $this->subscriberRequeuer->reQueue('', '', '', FALSE, FALSE);
  }

}
