<?php

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

use Acquia\ContentHubClient\ContentHubClient;
use Acquia\ContentHubClient\Settings;
use Drupal\acquia_contenthub\Client\ClientFactory;
use Drupal\acquia_contenthub\Libs\ServiceQueue\QueueItem;
use Drupal\acquia_contenthub\Libs\ServiceQueue\ServiceQueueEntityActions;
use Drupal\acquia_contenthub_subscriber\Libs\PullSyndication\EntityDeleteAction;
use Drupal\acquia_contenthub_subscriber\SubscriberTracker;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\NodeInterface;
use Drupal\Tests\acquia_contenthub\Kernel\Traits\RequestTrait;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
use Drupal\Tests\node\Traits\NodeCreationTrait;
use Drupal\user\Entity\Role;
use Drupal\user\Entity\User;
use Drupal\user\RoleInterface;
use Drupal\user\UserInterface;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;

/**
 * Tests entity deletion action.
 *
 * @group acquia_contenthub_subscriber
 *
 * @coversDefaultClass \Drupal\acquia_contenthub_subscriber\Libs\PullSyndication\EntityDeleteAction
 *
 * @requires module depcalc
 *
 * @package Drupal\Tests\acquia_contenthub_subscriber\Kernel\Libs\PullSyndication
 */
class EntityDeleteActionTest extends EntityKernelTestBase {

  use ContentTypeCreationTrait;
  use NodeCreationTrait;
  use RequestTrait;
  use ProphecyTrait;

  /**
   * Entity delete action instance.
   *
   * @var \Drupal\acquia_contenthub_subscriber\Libs\PullSyndication\EntityDeleteAction
   */
  protected EntityDeleteAction $entityDeleteAction;

  /**
   * The node entity.
   *
   * @var \Drupal\node\NodeInterface
   */
  protected NodeInterface $node;

  /**
   * The user entity.
   *
   * @var \Drupal\user\UserInterface
   */
  protected UserInterface $user;

  /**
   * The user role used in this test.
   *
   * @var \Drupal\user\RoleInterface
   */
  protected RoleInterface $role;

  /**
   * Modules to enable.
   *
   * @var array
   */
  protected static $modules = [
    'acquia_contenthub',
    'acquia_contenthub_subscriber',
    'depcalc',
    'node',
  ];

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

    $this->installSchema('acquia_contenthub_subscriber', ['acquia_contenthub_subscriber_import_tracking']);
    $this->installSchema('user', ['users_data']);
    $this->installSchema('node', 'node_access');
    $this->installEntitySchema('node');
    $this->installConfig([
      'field',
      'filter',
      'node',
    ]);

    $role_id = $this->drupalCreateRole([]);
    if (version_compare(\Drupal::VERSION, '10.1.0', '>=')) {
      $this->user = $this->createUser([], 'foo', FALSE, [
        'roles' => [$role_id],
      ]);
    }
    else {
      $this->user = $this->createUser([
        'roles' => [$role_id],
      ]);
    }
    $this->createContentType([
      'type' => 'article',
    ]);
    $this->node = $this->createNode([
      'type' => 'article',
      'uid' => $this->user->id(),
    ]);
    $this->role = Role::load($role_id);
    $user_uuid = $this->user->uuid();
    $role_uuid = $this->role->uuid();

    $tracker = $this->prophesize(SubscriberTracker::class);
    $tracker->getEntitiesByRemoteIdAndHash([$user_uuid => NULL])->willReturn([$this->user->uuid() => $this->user]);
    $tracker->getEntitiesByRemoteIdAndHash([$role_uuid => NULL])->willReturn([$this->role->uuid() => $this->role]);
    $tracker->getStatusesByUuids([$user_uuid])->willReturn([$user_uuid => 'test']);
    $tracker->getStatusesByUuids([$role_uuid])->willReturn([$role_uuid => 'test']);
    $ach_configurations = $this->container->get('acquia_contenthub.configuration');
    $deletion_handler = $this->container->get('acquia_contenthub_subscriber.user_deletion_handler');

