<?php

namespace Drupal\Tests\acquia_contenthub_subscriber\Kernel\EventSubscriber\LoadLocalEntity;

use Acquia\ContentHubClient\CDF\CDFObject;
use Acquia\ContentHubClient\CDFAttribute;
use Drupal\acquia_contenthub\Event\LoadLocalEntityEvent;
use Drupal\acquia_contenthub_subscriber\EventSubscriber\LoadLocalEntity\LoadMatchingPathAlias;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\depcalc\DependencyStack;
use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\node\NodeInterface;
use Drupal\path_alias\Entity\PathAlias;
use Drupal\path_alias\PathAliasInterface;
use Drupal\Tests\acquia_contenthub\Unit\Helpers\LoggerMock;
use Drupal\Tests\node\Traits\ContentTypeCreationTrait;
use Drupal\Tests\node\Traits\NodeCreationTrait;
use Psr\Log\LoggerInterface;

/**
 * Tests prevention of path alias duplicacy.
 *
 * @coversDefaultClass \Drupal\acquia_contenthub_subscriber\EventSubscriber\LoadLocalEntity\LoadMatchingPathAlias
 *
 * @group acquia_contenthub_subscriber
 *
 * @requires module depcalc
 */
class LoadMatchingPathAliasTest extends EntityKernelTestBase {

  use ContentTypeCreationTrait;
  use NodeCreationTrait;

  /**
   * Path alias entity.
   *
   * @var \Drupal\path_alias\PathAliasInterface
   */
  protected PathAliasInterface $pathAliasEntity;

  /**
   * The config factory.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected ConfigFactoryInterface $configFactory;

  /**
   * The acquia_contenthub_subscriber logger channel.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected LoggerInterface $logger;

  /**
   * The CDF object.
   *
   * @var \Acquia\ContentHubClient\CDF\CDFObjectInterface|\Prophecy\Prophecy\ObjectProphecy
   */
  protected $cdfObject;

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

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

  /**
   * {@inheritDoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->installConfig([
      'filter',
      'node',
    ]);
    $this->installEntitySchema('path_alias');

    $this->createContentType([
      'type' => 'article',
    ]);
    $this->node = $this->createNode([
      'type' => 'article',
    ]);
    $this->pathAliasEntity = PathAlias::create([
      'path' => '/node/' . $this->node->id(),
      'alias' => '/testing-path-alias1',
      'langcode' => 'en',
    ]);
    $this->pathAliasEntity->save();

    $this->configFactory = $this->container->get('config.factory');
    $this->logger = new LoggerMock();

    $cdf_attribute = $this->prophesize(CDFAttribute::class);
    $cdf_attribute->getValue()->willReturn([
      CDFObject::LANGUAGE_UNDETERMINED => 'path_alias',
    ]);
    $this->cdfObject = $this->prophesize(CDFObject::class);
    $this->cdfObject->getAttribute('entity_type')->willReturn($cdf_attribute->reveal());
  }

  /**
   * Testing prevention of path alias duplicacy feature.
   *
   * @throws \Exception
   */
  public function testPreventionOfDuplicatePathAlias(): void {
    $data = [
      'uuid' => [
        'value' => [
          'en' => [
            'value' => 'some-uuid',
          ],
        ],
      ],
      'alias' => [
        'value' => [
          'en' => '/testing-path-alias1',
        ],
      ],
    ];
    $this->cdfObject->getMetadata()->willReturn([
      'data' => base64_encode(json_encode($data)),
      'default_language' => 'en',
    ]);
    $this->cdfObject->getUuid()->willReturn('some-uuid');

    $this->disableDuplicatePathAlias();

    $local_entity_event = new LoadLocalEntityEvent($this->cdfObject->reveal(), new DependencyStack());
    $sut = new LoadMatchingPathAlias($this->entityTypeManager, $this->configFactory, $this->logger);
    $sut->onLoadLocalEntity($local_entity_event);
    /** @var \Drupal\path_alias\PathAliasInterface $entity */
    $entity = $local_entity_event->getEntity();

    $this->assertSame($this->pathAliasEntity->uuid(), $entity->uuid());
    $this->assertSame($this->pathAliasEntity->getAlias(), $entity->getAlias());
    $this->assertSame(
      'Overriding local path alias: ' . $this->pathAliasEntity->getAlias() . ' with pid: ' . $this->pathAliasEntity->id(),
      $this->logger->getNoticeMessages()[0]
    );
  }

