<?php

declare(strict_types=1);

namespace Drupal\Tests\visitors\Unit\Form;

use Drupal\Tests\UnitTestCase;
use Drupal\user\RoleInterface;
use Drupal\visitors\Form\SettingsForm;
use Prophecy\PhpUnit\ProphecyTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * Tests settings form.
 *
 * @group visitors
 * @coversDefaultClass \Drupal\visitors\Form\SettingsForm
 * @uses \Drupal\visitors\Form\SettingsForm
 */
class SettingsFormTest extends UnitTestCase {

  use ProphecyTrait;

  /**
   * The form under test.
   *
   * @var \Drupal\visitors\Form\SettingsForm
   */
  protected $form;

  /**
   * The extension theme list.
   *
   * @var \Drupal\Core\Extension\ThemeExtensionList|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $extensionThemeList;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $entityTypeManager;

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

  /**
   * The request stack.
   *
   * @var \Symfony\Component\HttpFoundation\RequestStack|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $requestStack;

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

    $container = new ContainerBuilder();

    $string_translation = $this->getStringTranslationStub();
    $container->set('string_translation', $string_translation);

    $this->extensionThemeList = $this->createMock('Drupal\Core\Extension\ThemeExtensionList');
    $container->set('extension.list.theme', $this->extensionThemeList);

    $this->entityTypeManager = $this->createMock('Drupal\Core\Entity\EntityTypeManagerInterface');
    $container->set('entity_type.manager', $this->entityTypeManager);

    $this->configFactory = $this->createMock('Drupal\Core\Config\ConfigFactoryInterface');
    $container->set('config.factory', $this->configFactory);

    $this->requestStack = $this->createMock('Symfony\Component\HttpFoundation\RequestStack');
    $container->set('request_stack', $this->requestStack);

    $unrouted_url_assembler = $this->createMock('Drupal\Core\Utility\UnroutedUrlAssemblerInterface');
    $container->set('unrouted_url_assembler', $unrouted_url_assembler);

    $url_generator = $this->createMock('Drupal\Core\Routing\UrlGeneratorInterface');
    $container->set('url_generator', $url_generator);

    $messenger = $this->createMock('Drupal\Core\Messenger\MessengerInterface');
    $container->set('messenger', $messenger);

    \Drupal::setContainer($container);

    $this->form = SettingsForm::create($container);
  }

  /**
   * Tests the create method.
   *
   * @covers ::create
   */
  public function testCreate() {
    $container = \Drupal::getContainer();
    $form = SettingsForm::create($container);
    $this->assertInstanceOf(SettingsForm::class, $form);
  }

  /**
   * Tests the construct method.
   *
   * @covers ::__construct
   */
  public function testConstruct() {
    $form = new SettingsForm(
      $this->entityTypeManager,
    );
    $this->assertInstanceOf(SettingsForm::class, $form);
  }

  /**
   * Test the getFormId method.
   *
   * @covers ::getFormId
   */
  public function testGetFormId() {
    $this->assertEquals('visitors_admin_settings', $this->form->getFormId());
  }

  /**
   * Test the buildForm method.
   *
   * @covers ::buildForm
   * @covers ::roleOptions
   * @covers ::entityTypes
   * @covers ::getEditableConfigNames
   */
  public function testBuildForm() {
    $user_role_storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
    $this->entityTypeManager->expects($this->once())
      ->method('getStorage')
      ->with('user_role')
      ->willReturn($user_role_storage);
    $anonymous_role = $this->createMock('Drupal\user\RoleInterface');
    $anonymous_role->expects($this->once())
      ->method('id')
      ->willReturn(RoleInterface::ANONYMOUS_ID);
    $anonymous_role->expects($this->once())
      ->method('label')
      ->willReturn('Anonymous');
    $authenticate_role = $this->createMock('Drupal\user\RoleInterface');
    $authenticate_role->expects($this->once())
      ->method('id')
      ->willReturn(RoleInterface::AUTHENTICATED_ID);
    $authenticate_role->expects($this->once())
      ->method('label')
      ->willReturn('Authenticated');
    $administrator_role = $this->createMock('Drupal\user\RoleInterface');
    $administrator_role->expects($this->once())
      ->method('id')
      ->willReturn('administrator');
    $administrator_role->expects($this->once())
      ->method('label')
      ->willReturn('Administrator');
    $user_roles = [$anonymous_role, $authenticate_role, $administrator_role];
    $user_role_storage->expects($this->once())
      ->method('loadMultiple')
      ->willReturn($user_roles);

    $config = $this->createMock('Drupal\Core\Config\Config');
    $config
      ->expects($this->exactly(14))
      ->method('get')
      ->willReturnMap([
        ['flush_log_timer', 0],
        ['bot_retention_log', 0],
        ['items_per_page', 10],
        ['disable_tracking', FALSE],
        ['visibility.request_path_mode', 1],
        ['script_type', 'minified'],
      ]);

    $this->configFactory->expects($this->once())
      ->method('getEditable')
      ->with('visitors.settings')
      ->willReturn($config);

    $request = $this->createMock('Symfony\Component\HttpFoundation\Request');
    $request_stack = $this->createMock('Symfony\Component\HttpFoundation\RequestStack');

    $node_definition = $this->createMock('Drupal\Core\Entity\ContentEntityType');
    $node_type_definition = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityType');

    $this->entityTypeManager->expects($this->once())
      ->method('getDefinitions')
      ->willReturn([$node_definition, $node_type_definition]);

    $form_state = $this->createMock('Drupal\Core\Form\FormStateInterface');
    $form = $this->form->buildForm([], $form_state);

    $this->assertIsArray($form);
    $this->assertArrayHasKey('actions', $form);
    $this->assertArrayHasKey('#theme', $form);
    $this->assertArrayHasKey('tracking_scope', $form);
    $this->assertArrayHasKey('retention', $form);
    $this->assertArrayHasKey('miscellaneous', $form);
    $this->assertArrayHasKey('entity', $form);
  }

