<?php

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

use Acquia\ContentHubClient\ContentHubClient;
use Acquia\ContentHubClient\Settings;
use Acquia\Hmac\Key;
use Drupal\acquia_contenthub\Client\ClientFactory;
use Drupal\acquia_contenthub\Event\HandleWebhookEvent;
use Drupal\acquia_contenthub_subscriber\EventSubscriber\HandleWebhook\DeleteAssets;
use Drupal\acquia_contenthub_subscriber\SubscriberTracker;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\Entity\Node;
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\Prophecy\ObjectProphecy;

/**
 * Tests delete assets.
 *
 * @group acquia_contenthub
 *
 * @coversDefaultClass \Drupal\acquia_contenthub_subscriber\EventSubscriber\HandleWebhook\DeleteAssets
 *
 * @requires module depcalc
 *
 * @package Drupal\Tests\acquia_contenthub\Kernel\EventSubscriber\HandleWebhook
 */
class DeleteAssetsTest extends EntityKernelTestBase {

  use ContentTypeCreationTrait;
  use NodeCreationTrait;
  use RequestTrait;

  /**
   * Delete assets instance.
   *
   * @var \Drupal\acquia_contenthub_subscriber\EventSubscriber\HandleWebhook\DeleteAssets
   */
  protected DeleteAssets $deleteAssets;

  /**
   * 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;

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

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

  /**
   * {@inheritdoc}
   */
  protected 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',
    ]);
    $this->clientFactory = $this->container->get('acquia_contenthub.client.factory');

    $role_id = $this->drupalCreateRole([]);
    if (version_compare(\Drupal::VERSION, '10.1.0', '>=')) {
      $this->user = $this->createUser([], 'test_user', 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,
      $role_uuid => NULL,
    ])->willReturn([
      $user_uuid => $this->user,
      $role_uuid => $this->role,
    ]);
    $tracker->getStatusesByUuids([$user_uuid, $role_uuid])->willReturn([
      $user_uuid => 'test',
      $role_uuid => 'test',
    ]);
    $ach_configurations = $this->container->get('acquia_contenthub.configuration');
    $deletion_handler = $this->container->get('acquia_contenthub_subscriber.user_deletion_handler');
    $this->deleteAssets = new DeleteAssets($tracker->reveal(), $ach_configurations, $deletion_handler);
  }

  /**
   * Tests delete assets.
   *
   * @throws \Exception
   */
  public function testDeleteAsset(): void {
    $user = User::load($this->user->id());
    $this->assertFalse($user->isBlocked());

    $this->triggerDeleteEvent();

    $node = Node::load($this->node->id());
    $this->assertSame($this->user->id(), $node->getOwnerId());

    /** @var \Drupal\user\UserInterface $user */
    $user = $node->getOwner();
    $this->assertTrue($user->isBlocked());
  }

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

    $settings = $this->container->get('acquia_contenthub_subscriber.user_syndication.settings');
    $settings->toggleUserSyndication(TRUE);
    $this->triggerDeleteEvent();

    $node = Node::load($this->node->id());
    $this->assertSame($this->user->id(), $node->getOwnerId());

    /** @var \Drupal\user\UserInterface $user */
    $user = $node->getOwner();
    $this->assertFalse($user->isBlocked(), 'User is deletion protected, user syndication is disabled.');

    $role = Role::load($this->role->id());
    $this->assertNull($role, 'Role does not exists, user role syndication is enabled.');
  }

  /**
   * @covers ::onHandleWebhook
   * @covers ::isSupportedType
   */
  public function testDeleteAssetsWhenUserRoleSyndicationIsDisabled(): void {
    $settings = $this->container->get('acquia_contenthub_subscriber.user_syndication.settings');
    $settings->toggleUserRoleSyndication(TRUE);
    $this->triggerDeleteEvent();

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

  /**
   * @covers ::onHandleWebhook
   * @covers ::isValidRequestPayload
   */
  public function testDeleteAssetsWhenPayloadIsNotValid(): void {
    $user = User::load($this->user->id());
    $this->assertFalse($user->isBlocked());

    $settings = $this->container->get('acquia_contenthub_subscriber.user_syndication.settings');
    $this->assertFalse($settings->isUserSyndicationDisabled());

    $this->triggerDeleteEvent(['crud' => 'update']);

    /** @var \Drupal\user\UserInterface $user */
    $user = User::load($this->user->id());
    $this->assertFalse($user->isBlocked(), 'The payload is invalid, user does not get blocked.');
  }

  /**
   * Triggers the deletion event.
   *
   * @throws \Exception
   */
  protected function triggerDeleteEvent(array $payload_override = []): void {
    $key = new Key('id', 'secret');
    $payload = [
      'crud' => 'delete',
      'status' => 'successful',
      'initiator' => 'some-initiator-uuid',
      'assets' => [
        [
          'type' => 'drupal8_content_entity',
          'uuid' => $this->user->uuid(),
        ],
        [
          'type' => 'drupal8_config_entity',
          'uuid' => $this->role->uuid(),
        ],
      ],
    ];
    $payload = array_replace($payload, $payload_override);

    $client = $this->getMockedContentHubClient();
    $event = new HandleWebhookEvent($this->createSignedRequest(), $payload, $key, $client->reveal());
    $this->deleteAssets->onHandleWebhook($event);
  }

  /**
   * Returns a client mock.
   *
   * @return \Prophecy\Prophecy\ObjectProphecy
   *   The prophecy object.
   *
   * @throws \Exception
   */
  public function getMockedContentHubClient(): 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);
    $client = $this->prophesize(ContentHubClient::class);
    $client->getSettings()->willReturn($settings->reveal());

    return $client;
  }

}
