<?php

namespace Drupal\Tests\visitors\Unit\Service;

use Drupal\Tests\UnitTestCase;
use Drupal\visitors\Service\TrackerService;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * Tests the CookieService class.
 *
 * @coversDefaultClass \Drupal\visitors\Service\TrackerService
 * @uses \Drupal\visitors\Service\TrackerService
 * @group visitors
 */
class TrackerServiceTest extends UnitTestCase {

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

  /**
   * The tracker service.
   *
   * @var \Drupal\visitors\Service\TrackerService
   */
  protected $service;

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

    $container = new ContainerBuilder();

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

    $this->service = new TrackerService($this->database);
  }

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

  /**
   * Tests the getVisitId method.
   *
   * @covers ::writeLog
   */
  public function testWriteLog() {
    $fields = [];

    $statement = $this->createMock('Drupal\Core\Database\StatementInterface');
    $statement->expects($this->once())
      ->method('execute')
      ->willReturn(1);

    $insert = $this->createMock('Drupal\Core\Database\Query\Insert');
    $insert->expects($this->once())
      ->method('fields')
      ->with($fields)
      ->willReturn($statement);

    $this->database->expects($this->once())
      ->method('insert')
      ->with('visitors_event')
      ->willReturn($insert);

    $id = $this->service->writeLog($fields);

    $this->assertIsInt($id);
    $this->assertEquals(1, $id);
  }

  /**
   * Tests the getVisitId method.
   *
   * @covers ::updateVisit
   */
  public function testUpdateVisit() {

    $visit_id = 1;
    $log_id = 1;
    $exit_time = 1;

    $update = $this->createMock('Drupal\Core\Database\Query\Update');
    $update->expects($this->once())
      ->method('fields')
      ->with([
        'exit_time' => $exit_time,
        'exit' => $log_id,
      ])
      ->willReturnSelf();
    $update->expects($this->exactly(4))
      ->method('expression')
      ->willReturnSelf();
    $update->expects($this->once())
      ->method('condition')
      ->willReturnSelf();
    $update->expects($this->once())
      ->method('execute');

    $this->database->expects($this->once())
      ->method('update')
      ->with('visitors_visit')
      ->willReturn($update);

    $this->service->updateVisit($visit_id, $log_id, $exit_time, NULL);
  }

  /**
   * Tests the getVisitId method.
   *
   * @covers ::getVisitId
   */
  public function testGetVisitIdExistingSession() {
    $statement = $this->createMock('Drupal\Core\Database\StatementInterface');
    $statement->expects($this->once())
      ->method('fetchField')
      ->willReturn(10);
    $select = $this->createMock('Drupal\Core\Database\Query\Select');
    $select->expects($this->once())
      ->method('fields')
      ->willReturnSelf();
    $select->expects($this->exactly(2))
      ->method('condition')
      ->willReturnSelf();
    $select->expects($this->once())
      ->method('execute')
      ->willReturn($statement);

    $this->database->expects($this->once())
      ->method('select')
      ->willReturn($select);
    $fields = [
      'visitor_id' => 1234567890,
      'location_browser_lang' => 'en',
      'config_os' => 'WIN',
      'config_browser_name' => 'CH',
      'config_browser_version' => '1.0',
      'config_windowsmedia' => 0,
      'config_quicktime' => 0,
      'config_realplayer' => 0,
      'config_pdf' => 0,
      'config_flash' => 0,
      'config_java' => 0,
      'config_silverlight' => 0,
      'config_cookie' => 0,
      'config_resolution' => '1920x1080',
      'bot' => 0,
    ];

    $id = $this->service->getVisitId($fields, 1234567890);

    $this->assertIsInt($id);
    $this->assertEquals(10, $id);
  }

  /**
   * Tests the getVisitId method.
   *
   * @covers ::getVisitId
   * @covers ::generateConfigId
   * @covers ::getCurrentSession
   */
  public function testGetVisitIdNew() {
    $statement = $this->createMock('Drupal\Core\Database\StatementInterface');
    $statement->expects($this->exactly(2))
      ->method('fetchField')
      ->willReturn(FALSE);
    $select = $this->createMock('Drupal\Core\Database\Query\Select');
    $select->expects($this->exactly(2))
      ->method('fields')
      ->willReturnSelf();
    $select->expects($this->exactly(3))
      ->method('condition')
      ->willReturnSelf();
    $select->expects($this->once())
      ->method('orderBy')
      ->willReturnSelf();
    $select->expects($this->once())
      ->method('range')
      ->willReturnSelf();

    $select->expects($this->exactly(2))
      ->method('execute')
      ->willReturn($statement);

    $this->database->expects($this->exactly(2))
      ->method('select')
      ->willReturn($select);
    $fields = [
      'visitor_id' => 1234567890,
      'location_browser_lang' => 'en',
      'config_os' => 'WIN',
      'config_browser_name' => 'CH',
      'config_browser_version' => '1.0',
      'config_windowsmedia' => 0,
      'config_quicktime' => 0,
      'config_realplayer' => 0,
      'config_pdf' => 0,
      'config_flash' => 0,
      'config_java' => 0,
      'config_silverlight' => 0,
      'config_cookie' => 0,
      'config_resolution' => '1920x1080',
      'bot' => 0,
    ];

    $insert = $this->createMock('Drupal\Core\Database\Query\Insert');
    $insert->expects($this->once())
      ->method('fields')
      ->willReturnSelf();
    $insert->expects($this->once())
      ->method('execute')
      ->willReturn(10);

    $this->database->expects($this->once())
      ->method('insert')
      ->willReturn($insert);

    $id = $this->service->getVisitId($fields, 1234567890);

    $this->assertIsInt($id);
    $this->assertEquals(10, $id);
  }

  /**
   * Tests the getVisitId method.
   *
   * @covers ::getVisitId
   * @covers ::generateConfigId
   * @covers ::doReturningVisit
   */
  public function testGetVisitIdReturning() {
    $statement_a = $this->createMock('Drupal\Core\Database\StatementInterface');
    $statement_a->expects($this->once())
      ->method('fetchField')
      ->willReturn(FALSE);

    $statement_b = $this->createMock('Drupal\Core\Database\StatementInterface');
    $statement_b->expects($this->once())
      ->method('fetchField')
      ->willReturn(9);

    $select_a = $this->createMock('Drupal\Core\Database\Query\Select');
    $select_a->expects($this->once())
      ->method('fields')
      ->willReturnSelf();
    $select_a->expects($this->exactly(2))
      ->method('condition')
      ->willReturnSelf();
    $select_a->expects($this->once())
      ->method('execute')
      ->willReturn($statement_a);

    $select_b = $this->createMock('Drupal\Core\Database\Query\Select');
    $select_b->expects($this->once())
      ->method('fields')
      ->willReturnSelf();
    $select_b->expects($this->once())
      ->method('condition')
      ->willReturnSelf();
    $select_b->expects($this->once())
      ->method('orderBy')
      ->willReturnSelf();
    $select_b->expects($this->once())
      ->method('range')
      ->willReturnSelf();
    $select_b->expects($this->once())
      ->method('execute')
      ->willReturn($statement_b);

    $selects = [
      $select_a,
      $select_b,
    ];

    $this->database->expects($this->exactly(2))
      ->method('select')
      ->willReturnCallback(function (string $table, string $alias) use ($selects) {
        switch ($alias) {
          case 'vv2':
            return $selects[1];

          case 'vv1':
          default:
            return $selects[0];
        }
      });
    $fields = [
      'visitor_id' => 1234567890,
      'location_browser_lang' => 'en',
      'config_os' => 'WIN',
      'config_browser_name' => 'CH',
      'config_browser_version' => '1.0',
      'config_windowsmedia' => 0,
      'config_quicktime' => 0,
      'config_realplayer' => 0,
      'config_pdf' => 0,
      'config_flash' => 0,
      'config_java' => 0,
      'config_silverlight' => 0,
      'config_cookie' => 0,
      'config_resolution' => '1920x1080',
      'bot' => 0,
    ];

    $insert = $this->createMock('Drupal\Core\Database\Query\Insert');
    $insert->expects($this->once())
      ->method('fields')
      ->willReturnSelf();
    $insert->expects($this->once())
      ->method('execute')
      ->willReturn(10);

    $this->database->expects($this->once())
      ->method('insert')
      ->willReturn($insert);

    $id = $this->service->getVisitId($fields, 1234567890);

    $this->assertIsInt($id);
    $this->assertEquals(10, $id);
  }

  /**
   * Tests the getVisitId method.
   *
   * @covers ::getVisitId
   * @covers ::generateConfigId
   * @covers ::doReturningVisit
   * @covers ::getCurrentSession
   * @covers ::getCurrentSessionByConfigId
   */
  public function testGetVisitIdReturningVisitorIdMissing() {
    $statement = $this->createMock('Drupal\Core\Database\StatementInterface');
    $statement->expects($this->once())
      ->method('fetchField')
      ->willReturn(10);

    $select = $this->createMock('Drupal\Core\Database\Query\Select');
    $select->expects($this->once())
      ->method('fields')
      ->willReturnSelf();
    $select->expects($this->exactly(3))
      ->method('condition')
      ->willReturnSelf();
    $select->expects($this->once())
      ->method('execute')
      ->willReturn($statement);

    $this->database->expects($this->exactly(1))
      ->method('select')
      ->willReturn($select);
    $fields = [
      'location_browser_lang' => 'en',
      'config_os' => 'WIN',
      'config_browser_name' => 'CH',
      'config_browser_version' => '1.0',
      'config_windowsmedia' => 0,
      'config_quicktime' => 0,
      'config_realplayer' => 0,
      'config_pdf' => 0,
      'config_flash' => 0,
      'config_java' => 0,
      'config_silverlight' => 0,
      'config_cookie' => 0,
      'config_resolution' => '1920x1080',
      'bot' => 0,
      'location_ip' => '127.0.0.1',
    ];

    $id = $this->service->getVisitId($fields, 1234567890);

    $this->assertIsInt($id);
    $this->assertEquals(10, $id);
  }

  /**
   * Tests the getVisitId method.
   *
   * @covers ::getCurrentSessionByConfigId
   */
  public function testGetCurrentSessionByConfigId(): void {

    $this->database->expects($this->never())
      ->method('select');

    $fields = [];
    $ago = 1;
    // Make getCurrentSessionByConfigId accessible.
    $reflection = new \ReflectionClass($this->service);
    $method = $reflection->getMethod('getCurrentSessionByConfigId');
    $method->setAccessible(TRUE);

    $method->invokeArgs($this->service, [$fields, $ago]);
  }

  /**
   * Tests the doReturningVisit method.
   *
   * @covers ::doReturningVisit
   */
  public function testDoReturningVisit(): void {

    $this->database->expects($this->never())
      ->method('select');

    $fields = [];
    $ago = 1;
    // Make getCurrentSessionByConfigId accessible.
    $reflection = new \ReflectionClass($this->service);
    $method = $reflection->getMethod('doReturningVisit');
    $method->setAccessible(TRUE);

    $method->invokeArgs($this->service, [&$fields]);
  }

}