  /**
   * Test the buildForm method with different options.
   *
   * @covers ::buildForm
   * @covers ::entityTypes
   * @covers ::roleOptions
   */
  public function testBuildFormOtherSettings() {
    $user_role_storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
    $this->entityTypeManager->expects($this->once())
      ->method('getStorage')
      ->with('user_role')
      ->willReturn($user_role_storage);
    $anonymous_role = $this->createMock('Drupal\user\RoleInterface');
    $anonymous_role->expects($this->once())
      ->method('id')
      ->willReturn(RoleInterface::ANONYMOUS_ID);
    $anonymous_role->expects($this->once())
      ->method('label')
      ->willReturn('Anonymous');
    $authenticate_role = $this->createMock('Drupal\user\RoleInterface');
    $authenticate_role->expects($this->once())
      ->method('id')
      ->willReturn(RoleInterface::AUTHENTICATED_ID);
    $authenticate_role->expects($this->once())
      ->method('label')
      ->willReturn('Authenticated');
    $administrator_role = $this->createMock('Drupal\user\RoleInterface');
    $administrator_role->expects($this->once())
      ->method('id')
      ->willReturn('administrator');
    $administrator_role->expects($this->once())
      ->method('label')
      ->willReturn('Administrator');
    $user_roles = [$anonymous_role, $authenticate_role, $administrator_role];
    $user_role_storage->expects($this->once())
      ->method('loadMultiple')
      ->willReturn($user_roles);

    $config = $this->createMock('Drupal\Core\Config\Config');
    $config
      ->expects($this->exactly(14))
      ->method('get')
      ->willReturnMap([
        ['flush_log_timer', 0],
        ['bot_retention_log', 0],
        ['items_per_page', 10],
        ['disable_tracking', FALSE],
        ['visibility.request_path_mode', 1],
        ['script_type', 'minified'],
      ]);

    $this->configFactory->expects($this->once())
      ->method('getEditable')
      ->with('visitors.settings')
      ->willReturn($config);

    $node_definition = $this->createMock('Drupal\Core\Entity\ContentEntityType');
    $node_type_definition = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityType');

    $this->entityTypeManager->expects($this->once())
      ->method('getDefinitions')
      ->willReturn([$node_definition, $node_type_definition]);

    $form_state = $this->createMock('Drupal\Core\Form\FormStateInterface');
    $form = $this->form->buildForm([], $form_state);

    $this->assertIsArray($form);
    $this->assertArrayHasKey('actions', $form);
    $this->assertArrayHasKey('#theme', $form);
    $this->assertArrayHasKey('tracking_scope', $form);
    $this->assertArrayHasKey('retention', $form);
    $this->assertArrayHasKey('miscellaneous', $form);
    $this->assertArrayHasKey('entity', $form);
  }

  /**
   * Test the submitForm method.
   *
   * @covers ::submitForm
   * @covers ::getEditableConfigNames
   */
  public function testSubmitForm() {
    $form = [];
    $form_state = $this->createMock('Drupal\Core\Form\FormStateInterface');
    $form_state->expects($this->once())
      ->method('getValues')
      ->willReturn([
        'theme' => 'theme',
        'items_per_page' => 'items_per_page',
        'flush_log_timer' => 'flush_log_timer',
        'bot_retention_log' => 'bot_retention_log',
        'visitors_trackuserid' => 'visitors_trackuserid',
        'counter_enabled' => 'counter_enabled',
        'visitors_privacy_disablecookies' => 'visitors_privacy_disablecookies',
        'visitors_disable_tracking' => 'visitors_disable_tracking',
        'visitors_visibility_request_path_mode' => 'visitors_visibility_request_path_mode',
        'visitors_visibility_request_path_pages' => 'visitors_visibility_request_path_pages',
        'visitors_visibility_user_account_mode' => 'visitors_visibility_user_account_mode',
        'visitors_visibility_user_role_mode' => 'visitors_visibility_user_role_mode',
        'visitors_visibility_user_role_roles' => ['visitors_visibility_user_role_roles'],
        'visibility_exclude_user1' => 'visibility_exclude_user1',
      ]);

    $config = $this->createMock('Drupal\Core\Config\Config');
    $this->configFactory->expects($this->once())
      ->method('getEditable')
      ->with('visitors.settings')
      ->willReturn($config);

    $config->method('set')
      ->willReturnSelf();
    $config->expects($this->once())
      ->method('save');

    $this->form->submitForm($form, $form_state);
  }

}
