<?php

declare(strict_types=1);

namespace Drupal\Tests\visitors\Kernel\Plugin\views\field;

use Drupal\KernelTests\KernelTestBase;
use Drupal\Core\Database\Connection;
use Drupal\visitors\Plugin\views\field\RefererType;
use Drupal\views\ResultRow;

/**
 * Kernel tests for RefererType views field plugin.
 *
 * @group visitors
 * @coversDefaultClass \Drupal\visitors\Plugin\views\field\RefererType
 */
class RefererTypeTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'system',
    'user',
    'views',
    'visitors',
  ];

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

  /**
   * The field plugin instance.
   *
   * @var \Drupal\visitors\Plugin\views\field\RefererType
   */
  protected RefererType $field;

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

    $this->installEntitySchema('user');
    $this->installSchema('system', ['sequences']);
    $this->installSchema('visitors', ['visitors_visit', 'visitors_event']);
    $this->installConfig(['user', 'system', 'views']);

    $this->database = $this->container->get('database');

    // Insert test data.
    $this->insertTestData();

    // Create the field plugin instance.
    $configuration = [];
    $plugin_id = 'visitors_referer_type';
    $plugin_definition = [];
    $this->field = RefererType::create($this->container, $configuration, $plugin_id, $plugin_definition);
  }

  /**
   * Inserts test data into the visitors_visit table.
   */
  protected function insertTestData(): void {
    $test_data = [
      [
        'id' => 1,
        'visitor_id' => 'visitor1',
        'uid' => 0,
        'entry_time' => time(),
        'exit_time' => time() + 300,
        'location_ip' => '127.0.0.1',
        'config_id' => 'config1',
        'referer_type' => 'direct',
        'referer_url' => '',
        'referer_name' => NULL,
        'referer_keyword' => NULL,
      ],
      [
        'id' => 2,
        'visitor_id' => 'visitor2',
        'uid' => 0,
        'entry_time' => time() - 3600,
        'exit_time' => time() - 3300,
        'location_ip' => '127.0.0.2',
        'config_id' => 'config2',
        'referer_type' => 'website',
        'referer_url' => 'http://google.com/search?q=test',
        'referer_name' => 'Google',
        'referer_keyword' => 'test',
      ],
      [
        'id' => 3,
        'visitor_id' => 'visitor3',
        'uid' => 1,
        'entry_time' => time() - 7200,
        'exit_time' => time() - 6900,
        'location_ip' => '127.0.0.3',
        'config_id' => 'config3',
        'referer_type' => 'internal',
        'referer_url' => 'http://example.com/other',
        'referer_name' => 'Internal',
        'referer_keyword' => NULL,
      ],
      [
        'id' => 4,
        'visitor_id' => 'visitor4',
        'uid' => 1,
        'entry_time' => time() - 7200,
        'exit_time' => time() - 6900,
        'location_ip' => '127.0.0.4',
        'config_id' => 'config4',
        'referer_type' => 'search_engine',
        'referer_url' => 'https://google.com/search?q=test',
        'referer_name' => 'Google',
        'referer_keyword' => 'test',
      ],
      [
        'id' => 5,
        'visitor_id' => 'visitor5',
        'uid' => 1,
        'entry_time' => time() - 7200,
        'exit_time' => time() - 6900,
        'location_ip' => '127.0.0.5',
        'config_id' => 'config5',
        'referer_type' => 'spam',
        'referer_url' => 'https://spam.com/search?q=test',
        'referer_name' => 'Spam',
        'referer_keyword' => 'test',
      ],
    ];

    foreach ($test_data as $data) {
      $this->database->insert('visitors_visit')->fields($data)->execute();
    }
  }

  /**
   * Tests plugin creation and basic functionality.
   *
   * @covers ::create
   */
  public function testPluginCreation(): void {
    $this->assertInstanceOf(RefererType::class, $this->field);
  }

  /**
   * Tests the field plugin with different referer type values.
   *
   * @covers ::render
   */
  public function testRefererTypeFieldRendering(): void {
    // Set up field alias for getValue() method to work.
    $this->field->field_alias = 'referer_type';

    // Test direct referer type.
    $direct_row = new ResultRow(['referer_type' => 'direct']);
    $result = $this->field->render($direct_row);
    $this->assertEquals('Direct Entry', (string) $result);

    // Test website referer type.
    $website_row = new ResultRow(['referer_type' => 'website']);
    $result = $this->field->render($website_row);
    $this->assertEquals('Website', (string) $result);

    // Test internal referer type.
    $internal_row = new ResultRow(['referer_type' => 'internal']);
    $result = $this->field->render($internal_row);
    $this->assertEquals('Internal', (string) $result);

    // Test search engine referer type.
    $search_engine_row = new ResultRow(['referer_type' => 'search_engine']);
    $result = $this->field->render($search_engine_row);
    $this->assertEquals('Search Engine', (string) $result);

    // Test spam referer type.
    $spam_row = new ResultRow(['referer_type' => 'spam']);
    $result = $this->field->render($spam_row);
    $this->assertEquals('Spam', (string) $result);

    // Test unknown referer type.
    $unknown_row = new ResultRow(['referer_type' => 'unknown']);
    $result = $this->field->render($unknown_row);
    $this->assertEquals('Unknown', (string) $result);
  }

  /**
   * Tests field plugin integration with Views plugin manager.
   *
   * @covers ::create
   */
  public function testViewsPluginManagerIntegration(): void {
    // Verify the field plugin is available.
    $field_plugins = \Drupal::service('plugin.manager.views.field')->getDefinitions();
    $this->assertArrayHasKey('visitors_referer_type', $field_plugins);

    // Verify plugin definition.
    $plugin_definition = $field_plugins['visitors_referer_type'];
    $this->assertEquals('Drupal\visitors\Plugin\views\field\RefererType', $plugin_definition['class']);
    $this->assertEquals('visitors_referer_type', $plugin_definition['id']);

    // Test creating plugin through plugin manager.
    $plugin_manager = \Drupal::service('plugin.manager.views.field');
    $plugin = $plugin_manager->createInstance('visitors_referer_type');
    $this->assertInstanceOf(RefererType::class, $plugin);
  }

  /**
   * Tests database integration by querying actual data.
   *
   * @covers ::render
   */
  public function testDatabaseIntegration(): void {
    // Query the database directly to test our test data.
    $query = $this->database->select('visitors_visit', 'v')
      ->fields('v', ['referer_type'])
      ->orderBy('id');
    $results = $query->execute()->fetchCol();

    // Verify our test data was inserted correctly.
    $this->assertCount(5, $results);
    $this->assertEquals([
      'direct',
      'website',
      'internal',
      'search_engine',
      'spam',
    ], $results);

    // Test rendering with actual database data.
    $this->field->field_alias = 'referer_type';

    foreach ($results as $referer_type) {
      $row = new ResultRow(['referer_type' => $referer_type]);
      $result = $this->field->render($row);

      switch ($referer_type) {
        case 'direct':
          $this->assertEquals('Direct Entry', (string) $result);
          break;

        case 'website':
          $this->assertEquals('Website', (string) $result);
          break;

        case 'internal':
          $this->assertEquals('Internal', (string) $result);
          break;

        case 'search_engine':
          $this->assertEquals('Search Engine', (string) $result);
          break;

        case 'spam':
          $this->assertEquals('Spam', (string) $result);
          break;
      }
    }
  }

  /**
   * Tests field plugin with empty database.
   */
  public function testEmptyDatabase(): void {
    // Clear all test data.
    $this->database->delete('visitors_visit')->execute();

    // Verify database is empty.
    $count = $this->database->select('visitors_visit')->countQuery()->execute()->fetchField();
    $this->assertEquals(0, $count);

    // The field plugin should still work with mock data even if DB is empty.
    $this->field->field_alias = 'referer_type';
    $mock_row = new ResultRow(['referer_type' => 'direct']);
    $result = $this->field->render($mock_row);
    $this->assertEquals('Direct Entry', (string) $result);
  }

}
