<?php

namespace Drupal\Tests\acquia_contenthub_subscriber\Kernel\Libs\InterestList;

use Acquia\ContentHubClient\ContentHubClient;
use Acquia\ContentHubClient\Settings;
use Drupal\acquia_contenthub\Client\ClientFactory;
use Drupal\acquia_contenthub\Exception\ContentHubClientException;
use Drupal\acquia_contenthub\Exception\ContentHubException;
use Drupal\acquia_contenthub_subscriber\SubscriberTracker;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\NodeInterface;
use Drupal\node\NodeTypeInterface;
use Drupal\Tests\acquia_contenthub\Unit\Helpers\LoggerMock;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
use Drupal\Tests\node\Traits\NodeCreationTrait;
use GuzzleHttp\Psr7\Response;
use Psr\Log\LoggerInterface;

/**
 * Tests interest list handler.
 *
 * @coversDefaultClass \Drupal\acquia_contenthub_subscriber\Libs\InterestList\InterestListHandler
 *
 * @package acquia_contenthub_subscriber
 *
 * @requires module depcalc
 */
class InterestListHandlerTest extends EntityKernelTestBase {

  use NodeCreationTrait;
  use ContentTypeCreationTrait;

  /**
   * Webhook uuid.
   */
  protected const WEBHOOK_UUID = 'webhook-uuid';

  /**
   * Article content type.
   *
   * @var \Drupal\node\NodeTypeInterface
   */
  protected NodeTypeInterface $articleContentType;

  /**
   * Article node.
   *
   * @var \Drupal\node\NodeInterface
   */
  protected NodeInterface $articleNode;

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

  /**
   * Client factory mock.
   *
   * @var \Drupal\acquia_contenthub\Client\ClientFactory|\Prophecy\Prophecy\ObjectProphecy
   */
  protected $clientFactory;

  /**
   * Logger mock.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected LoggerInterface $loggerMock;

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

  /**
   * {@inheritDoc}
   */
  public function setUp(): void {
    parent::setUp();
    $this->installConfig('node');
    $this->installEntitySchema('node');
    $this->installConfig([
      'field',
      'filter',
      'node',
    ]);
    $this->installSchema('acquia_contenthub_subscriber', SubscriberTracker::IMPORT_TRACKING_TABLE);
    $this->articleContentType = $this->createContentType(['type' => 'article']);
    $this->articleNode = $this->createNode(['type' => 'article']);
    $subscriber_tracker = $this->container->get('acquia_contenthub_subscriber.tracker');
    $subscriber_tracker->track($this->articleContentType, 'random-hash');
    $subscriber_tracker->setStatusByUuid($this->articleContentType->uuid(), SubscriberTracker::AUTO_UPDATE_DISABLED);
    $subscriber_tracker->track($this->articleNode, 'random-hash');
    $subscriber_tracker->setStatusByUuid($this->articleNode->uuid(), SubscriberTracker::AUTO_UPDATE_DISABLED);
    $this->clientFactory = $this->prophesize(ClientFactory::class);
    $this->client = $this->prophesize(ContentHubClient::class);

    $this->container->set('acquia_contenthub.client.factory', $this->clientFactory->reveal());
    $this->loggerMock = new LoggerMock();
    $this->container->set('acquia_contenthub_subscriber.logger_channel', $this->loggerMock);
  }

  /**
   * Mocks Content Hub client.
   *
   * @throws \Exception
   */
  protected function mockClient(bool $mock_settings = TRUE): void {
    if ($mock_settings) {
      $settings = $this->prophesize(Settings::class);
      $settings
        ->getWebhook('uuid')
        ->willReturn(self::WEBHOOK_UUID);
      $this->client
        ->getSettings()
        ->willReturn($settings->reveal());
    }
    $this->clientFactory
      ->getClient()
      ->willReturn($this->client->reveal());
  }

  /**
   * @covers ::updateDisabledEntitiesOnInterestList
   */
  public function testDisabledEntitiesAreUpdatedOnInterestList(): void {
    $this->mockClient();
    /** @var \Drupal\acquia_contenthub_subscriber\Libs\InterestList\InterestListHandler $interest_list_handler */
    $interest_list_handler = $this->container->get('acquia_contenthub_subscriber.interest_list_handler');
    $interest_list = [
      'uuids' => [$this->articleNode->uuid()],
      'disable_syndication' => TRUE,
    ];
    $this->client
      ->updateInterestListBySiteRole(self::WEBHOOK_UUID, 'subscriber', $interest_list)
      ->shouldBeCalled()
      ->willReturn(new Response());
    self::assertEmpty($this->loggerMock->getLogMessages());
    $interest_list_handler->updateDisabledEntitiesOnInterestList();
    $config_entities = [
      "{$this->articleContentType->getEntityTypeId()} : {$this->articleContentType->uuid()}",
    ];
    $info_messages = $this->loggerMock->getInfoMessages();
    self::assertContains(sprintf('All the config entities (%s) that were set to AUTO_UPDATE_DISABLED have been skipped for getting updated as disabled for syndication in interest list.',
      implode(', ', $config_entities)
    ), $info_messages);
    $content_entities = [
      "{$this->articleNode->getEntityTypeId()} : {$this->articleNode->uuid()}",
    ];
    self::assertContains(sprintf('All the content entities (%s) that were set to AUTO_UPDATE_DISABLED have been marked as disabled for syndication in interest list.',
      implode(', ', $content_entities)
    ), $info_messages);
  }

