<?php

namespace Drupal\Tests\acquia_contenthub_publisher\Kernel\EventSubscriber\HandleWebhook;

use Acquia\ContentHubClient\ContentHubClient;
use Acquia\Hmac\Key;
use Drupal\acquia_contenthub\Event\HandleWebhookEvent;
use Drupal\acquia_contenthub_publisher\EventSubscriber\HandleWebhook\ReExport;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\Entity\Node;
use Symfony\Component\HttpFoundation\Request;

/**
 * Tests Re-export functionality from a Webhook.
 *
 * @group acquia_contenthub_publisher
 *
 * @coversDefaultClass \Drupal\acquia_contenthub_publisher\EventSubscriber\HandleWebhook\ReExport
 *
 * @package Drupal\Tests\acquia_contenthub_publisher\Kernel\EventSubscriber\HandleWebhook
 */
class ReExportTest extends EntityKernelTestBase {

  /**
   * Re-Export instance.
   *
   * @var \Drupal\acquia_contenthub_publisher\EventSubscriber\HandleWebhook\ReExport
   */
  protected $reExport;

  /**
   * Config object.
   *
   * @var \Drupal\Core\Config\Config
   */
  protected $configFactory;

  /**
   * The Publisher Actions Service.
   *
   * @var \Drupal\acquia_contenthub_publisher\PublisherActions
   */
  protected $publisherActions;

  /**
   * The stream wrapper manager.
   *
   * @var \Drupal\Core\Entity\EntityRepositoryInterface
   */
  protected $entityRepository;

  /**
   * The logger factory.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
   */
  protected $logger;

  /**
   * Content Hub Client Factory.
   *
   * @var \Drupal\acquia_contenthub\Client\ClientFactory
   */
  protected $clientFactory;

  /**
   * Database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $database;

  /**
   * The Content Hub Settings Object.
   *
   * @var \Acquia\ContentHubClient\Settings
   */
  protected $settings;

  /**
   * The Publisher Tracker.
   *
   * @var \Drupal\acquia_contenthub_publisher\PublisherTracker
   */
  protected $tracker;

  /**
   * Publisher Queue.
   *
   * @var \Drupal\Core\Queue\QueueInterface
   */
  protected $publisherQueue;
  /**
   * A test node.
   *
   * @var \Drupal\node\NodeInterface
   */
  protected $node;

  /**
   * The remote origin that sends the webhook.
   *
   * @var string
   */
  protected $remoteOrigin;

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

  /**
   * {@inheritDoc}
   */
  public function setUp(): void {
    parent::setUp();

    $this->installSchema('acquia_contenthub_publisher', ['acquia_contenthub_publisher_export_tracking']);
    $this->remoteOrigin = '98213529-0000-2222-0000-123456789123';

    // Saving Content Hub Settings.
    $this->configFactory = $this->container->get('config.factory');
    $this->createAcquiaContentHubAdminSettings();
    $this->clientFactory = $this->container->get('acquia_contenthub.client.factory');

    // The services for publisher tracker and publisher queue.
    $this->tracker = $this->container->get('acquia_contenthub_publisher.tracker');
    $this->publisherQueue = $this->container->get('queue')->get('acquia_contenthub_publish_export');

    // Create a test node.
    $node_uuid = '98213529-0000-0001-0000-123456789123';
    $this->node = Node::create([
      'type' => 'article',
      'title' => 'Test EN',
      'uuid' => $node_uuid,
    ]);
    $this->node->save();

    // Saving the node would add it to the Publisher Queue.
    // Delete item from the queue and tracking table to start over.
    $this->deleteQueueItem($node_uuid);

    // Creating the ReExport EventSubscriber.
    $this->publisherActions = $this->container->get('acquia_contenthub_publisher.actions');
    $this->entityRepository = $this->container->get('entity.repository');
    $this->logger = $this->container->get('logger.factory');
    $this->database = $this->container->get('database');

    // This should re-add the queue item in the export queue.
    $this->reExport = new ReExport($this->publisherActions, $this->entityRepository, $this->logger);
  }

  /**
   * Get Acquia Content Hub settings.
   */
  public function createAcquiaContentHubAdminSettings(): void {
    /** @var \Drupal\acquia_contenthub\Settings\ContentHubConfigurationInterface $ch_configurations */
    $ch_configurations = $this->container->get('acquia_contenthub.configuration');
    $connection_details = $ch_configurations->getConnectionDetails();

    $connection_details->setClientName('test-client');
    $connection_details->setClientUuid('00000000-0000-0001-0000-123456789123');
    $connection_details->setApiKey('HqkhciruZhJxg6b844wc');
    $connection_details->setSecretKey('u8Pk4dTaeBWpRxA9pBvPJfru8BFSenKZi79CBKkk');
    $connection_details->setHostname('https://dev-use1.content-hub-dev.acquia.com');
    $connection_details->setSharedSecret('12312321312321');
    $ch_configurations->getContentHubConfig()->disableContentHubUpdates();
    $this->settings = $ch_configurations->getSettings();
  }

