<?php

declare(strict_types=1);

namespace Drupal\Tests\visitors\Unit\Service;

use Drupal\Tests\UnitTestCase;
use Drupal\visitors\Service\ReportService;
use Drupal\visitors\VisitorsReportInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\InputBag;

/**
 * Tests the ReportService class.
 *
 * @coversDefaultClass \Drupal\visitors\Service\ReportService
 *
 * @group visitors
 */
class ReportServiceTest extends UnitTestCase {

  /**
   * The report service.
   *
   * @var \Drupal\visitors\Service\ReportService
   */
  protected $service;

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

  /**
   * 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;

  /**
   * The date range service.
   *
   * @var \Drupal\visitors\VisitorsDateRangeInterface|\PHPUnit\Framework\MockObject\MockObject
   */
  protected $dateRange;

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

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

    $container = new ContainerBuilder();

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

    $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);

    $this->dateRange = $this->createMock('\Drupal\visitors\VisitorsDateRangeInterface');
    $container->set('visitors.date_range', $this->dateRange);

    \Drupal::setContainer($container);

    $this->settings = $this->createMock('\Drupal\Core\Config\ImmutableConfig');
    $this->settings->expects($this->any())
      ->method('get')
      ->with('items_per_page')
      ->willReturn(10);

    $this->configFactory->expects($this->any())
      ->method('get')
      ->willReturnMap([
        ['visitors.settings', $this->settings],
      ]);

    $request = $this->createMock('\Symfony\Component\HttpFoundation\Request');
    $this->requestStack->expects($this->any())
      ->method('getCurrentRequest')
      ->willReturn($request);
    $query = new InputBag();

    $request->query = $query;

    $this->service = new ReportService(
      $this->database,
      $this->configFactory,
      $this->requestStack,
      $this->dateRange,
    );
  }

  /**
   * Tests the constructor.
   *
   * @covers ::__construct
   */
  public function testConstructor(): void {
    $service = new ReportService(
      $this->database,
      $this->configFactory,
      $this->requestStack,
      $this->dateRange,
    );
    $this->assertInstanceOf(ReportService::class, $service);
  }

  /**
   * Tests the addDateFilter method.
   *
   * @covers ::addDateFilter
   */
  public function testAddDateFilter(): void {
    $this->dateRange->expects($this->once())
      ->method('getStartTimestamp')
      ->willReturn(100);
    $this->dateRange->expects($this->once())
      ->method('getEndTimestamp')
      ->willReturn(200);

    $query = $this->createMock('\Drupal\Core\Database\Query\SelectInterface');
    $query->expects($this->once())
      ->method('condition')
      ->with('created', [100, 200], 'BETWEEN')
      ->willReturnSelf();

    // Make addDateFilter method public.
    $method = new \ReflectionMethod($this->service, 'addDateFilter');
    $method->setAccessible(TRUE);
    $method->invokeArgs($this->service, [&$query]);
  }

  /**
   * Tests the setReferrersCondition method with no date range.
   *
   * @covers ::setReferrersCondition
   */
  public function testSetReferrersConditionInternal(): void {
    $_SERVER['HTTP_HOST'] = 'example.com';
    $_SESSION['referer_type'] = VisitorsReportInterface::REFERER_TYPE_INTERNAL_PAGES;
    $query = $this->createMock('\Drupal\Core\Database\Query\SelectInterface');
    $query->expects($this->exactly(2))
      ->method('condition')
      ->willReturnMap([
        ['referrer_url', '%example.com%', 'LIKE'],
        ['referrer_url', '', '<>'],
      ]);

    // Make addDateFilter method public.
    $method = new \ReflectionMethod($this->service, 'setReferrersCondition');
    $method->setAccessible(TRUE);
    $method->invoke($this->service, $query);
  }

  /**
   * Tests the setReferrersCondition method with no date range.
   *
   * @covers ::setReferrersCondition
   */
  public function testSetReferrersConditionExternal(): void {
    $_SERVER['HTTP_HOST'] = 'example.com';
    $_SESSION['referer_type'] = VisitorsReportInterface::REFERER_TYPE_EXTERNAL_PAGES;
    $query = $this->createMock('\Drupal\Core\Database\Query\SelectInterface');
    $query->expects($this->once())
      ->method('condition')
      ->willReturnMap([
        ['referrer_url', '%example.com%', 'NOT LIKE'],
      ]);

    // Make addDateFilter method public.
    $method = new \ReflectionMethod($this->service, 'setReferrersCondition');
    $method->setAccessible(TRUE);
    $method->invoke($this->service, $query);
  }

  /**
   * Tests the referer method.
   *
   * @covers ::referer
   */
  public function testReferer(): void {
    $_SERVER['HTTP_HOST'] = 'example.com';
    $_SESSION['referer_type'] = VisitorsReportInterface::REFERER_TYPE_EXTERNAL_PAGES;
    $select = $this->createMock('\Drupal\Core\Database\Query\TableSortExtender');
    $select->expects($this->exactly(2))
      ->method('extend')
      ->willReturnSelf();
    $this->database->expects($this->exactly(2))
      ->method('select')
      ->with('visitors_event', 'v')
      ->willReturn($select);

    $select->expects($this->once())
      ->method('execute')
      ->willReturn([
        (object) ['referrer_url' => 'http://example.com', 'count' => 1],
      ]);

    $header = [
      'referrer_url' => [
        'data'      => 'Referer',
        'field'     => 'referrer_url',
        'specifier' => 'referrer_url',
      ],
      'count' => [
        'data'      => 'Count',
        'field'     => 'count',
        'specifier' => 'count',
        'sort'      => 'desc',
      ],
    ];

    $this->service->referer($header);

  }

}