    $client_factory = $this->getMockedContentHubClientFactory();
    $this->entityDeleteAction = new EntityDeleteAction($tracker->reveal(), $ach_configurations, $deletion_handler, $client_factory->reveal());
  }

  /**
   * @covers ::canHandle
   */
  public function testCanHandle(): void {
    $queue_item_data = [
      'id' => '123',
      'entity_uuid' => $this->user->uuid(),
      'client_uuid' => '8fb4c61c-bc0c-4451-a1aa-f576bf4eb966',
      'state' => 'queued',
      'payload' => ['action' => ServiceQueueEntityActions::DELETE],
      'visible_at' => '',
      'created_at' => '',
      'updated_at' => '',
    ];

    $queue_item = QueueItem::fromArray($queue_item_data);
    $this->assertTrue($this->entityDeleteAction->canHandle($queue_item));

    $queue_item_data['payload']['action'] = ServiceQueueEntityActions::CREATE;
    $queue_item = QueueItem::fromArray($queue_item_data);
    $this->assertFalse($this->entityDeleteAction->canHandle($queue_item));
  }

  /**
   * @covers ::execute
   * @covers ::isSupportedType
   */
  public function testExecuteDeletesUser(): void {
    $user = User::load($this->user->id());
    $this->assertFalse($user->isBlocked());

    $this->triggerDeleteActionWithQueueItem($this->user->uuid(), 'drupal8_content_entity');

    /** @var \Drupal\user\UserInterface $user */
    $user = User::load($this->user->id());
    $this->assertTrue($user->isBlocked());
  }

  /**
   * @covers ::execute
   * @covers ::isSupportedType
   */
  public function testExecuteWhenUserRoleSyndicationIsDisabled(): void {
    $settings = $this->container->get('acquia_contenthub_subscriber.user_syndication.settings');
    $settings->toggleUserRoleSyndication(TRUE);
    $this->triggerDeleteActionWithQueueItem($this->role->uuid(), 'drupal8_config_entity');

    $role = Role::load($this->role->id());
    $this->assertNotNull($role, 'Role exists, user role syndication is disabled.');
  }

  /**
   * @covers ::execute
   * @covers ::isSupportedType
   */
  public function testExecuteSkipsUnsupportedType(): void {
    $user = User::load($this->user->id());
    $this->assertFalse($user->isBlocked());

    $this->triggerDeleteActionWithQueueItem($this->user->uuid(), 'unsupported_type');

    /** @var \Drupal\user\UserInterface $user */
    $user = User::load($this->user->id());
    $this->assertFalse($user->isBlocked(), 'User should not be blocked due to unsupported type.');
  }

  /**
   * @covers ::execute
   */
  public function testExecuteHandlesAutoUpdateDisabled(): void {
    $tracker = $this->prophesize(SubscriberTracker::class);
    $tracker->getEntitiesByRemoteIdAndHash([$this->user->uuid() => NULL])->willReturn([$this->user->uuid() => $this->user]);
    $tracker->getStatusesByUuids([$this->user->uuid()])->willReturn([$this->user->uuid() => SubscriberTracker::AUTO_UPDATE_DISABLED]);
    $tracker->deleteMultipleByUuids([$this->user->uuid()])->shouldBeCalled();

    $ach_configurations = $this->container->get('acquia_contenthub.configuration');
    $deletion_handler = $this->container->get('acquia_contenthub_subscriber.user_deletion_handler');
    $client_factory = $this->getMockedContentHubClientFactory();
    $entityDeleteAction = new EntityDeleteAction($tracker->reveal(), $ach_configurations, $deletion_handler, $client_factory->reveal());

    $user = User::load($this->user->id());
    $this->assertFalse($user->isBlocked());

    $queue_item_data = [
      'id' => '123',
      'entity_uuid' => $this->user->uuid(),
      'client_uuid' => '8fb4c61c-bc0c-4451-a1aa-f576bf4eb966',
      'state' => 'queued',
      'payload' => [
        'action' => ServiceQueueEntityActions::DELETE,
        'type' => 'drupal8_content_entity',
      ],
      'visible_at' => '',
      'created_at' => '',
      'updated_at' => '',
    ];

    $queue_item = QueueItem::fromArray($queue_item_data);
    $entityDeleteAction->execute([$queue_item]);

    /** @var \Drupal\user\UserInterface $user */
    $user = User::load($this->user->id());
    $this->assertNotNull($user);
    $this->assertFalse($user->isBlocked(), 'User should not be blocked when auto-update is disabled.');
  }

  /**
   * Triggers the deletion action with a queue item.
   *
   * @param string $entity_uuid
   *   The entity UUID.
   * @param string $type
   *   The entity type.
   */
  protected function triggerDeleteActionWithQueueItem(string $entity_uuid, string $type): void {
    $queue_item_data = [
      'id' => '123',
      'entity_uuid' => $entity_uuid,
      'client_uuid' => '8fb4c61c-bc0c-4451-a1aa-f576bf4eb966',
      'state' => 'queued',
      'payload' => [
        'action' => ServiceQueueEntityActions::DELETE,
        'type' => $type,
      ],
      'visible_at' => '',
      'created_at' => '',
      'updated_at' => '',
    ];

    $queue_item = QueueItem::fromArray($queue_item_data);
    $this->entityDeleteAction->execute([$queue_item]);
  }

  /**
   * Returns a client mock.
   *
   * @return \Prophecy\Prophecy\ObjectProphecy|ClientFactory
   *   The prophecy object.
   *
   * @throws \Exception
   */
  public function getMockedContentHubClientFactory(): ObjectProphecy {
    /** @var \Drupal\acquia_contenthub\Settings\ContentHubConfigurationInterface $ach_configuration */
    $ach_configuration = $this->container->get('acquia_contenthub.configuration');
    $ch_connection = $ach_configuration->getConnectionDetails();
    $origin = $ch_connection->getClientUuid();
    $settings = $this->prophesize(Settings::class);
    $settings->getUuid()->willReturn($origin);
    $settings->getWebhook('uuid')->willReturn('webhook-uuid');
    $client = $this->prophesize(ContentHubClient::class);
    $client->getSettings()->willReturn($settings->reveal());
    $client->deleteMultipleInterest('webhook-uuid', Argument::any(), 'subscriber')->willReturn(TRUE);

    $client_factory = $this->prophesize(ClientFactory::class);
    $client_factory->getClient()->willReturn($client->reveal());

    return $client_factory;
  }

}