  /**
   * Tests entity updated status.
   *
   * @param mixed $args
   *   Data.
   *
   * @dataProvider dataProvider
   */
  public function testReExport(...$args) {
    $client = $this->prophesize(ContentHubClient::class);
    $client->getSettings()->willReturn($this->settings);
    $key = new Key('id', 'secret');
    $request = $this->createSignedRequest();
    $entities[] = [
      'uuid' => $args[1],
      'type' => $args[0],
      'dependencies' => [$args[1]],
    ];

    $payload = [
      'crud' => 'republish',
      'status' => 'successful',
      'initiator' => $this->remoteOrigin,
      'entities' => $entities,
    ];

    // Verify we are starting clean.
    $entity_status = $this->getStatusByUuid($args[1]);
    $this->assertEquals($entity_status, $args[2]);

    // Handle Webhook Request Event to Re-export entity.
    $event = new HandleWebhookEvent($request, $payload, $key, $client->reveal());
    $this->reExport->onHandleWebhook($event);

    // Verify item has been added to the publisher queue.
    $entity_status = $this->getStatusByUuid($args[1]);
    $this->assertEquals($entity_status, $args[3]);

    // Verify response code.
    $response = $event->getResponse();
    $code = $response->getStatusCode();
    $this->assertEquals($args[4], $code);

    // Verify response message.
    $message = $response->getBody()->getContents();
    $this->assertEquals($args[5], $message);
  }

  /**
   * Data provider for testUpdatePublished.
   */
  public static function dataProvider(): array {
    return [
      [
        'test_entity',
        '11111111-1111-1111-0000-111111111111',
        '',
        '',
        200,
        'The entities could not be re-exported. Requesting client: 98213529-0000-2222-0000-123456789123. Entities: test_entity/11111111-1111-1111-0000-111111111111.',
      ],
      [
        'node',
        '98213529-0000-0001-0000-123456789123',
        '',
        'queued',
        200,
        'Entities have been successfully enqueued by origin = 98213529-0000-2222-0000-123456789123. Entities: node/98213529-0000-0001-0000-123456789123.' . PHP_EOL,
      ],
    ];
  }

  /**
   * Creates an test HMAC-Signed Request.
   *
   * @return \Symfony\Component\HttpFoundation\Request
   *   The HMAC signed request.
   */
  public function createSignedRequest(): Request {
    $request_global = Request::createFromGlobals();
    $request = $request_global->duplicate(NULL, NULL, NULL, NULL, NULL, [
      'REQUEST_URI' => 'http://example.com/acquia-contenthub/webhook',
      'SERVER_NAME' => 'example.com',
    ]);
    // @codingStandardsIgnoreStart
    $header = 'acquia-http-hmac headers="X-Custom-Signer1;X-Custom-Signer2",id="e7fe97fa-a0c8-4a42-ab8e-2c26d52df059",nonce="a9938d07-d9f0-480c-b007-f1e956bcd027",realm="CIStore",signature="0duvqeMauat7pTULg3EgcSmBjrorrcRkGKxRDtZEa1c=",version="2.0"';
    // @codingStandardsIgnoreEnd
    $request->headers->set('Authorization', $header);
    return $request;
  }

  /**
   * Fetch entity status.
   *
   * @param string $uuid
   *   Entity uuid.
   *
   * @return string
   *   Export status.
   */
  protected function getStatusByUuid(string $uuid): string {
    return $this->database->select('acquia_contenthub_publisher_export_tracking', 'acpet')
      ->fields('acpet', ['status'])
      ->condition('acpet.entity_uuid', $uuid)
      ->execute()
      ->fetchField();
  }

  /**
   * Deletes Item from Queue and tracking table.
   *
   * @param string $uuid
   *   The Entity UUID.
   *
   * @throws \Exception
   */
  protected function deleteQueueItem(string $uuid) {
    $item_id = $this->tracker->getQueueId($uuid);
    if ($item_id) {
      $item = $this->publisherQueue->claimItem();
      if ($item_id === $item->item_id) {
        $this->publisherQueue->deleteItem($item);
      }
    }
    $this->tracker->delete('entity_uuid', $uuid);
  }

}