  /**
   * @covers ::updateDisabledEntitiesOnInterestList
   */
  public function testUnsuccessfulInterestListUpdate(): void {
    $this->mockClient();
    /** @var \Drupal\acquia_contenthub_subscriber\Libs\InterestList\InterestListHandler $interest_list_handler */
    $interest_list_handler = $this->container->get('acquia_contenthub_subscriber.interest_list_handler');
    $interest_list = [
      'uuids' => [$this->articleNode->uuid()],
      'disable_syndication' => TRUE,
    ];
    $this->client
      ->updateInterestListBySiteRole(self::WEBHOOK_UUID, 'subscriber', $interest_list)
      ->shouldBeCalled()
      ->willReturn(new Response(500, [], 'Service failure: cannot update interest list.'));
    self::assertEmpty($this->loggerMock->getLogMessages());
    $interest_list_handler->updateDisabledEntitiesOnInterestList();
    $config_entities = [
      "{$this->articleContentType->getEntityTypeId()} : {$this->articleContentType->uuid()}",
    ];
    $info_messages = $this->loggerMock->getInfoMessages();
    self::assertContains(sprintf('All the config entities (%s) that were set to AUTO_UPDATE_DISABLED have been skipped for getting updated as disabled for syndication in interest list.',
      implode(', ', $config_entities)
    ), $info_messages);
    self::assertContains(sprintf('Could not update interest list for disabled entities. Error code: %s. Error: %s', 500, 'Service failure: cannot update interest list.'), $this->loggerMock->getErrorMessages());
  }

  /**
   * @covers ::getDisabledContentEntitiesFromTrackingTable
   */
  public function testGetDisabledContentEntitiesFromTrackingTable(): void {
    /** @var \Drupal\acquia_contenthub_subscriber\Libs\InterestList\InterestListHandler $interest_list_handler */
    $interest_list_handler = $this->container->get('acquia_contenthub_subscriber.interest_list_handler');
    $disabled_entities = $interest_list_handler->getDisabledContentEntitiesFromTrackingTable();
    self::assertArrayHasKey($this->articleNode->uuid(), $disabled_entities);
  }

  /**
   * @covers ::getDisabledContentEntitiesFromTrackingTable
   */
  public function testClientMissingExceptionOnUpdatingDisabledEntities(): void {
    /** @var \Drupal\acquia_contenthub_subscriber\Libs\InterestList\InterestListHandler $interest_list_handler */
    $interest_list_handler = $this->container->get('acquia_contenthub_subscriber.interest_list_handler');
    $this->expectException(ContentHubClientException::class);
    $this->expectExceptionMessage('Error trying to connect to the Content Hub. Make sure this site is registered to Content hub.');
    $interest_list_handler->updateDisabledEntitiesOnInterestList();
  }

  /**
   * @covers ::getDisabledContentEntitiesFromTrackingTable
   */
  public function testWebhookMissingExceptionOnUpdatingDisabledEntities(): void {
    $this->mockClient(FALSE);
    /** @var \Drupal\acquia_contenthub_subscriber\Libs\InterestList\InterestListHandler $interest_list_handler */
    $interest_list_handler = $this->container->get('acquia_contenthub_subscriber.interest_list_handler');
    $this->expectException(ContentHubException::class);
    $this->expectExceptionMessage('Webhook not available, unable to fetch interest list.');
    $interest_list_handler->updateDisabledEntitiesOnInterestList();
  }

  /**
   * @covers ::updateInterestList
   */
  public function testUpdateInterestList(): void {
    $this->mockClient();
    $interest_list = [
      'uuids' => [$this->articleNode->uuid()],
      'disable_syndication' => TRUE,
    ];
    $this->client
      ->updateInterestListBySiteRole(self::WEBHOOK_UUID, 'subscriber', $interest_list)
      ->shouldBeCalled();

    /** @var \Drupal\acquia_contenthub_subscriber\Libs\InterestList\InterestListHandler $interest_list_handler */
    $interest_list_handler = $this->container->get('acquia_contenthub_subscriber.interest_list_handler');
    $interest_list_handler->updateInterestList([$this->articleNode->uuid()], self::WEBHOOK_UUID, TRUE);
  }

}