  /**
   * Tests multiple duplicate path aliases.
   */
  public function testMultipleDuplicatePathAliases(): void {
    $duplicate_path_alias_entity = PathAlias::create([
      'path' => '/node/' . $this->node->id(),
      'alias' => '/testing-path-alias1',
      'langcode' => 'en',
    ]);
    $duplicate_path_alias_entity->save();
    $data = [
      'uuid' => [
        'value' => [
          'en' => [
            'value' => 'some-uuid',
          ],
        ],
      ],
      'alias' => [
        'value' => [
          'en' => '/testing-path-alias1',
        ],
      ],
    ];
    $this->cdfObject->getMetadata()->willReturn([
      'data' => base64_encode(json_encode($data)),
      'default_language' => 'en',
    ]);
    $this->cdfObject->getUuid()->willReturn('some-uuid');

    $this->disableDuplicatePathAlias();

    $local_entity_event = new LoadLocalEntityEvent($this->cdfObject->reveal(), new DependencyStack());
    $sut = new LoadMatchingPathAlias($this->entityTypeManager, $this->configFactory, $this->logger);
    $sut->onLoadLocalEntity($local_entity_event);
    /** @var \Drupal\path_alias\PathAliasInterface $entity */
    $entity = $local_entity_event->getEntity();

    $this->assertSame($this->pathAliasEntity->uuid(), $entity->uuid());
    $this->assertSame($this->pathAliasEntity->getAlias(), $entity->getAlias());
    $this->assertSame(
      'Multiple duplicate local path aliases found for alias: ' . $this->pathAliasEntity->getAlias() . ' with pids: ' . $this->pathAliasEntity->id() . ',' . $duplicate_path_alias_entity->id() . '.',
      $this->logger->getNoticeMessages()[0]
    );
    $this->assertSame(
      'Overriding local path alias: ' . $this->pathAliasEntity->getAlias() . ' with pid: ' . $this->pathAliasEntity->id(),
      $this->logger->getNoticeMessages()[1]
    );
  }

  /**
   * Testing when duplicate path aliases are allowed.
   */
  public function testAllowDuplicatePathAlias(): void {
    $data = [
      'uuid' => [
        'value' => [
          'en' => [
            'value' => 'some-uuid',
          ],
        ],
      ],
      'alias' => [
        'value' => [
          'en' => '/testing-path-alias1',
        ],
      ],
    ];
    $this->cdfObject->getMetadata()->willReturn([
      'data' => base64_encode(json_encode($data)),
      'default_language' => 'en',
    ]);
    $this->cdfObject->getUuid()->willReturn('some-uuid');

    // Allowing duplicacy of path alias.
    $this->disableDuplicatePathAlias(FALSE);

    $local_entity_event = new LoadLocalEntityEvent($this->cdfObject->reveal(), new DependencyStack());
    $sut = new LoadMatchingPathAlias($this->entityTypeManager, $this->configFactory, $this->logger);
    $sut->onLoadLocalEntity($local_entity_event);
    $entity = $local_entity_event->getEntity();
    $this->assertNull($entity);
  }

  /**
   * Testing empty path alias cdf, when duplicate path aliases are disabled.
   */
  public function testEmptyPathAlias(): void {
    // CDF data with empty path alias.
    $data = [
      'uuid' => [
        'value' => [
          'en' => [
            'value' => 'some-uuid',
          ],
        ],
      ],
      'alias' => [],
    ];

    $this->cdfObject->getMetadata()->willReturn([
      'data' => base64_encode(json_encode($data)),
      'default_language' => 'en',
    ]);
    $this->cdfObject->getUuid()->willReturn('some-uuid');

    // Disabling duplicacy of path alias.
    $this->disableDuplicatePathAlias();

    $local_entity_event = new LoadLocalEntityEvent($this->cdfObject->reveal(), new DependencyStack());
    $sut = new LoadMatchingPathAlias($this->entityTypeManager, $this->configFactory, $this->logger);
    $sut->onLoadLocalEntity($local_entity_event);
    /** @var \Drupal\path_alias\PathAliasInterface $entity */
    $entity = $local_entity_event->getEntity();
    // No local entity found.
    $this->assertNull($entity);
    $this->assertEmpty($this->logger->getNoticeMessages());
  }

  /**
   * Disables duplicate path alias import.
   *
   * @param bool $disable
   *   Boolean value to disable duplicate path alias.
   */
  protected function disableDuplicatePathAlias(bool $disable = TRUE): void {
    $this->configFactory->getEditable('acquia_contenthub_subscriber.import_settings')
      ->set('disable_duplicate_path_aliases', $disable)
      ->save();
  }

}
