<?php

namespace Drupal\Tests\visitors\Unit;

use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerBuilder;

require_once __DIR__ . '/../../../visitors.install';

/**
 * Tests visitors_update_30000().
 *
 * @group visitors
 */
class HookUpdate30000Test extends UnitTestCase {

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

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

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

  /**
   * The messenger service.
   *
   * @var \Drupal\Core\Messenger\MessengerInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $messenger;

  /**
   * The module handler.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $moduleHandler;

  /**
   * The module installer.
   *
   * @var \Drupal\Core\Extension\ModuleInstallerInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $moduleInstaller;

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $database;

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

    $container = new ContainerBuilder();

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

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

    $this->moduleHandler = $this->createMock('Drupal\Core\Extension\ModuleHandlerInterface');
    $container->set('module_handler', $this->moduleHandler);

    $this->moduleInstaller = $this->createMock('Drupal\Core\Extension\ModuleInstallerInterface');
    $container->set('module_installer', $this->moduleInstaller);

    $this->database = $this->createMock('Drupal\Core\Database\Connection');
    $container->set('database', $this->database);

    \Drupal::setContainer($container);

    $this->settings = $this->createMock('Drupal\Core\Config\Config');
    $this->config = $this->createMock('Drupal\Core\Config\Config');
  }

  /**
   * Tests visitors_update_30000().
   */
  public function testStepZero(): void {
    $this->configFactory->expects($this->exactly(2))
      ->method('getEditable')
      ->willReturnMap([
        ['visitors.settings', $this->settings],
        ['visitors.config', $this->config],
      ]);

    $this->config->expects($this->once())
      ->method('getRawData')
      ->willReturn([]);
    $this->config->expects($this->once())
      ->method('delete');

    $this->settings->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $this->settings->expects($this->once())
      ->method('save');

    $sandbox = [];
    visitors_update_30000($sandbox);

    $this->assertEquals(1, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(0.25, $sandbox['#finished']);
  }

  /**
   * Tests visitors_update_30000().
   */
  public function testStepOne(): void {
    // @todo replace visitors_schema() with static before an initial release.
    $visitors_schema = visitors_schema();
    $schema = $this->createMock('Drupal\Core\Database\Schema');
    $this->database->expects($this->once())
      ->method('schema')
      ->willReturn($schema);
    $schema->expects($this->exactly(2))
      ->method('createTable')
      ->willReturnMap([
        ['visitors_visit', $visitors_schema['visitors_visit']],
        ['visitors_event', $visitors_schema['visitors_event']],
      ]);

    $sandbox = [
      'current' => 1,
      'max' => 4,
    ];
    visitors_update_30000($sandbox);

    $this->assertEquals(2, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(0.5, $sandbox['#finished']);
  }

  /**
   * Tests visitors_update_30000() step 2 - configuration installation.
   */
  public function testStepTwo(): void {
    // Mock configuration files for step 2.
    $view_config = $this->createMock('Drupal\Core\Config\Config');
    $spam_config = $this->createMock('Drupal\Core\Config\Config');
    $ai_assistants_config = $this->createMock('Drupal\Core\Config\Config');
    $search_engine_config = $this->createMock('Drupal\Core\Config\Config');
    $social_networks_config = $this->createMock('Drupal\Core\Config\Config');
    $geoip_view = $this->createMock('Drupal\Core\Config\Config');

    // Set up expectations for all config objects.
    $view_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $view_config->expects($this->once())
      ->method('save');

    $spam_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $spam_config->expects($this->once())
      ->method('save');

    $ai_assistants_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $ai_assistants_config->expects($this->once())
      ->method('save');

    $search_engine_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $search_engine_config->expects($this->once())
      ->method('save');

    $social_networks_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $social_networks_config->expects($this->once())
      ->method('save');

    $geoip_view->expects($this->once())
      ->method('delete');

    $this->configFactory->expects($this->exactly(6))
      ->method('getEditable')
      ->willReturnMap([
        ['views.view.visitors', $view_config],
        ['visitors.spam', $spam_config],
        ['visitors.ai_assistants', $ai_assistants_config],
        ['visitors.search_engine', $search_engine_config],
        ['visitors.social_networks', $social_networks_config],
        ['views.view.visitors_geoip', $geoip_view],
      ]);

    $sandbox = [
      'current' => 2,
      'max' => 4,
    ];
    visitors_update_30000($sandbox);

    $this->assertEquals(3, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(0.75, $sandbox['#finished']);
  }

  /**
   * Tests visitors_update_30000() step 2 when geoip view doesn't exist.
   */
  public function testStepTwoNoGeoipView(): void {
    // Mock configuration files for step 2.
    $view_config = $this->createMock('Drupal\Core\Config\Config');
    $spam_config = $this->createMock('Drupal\Core\Config\Config');
    $ai_assistants_config = $this->createMock('Drupal\Core\Config\Config');
    $search_engine_config = $this->createMock('Drupal\Core\Config\Config');
    $social_networks_config = $this->createMock('Drupal\Core\Config\Config');

    // Set up expectations for all config objects.
    $view_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $view_config->expects($this->once())
      ->method('save');

    $spam_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $spam_config->expects($this->once())
      ->method('save');

    $ai_assistants_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $ai_assistants_config->expects($this->once())
      ->method('save');

    $search_engine_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $search_engine_config->expects($this->once())
      ->method('save');

    $social_networks_config->expects($this->once())
      ->method('setData')
      ->willReturnSelf();
    $social_networks_config->expects($this->once())
      ->method('save');

    $this->configFactory->expects($this->exactly(6))
      ->method('getEditable')
      ->willReturnMap([
        ['views.view.visitors', $view_config],
        ['visitors.spam', $spam_config],
        ['visitors.ai_assistants', $ai_assistants_config],
        ['visitors.search_engine', $search_engine_config],
        ['visitors.social_networks', $social_networks_config],
      ]);

    $sandbox = [
      'current' => 2,
      'max' => 4,
    ];
    visitors_update_30000($sandbox);

    $this->assertEquals(3, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(0.75, $sandbox['#finished']);
  }

  /**
   * Tests visitors_update_30000() step 3 - module uninstallation.
   */
  public function testStepThree(): void {
    $this->moduleHandler->expects($this->once())
      ->method('moduleExists')
      ->with('visitors_geoip')
      ->willReturn(TRUE);

    $this->moduleInstaller->expects($this->once())
      ->method('uninstall')
      ->with(['visitors_geoip']);

    $sandbox = [
      'current' => 3,
      'max' => 4,
    ];
    visitors_update_30000($sandbox);

    $this->assertEquals(4, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(1, $sandbox['#finished']);
  }

  /**
   * Tests visitors_update_30000() step 3 when geoip module is not installed.
   */
  public function testStepThreeNoGeoipModule(): void {
    $this->moduleHandler->expects($this->once())
      ->method('moduleExists')
      ->with('visitors_geoip')
      ->willReturn(FALSE);

    $this->moduleInstaller->expects($this->never())
      ->method('uninstall');

    $sandbox = [
      'current' => 3,
      'max' => 4,
    ];
    visitors_update_30000($sandbox);

    $this->assertEquals(4, $sandbox['current']);
    $this->assertEquals(4, $sandbox['max']);
    $this->assertEquals(1, $sandbox['#finished']);
  }

}
