<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_trigger\Unit\Hook;

use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\flowdrop_trigger\Hook\EntityHooks;
use Drupal\flowdrop_trigger\Service\TriggerManagerInterface;
use Drupal\Tests\UnitTestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;

/**
 * Tests the EntityHooks class.
 *
 * @coversDefaultClass \Drupal\flowdrop_trigger\Hook\EntityHooks
 * @group flowdrop_trigger
 */
class EntityHooksTest extends UnitTestCase {

  use ProphecyTrait;

  /**
   * The entity hooks instance under test.
   *
   * @var \Drupal\flowdrop_trigger\Hook\EntityHooks
   */
  private EntityHooks $hooks;

  /**
   * The trigger manager mock.
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|\Drupal\flowdrop_trigger\Service\TriggerManagerInterface
   */
  private $triggerManager;

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

    $this->triggerManager = $this->prophesize(TriggerManagerInterface::class);
    $this->hooks = new EntityHooks($this->triggerManager->reveal());
  }

  /**
   * @covers ::entityInsert
   */
  public function testEntityInsertContentEntityCallsProcessEvent(): void {
    $entity = $this->prophesize(ContentEntityInterface::class);

    $this->triggerManager->processEvent('entity.insert', $entity->reveal())
      ->shouldBeCalledOnce();

    $this->hooks->entityInsert($entity->reveal());
  }

  /**
   * @covers ::entityInsert
   */
  public function testEntityInsertNonContentEntitySkipped(): void {
    $entity = $this->prophesize(EntityInterface::class);

    $this->triggerManager->processEvent(Argument::cetera())
      ->shouldNotBeCalled();

    $this->hooks->entityInsert($entity->reveal());
  }

  /**
   * @covers ::entityUpdate
   */
  public function testEntityUpdateCallsProcessEvent(): void {
    $entityType = $this->prophesize(EntityTypeInterface::class);
    $entityType->hasKey('revision')->willReturn(FALSE);

    $entity = $this->prophesize(ContentEntityInterface::class);
    $entity->getEntityType()->willReturn($entityType->reveal());

    $this->triggerManager->processEvent('entity.update', $entity->reveal())
      ->shouldBeCalledOnce();

    $this->hooks->entityUpdate($entity->reveal());
  }

  /**
   * @covers ::entityUpdate
   */
  public function testEntityUpdateRevisionableDisablesNewRevision(): void {
    $entityType = $this->prophesize(EntityTypeInterface::class);
    $entityType->hasKey('revision')->willReturn(TRUE);

    $entity = $this->prophesize(ContentEntityInterface::class);
    $entity->getEntityType()->willReturn($entityType->reveal());
    $entity->setNewRevision(FALSE)->shouldBeCalled();
    $entity->updateLoadedRevisionId()->shouldBeCalled();

    $this->triggerManager->processEvent('entity.update', $entity->reveal())
      ->shouldBeCalledOnce();

    $this->hooks->entityUpdate($entity->reveal());
  }

  /**
   * @covers ::entityDelete
   */
  public function testEntityDeleteCallsProcessEvent(): void {
    $entity = $this->prophesize(ContentEntityInterface::class);

    $this->triggerManager->processEvent('entity.delete', $entity->reveal())
      ->shouldBeCalledOnce();

    $this->hooks->entityDelete($entity->reveal());
  }

  /**
   * @covers ::entityPresave
   */
  public function testEntityPresaveCallsProcessEvent(): void {
    $entity = $this->prophesize(ContentEntityInterface::class);

    $this->triggerManager->processEvent('entity.presave', $entity->reveal())
      ->shouldBeCalledOnce();

    $this->hooks->entityPresave($entity->reveal());
  }

  /**
   * @covers ::entityCreate
   */
  public function testEntityCreateCallsProcessEvent(): void {
    $entity = $this->prophesize(ContentEntityInterface::class);

    $this->triggerManager->processEvent('entity.create', $entity->reveal())
      ->shouldBeCalledOnce();

    $this->hooks->entityCreate($entity->reveal());
  }

  /**
   * @covers ::entityLoad
   */
  public function testEntityLoadCallsProcessEventForContentEntities(): void {
    $content = $this->prophesize(ContentEntityInterface::class);
    $config = $this->prophesize(EntityInterface::class);

    $this->triggerManager->processEvent('entity.load', $content->reveal())
      ->shouldBeCalledOnce();

    // Config entity should be skipped.
    $this->hooks->entityLoad([$content->reveal(), $config->reveal()], 'node');
  }

  /**
   * @covers ::entityRevisionCreate
   */
  public function testEntityRevisionCreateCallsProcessEvent(): void {
    $newRevision = $this->prophesize(ContentEntityInterface::class);
    $source = $this->prophesize(EntityInterface::class);

    $this->triggerManager->processEvent('entity.revision_create', $newRevision->reveal())
      ->shouldBeCalledOnce();

    $this->hooks->entityRevisionCreate($newRevision->reveal(), $source->reveal(), NULL);
  }

  /**
   * @covers ::entityTranslationInsert
   */
  public function testEntityTranslationInsertCallsProcessEvent(): void {
    $translation = $this->prophesize(ContentEntityInterface::class);

    $this->triggerManager->processEvent('entity.translation_insert', $translation->reveal())
      ->shouldBeCalledOnce();

    $this->hooks->entityTranslationInsert($translation->reveal());
  }

}
