<?php

namespace Drupal\Tests\acquia_contenthub_subscriber\Unit\Libs;

use Drupal\acquia_contenthub_subscriber\Libs\UserSyndicationSettings;
use Drupal\Core\Config\Config;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\user\UserInterface;
use Prophecy\Argument;

/**
 * @coversDefaultClass \Drupal\acquia_contenthub_subscriber\Libs\UserSyndicationSettings
 *
 * @group acquia_contenthub_subscriber
 */
class UserSyndicationSettingsUnitTest extends UnitTestCase {

  /**
   * A mocked configuration object.
   *
   * @var \Drupal\Core\Config\Config|\Prophecy\Prophecy\ObjectProphecy
   */
  protected $config;

  /**
   * A mocked entity storage for user entity.
   *
   * @var \Drupal\Core\Entity\EntityStorageInterface|\Prophecy\Prophecy\ObjectProphecy
   */
  protected $userStorage;

  /**
   * {@inheritdoc}
   */
  public function setUp(): void {
    parent::setUp();
    $this->userStorage = $this->prophesize(EntityStorageInterface::class);
    $this->config = $this->prophesize(Config::class);
    // By default let's return empty config array.
    $this->config->getRawData()->willReturn([]);
  }

  /**
   * @covers ::isUserSyndicationDisabled
   * @covers ::toggleUserSyndication
   */
  public function testIsUserSyndicationDisabledWithoutConfiguration(): void {
    $sut = $this->newUserSyndicationSettings();
    $this->assertEquals(FALSE, $sut->isUserSyndicationDisabled());

    $sut->toggleUserSyndication(TRUE);
    $this->assertEquals(TRUE, $sut->isUserSyndicationDisabled());

    $sut->toggleUserSyndication(FALSE);
    $this->assertEquals(FALSE, $sut->isUserSyndicationDisabled());
  }

  /**
   * @covers ::isUserSyndicationDisabled
   * @covers ::toggleUserSyndication
   */
  public function testIsUserSyndicationDisabledWithConfiguration(): void {
    $this->config->getRawData()->willReturn([
      'disable_user_syndication' => FALSE,
    ]);
    $sut = $this->newUserSyndicationSettings();
    $this->assertEquals(FALSE, $sut->isUserSyndicationDisabled());

    $this->config->getRawData()->willReturn([
      'disable_user_syndication' => TRUE,
    ]);
    $sut = $this->newUserSyndicationSettings();
    $this->assertEquals(TRUE, $sut->isUserSyndicationDisabled());

    $sut->toggleUserSyndication(TRUE);
    $this->assertEquals(TRUE, $sut->isUserSyndicationDisabled());

    $sut->toggleUserSyndication(FALSE);
    $this->assertEquals(FALSE, $sut->isUserSyndicationDisabled());
  }

  /**
   * @covers ::isUserRoleSyndicationDisabled
   * @covers ::toggleUserRoleSyndication
   */
  public function testIsUserRoleSyndicationDisabledWithoutConfiguration(): void {
    $sut = $this->newUserSyndicationSettings();
    $this->assertEquals(FALSE, $sut->isUserRoleSyndicationDisabled());

    $sut->toggleUserRoleSyndication(TRUE);
    $this->assertEquals(TRUE, $sut->isUserRoleSyndicationDisabled());

    $sut->toggleUserRoleSyndication(FALSE);
    $this->assertEquals(FALSE, $sut->isUserRoleSyndicationDisabled());
  }

  /**
   * @covers ::isUserRoleSyndicationDisabled
   * @covers ::toggleUserRoleSyndication
   */
  public function testIsUserRoleSyndicationDisabledWithConfiguration(): void {
    $this->config->getRawData()->willReturn([
      'disable_user_role_syndication' => FALSE,
    ]);
    $sut = $this->newUserSyndicationSettings();
    $this->assertEquals(FALSE, $sut->isUserRoleSyndicationDisabled());

    $this->config->getRawData()->willReturn([
      'disable_user_role_syndication' => TRUE,
    ]);
    $sut = $this->newUserSyndicationSettings();
    $this->assertEquals(TRUE, $sut->isUserRoleSyndicationDisabled());

    $sut->toggleUserRoleSyndication(TRUE);
    $this->assertEquals(TRUE, $sut->isUserRoleSyndicationDisabled());

    $sut->toggleUserRoleSyndication(FALSE);
    $this->assertEquals(FALSE, $sut->isUserRoleSyndicationDisabled());
  }

  /**
   * @covers ::setProxyUser
   * @covers ::getProxyUser
   */
  public function testSetProxyUserWithNonExistingUser(): void {
    $sut = $this->newUserSyndicationSettings();
    $this->assertNull($sut->getProxyUser(), 'By default there is no proxy user configured');

    $this->userStorage->load(Argument::type('integer'))->willReturn(NULL);
    $sut = $this->newUserSyndicationSettings();

    $sut->setProxyUser(42);
    $this->assertNull($sut->getProxyUser(), 'The user does not exist');
  }

  /**
   * @covers ::setProxyUser
   * @covers ::getProxyUser
   */
  public function testSetProxyUserWithExistingUser(): void {
    $user = $this->prophesize(UserInterface::class);
    $this->userStorage->load(Argument::type('integer'))->willReturn($user->reveal());
    $sut = $this->newUserSyndicationSettings();

    $sut->setProxyUser(42);
    $this->assertInstanceOf(UserInterface::class, $sut->getProxyUser(), 'The user exists');
  }

  /**
   * Tests if the save function is called on config.
   *
   * In order to test this thoroughly it requires kernel tests.
   *
   * @covers ::save
   */
  public function testSave(): void {
    $user = $this->prophesize(UserInterface::class);
    $this->userStorage->load(1)->willReturn($user->reveal());
    $this->config->save()->shouldBeCalledOnce();
    $this->config->setData([
      'proxy_user' => 1,
      'disable_user_syndication' => TRUE,
    ])->shouldBeCalledOnce();

    $sut = $this->newUserSyndicationSettings();
    $sut->setProxyUser(1);
    $sut->toggleUserSyndication(TRUE);
    $sut->save();
  }

  /**
   * Returns and new UserSyndicationSettings instance.
   *
   * The behaviour of passed dependencies is alterable by setting them up first,
   * in the individual test cases.
   *
   * @return \Drupal\acquia_contenthub_subscriber\Libs\UserSyndicationSettings
   *   A new instance with mocked dependencies.
   *
   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
   * @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
   */
  protected function newUserSyndicationSettings(): UserSyndicationSettings {
    $etm = $this->prophesize(EntityTypeManagerInterface::class);
    $config_factory = $this->prophesize(ConfigFactoryInterface::class);
    $etm->getStorage('user')->willReturn($this->userStorage->reveal());
    $config_factory->getEditable('acquia_contenthub_subscriber.user_syndication')
      ->willReturn($this->config->reveal());
    return new UserSyndicationSettings(
      $etm->reveal(),
      $config_factory->reveal(),
    );
  }

}
