<?php

namespace Drupal\Tests\visitors\Kernel\Controller;

use Drupal\KernelTests\KernelTestBase;
use Drupal\visitors\Controller\VisitorsController;

use Symfony\Component\HttpFoundation\Request;

/**
 * Kernel test for VisitorsController.
 *
 * @group visitors
 * @coversDefaultClass \Drupal\visitors\Controller\VisitorsController
 */
class VisitorsControllerTest extends KernelTestBase {

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

  /**
   * The controller under test.
   *
   * @var \Drupal\visitors\Controller\VisitorsController
   */
  protected VisitorsController $controller;

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

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

    // Create the controller using the container to use real services.
    $this->controller = VisitorsController::create($this->container);
  }

  /**
   * Tests basic tracking functionality.
   *
   * @covers ::track
   */
  public function testBasicTracking(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://google.com/search?q=test',
      'uid' => '1',
      'action_name' => 'Test Page',
      'pv_id' => '789',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
    $this->assertEquals('must-revalidate, no-cache, no-store, private', $response->headers->get('Cache-Control'));
    $this->assertEquals('no-cache', $response->headers->get('Pragma'));
    $this->assertEquals('0', $response->headers->get('Expires'));
  }

  /**
   * Tests tracking with image response.
   *
   * @covers ::track
   * @covers ::getResponse
   * @covers ::getImageContent
   */
  public function testTrackingWithImage(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'send_image' => '1',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(200, $response->getStatusCode());
    $this->assertEquals('image/gif', $response->headers->get('Content-Type'));

    // Test the exact image content returned by getImageContent method.
    $expectedImageContent = hex2bin('47494638396101000100800000000000FFFFFF21F9040100000000002C00000000010001000002024401003B');
    $this->assertEquals($expectedImageContent, $response->getContent());
    $this->assertEquals(strlen($expectedImageContent), $response->headers->get('Content-Length'));

    // Verify it's a valid GIF image (starts with GIF89a or GIF87a)
    $content = $response->getContent();
    $this->assertStringStartsWith('GIF', $content);

    // Verify all response headers are properly set for image.
    $this->assertEquals('must-revalidate, no-cache, no-store, private', $response->headers->get('Cache-Control'));
    $this->assertEquals('no-cache', $response->headers->get('Pragma'));
    $this->assertEquals('0', $response->headers->get('Expires'));
  }

  /**
   * Tests the getImageContent method directly.
   *
   * @covers ::getImageContent
   */
  public function testGetImageContent(): void {
    // Use reflection to access the protected method.
    $reflection = new \ReflectionClass($this->controller);
    $method = $reflection->getMethod('getImageContent');
    $method->setAccessible(TRUE);

    $imageContent = $method->invoke($this->controller);

    // Test the exact hex-encoded GIF content.
    $expectedImageContent = hex2bin('47494638396101000100800000000000FFFFFF21F9040100000000002C00000000010001000002024401003B');
    $this->assertEquals($expectedImageContent, $imageContent);

    // Verify it's a valid GIF image.
    $this->assertStringStartsWith('GIF', $imageContent);

    // Verify the content length matches the expected GIF.
    $this->assertEquals(44, strlen($imageContent));

    // Verify it's a 1x1 transparent GIF (common tracking pixel)
    $this->assertStringContainsString('GIF89a', $imageContent);
  }

  /**
   * Tests tracking without image response (default behavior).
   *
   * @covers ::track
   */
  public function testTrackingWithoutImage(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      // send_image not set.
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
    $this->assertEmpty($response->getContent());
    $this->assertNull($response->headers->get('Content-Type'));
    $this->assertNull($response->headers->get('Content-Length'));
  }

  /**
   * Tests tracking with explicit send_image=false.
   *
   * @covers ::track
   */
  public function testTrackingWithSendImageFalse(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'send_image' => '0',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
    $this->assertEmpty($response->getContent());
    $this->assertNull($response->headers->get('Content-Type'));
    $this->assertNull($response->headers->get('Content-Length'));
  }

  /**
   * Tests tracking with search engine referrer.
   *
   * @covers ::track
   */
  public function testTrackingWithSearchEngineReferrer(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://google.com/search?q=test',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with internal referrer.
   *
   * @covers ::track
   */
  public function testTrackingWithInternalReferrer(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page2',
      'urlref' => 'https://example.com/page1',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with direct traffic.
   *
   * @covers ::track
   */
  public function testTrackingWithDirectTraffic(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      // No urlref = direct traffic.
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with custom variables.
   *
   * @covers ::track
   */
  public function testTrackingWithCustomVariables(): void {
    $customData = json_encode([
      ['path', '/test-path'],
      ['route', 'test.route'],
      ['server', 'test-server'],
    ]);

    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'cvar' => $customData,
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with event variables.
   *
   * @covers ::track
   */
  public function testTrackingWithEventVariables(): void {
    $eventData = json_encode([
      ['plugin', 'test-plugin'],
      ['event', 'test-event'],
      ['plugin_int_1', '123'],
      ['plugin_var_1', 'test-value'],
    ]);

    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'e_cvar' => $eventData,
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with performance data.
   *
   * @covers ::track
   */
  public function testTrackingWithPerformanceData(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'pf_net' => '100',
      'pf_srv' => '200',
      'pf_tfr' => '150',
      'pf_dm1' => '50',
      'pf_dm2' => '75',
      'pf_onl' => '25',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with local time data.
   *
   * @covers ::track
   */
  public function testTrackingWithLocalTime(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'h' => '14',
      'm' => '30',
      's' => '45',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with language data.
   *
   * @covers ::track
   */
  public function testTrackingWithLanguage(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
    ]);

    // Set specific languages for testing.
    $request->headers->set('Accept-Language', 'en-US,en;q=0.9,de;q=0.8');

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with configuration data.
   *
   * @covers ::track
   */
  public function testTrackingWithConfigurationData(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'res' => '1920x1080',
      'pdf' => '1',
      'fla' => '1',
      'java' => '1',
      'qt' => '1',
      'realp' => '1',
      'wma' => '1',
      'ag' => '1',
      'cookie' => '1',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with malformed URL.
   *
   * @covers ::track
   */
  public function testTrackingWithMalformedUrl(): void {
    $request = $this->createRequest([
      'url' => 'not-a-valid-url',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with missing required fields.
   *
   * @covers ::track
   */
  public function testTrackingWithMissingFields(): void {
    $request = $this->createRequest([]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with AI assistant referrer.
   *
   * @covers ::track
   */
  public function testTrackingWithAiAssistantReferrer(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://chatgpt.com/chat',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with another AI assistant referrer.
   *
   * @covers ::track
   */
  public function testTrackingWithAnotherAiAssistantReferrer(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://copilot.microsoft.com/chat',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with social network referrer.
   *
   * @covers ::track
   */
  public function testTrackingWithSocialNetworkReferrer(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://facebook.com/post/123',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with another social network referrer.
   *
   * @covers ::track
   */
  public function testTrackingWithAnotherSocialNetworkReferrer(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://twitter.com/user/status/456',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with AI assistant that doesn't match.
   *
   * @covers ::track
   */
  public function testTrackingWithAiAssistantNoMatch(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://unknown-ai.com/chat',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with social network that doesn't match.
   *
   * @covers ::track
   */
  public function testTrackingWithSocialNetworkNoMatch(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://unknown-social.com/post',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with campaign referrer using mtm_* parameters.
   *
   * @covers ::track
   */
  public function testTrackingWithCampaignMtmParameters(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://example.com/offer?mtm_campaign=Best-Seller&mtm_source=Newsletter_7&mtm_medium=email&mtm_keyword=discount&mtm_content=banner',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with campaign referrer using utm_* parameters.
   *
   * @covers ::track
   */
  public function testTrackingWithCampaignUtmParameters(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://example.com/offer?utm_campaign=Summer_Sale&utm_source=google&utm_medium=cpc&utm_term=summer+sale&utm_content=text_ad',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with campaign referrer that has no campaign data.
   *
   * @covers ::track
   */
  public function testTrackingWithCampaignNoData(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://example.com/offer?page=1&sort=date',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Tests tracking with campaign referrer priority over other services.
   *
   * @covers ::track
   */
  public function testTrackingWithCampaignPriority(): void {
    $request = $this->createRequest([
      'url' => 'https://example.com/page1',
      'urlref' => 'https://facebook.com/post/123?mtm_campaign=Social_Campaign&mtm_source=facebook&mtm_medium=social',
    ]);

    $response = $this->controller->track($request);

    $this->assertEquals(204, $response->getStatusCode());
  }

  /**
   * Helper method to create a request with query parameters.
   *
   * @param array $query
   *   The query parameters.
   *
   * @return \Symfony\Component\HttpFoundation\Request
   *   The created request.
   */
  protected function createRequest(array $query): Request {
    $request = Request::create('https://example.com/visitors/track');
    $request->query->add($query);
    $request->server->set('HTTP_USER_AGENT', 'Mozilla/5.0 (Test Browser)');
    $request->server->set('HTTP_ACCEPT_LANGUAGE', 'en-US,en;q=0.9');
    $request->server->set('REMOTE_ADDR', '127.0.0.1');

    return $request;
  }

}
