<?php

declare(strict_types=1);

namespace Drupal\Tests\visitors\Kernel\Service;

use Drupal\KernelTests\KernelTestBase;
use Drupal\visitors\Service\CookieService;
use Symfony\Component\HttpFoundation\Request;

/**
 * Kernel tests for the CookieService.
 *
 * @group visitors
 * @coversDefaultClass \Drupal\visitors\Service\CookieService
 */
final class CookieServiceTest extends KernelTestBase {

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

  /**
   * The cookie service.
   *
   * @var \Drupal\visitors\Service\CookieService
   */
  private CookieService $cookieService;

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

    $this->installConfig(['visitors']);
    $this->cookieService = $this->container->get('visitors.cookie');
  }

  /**
   * Tests the service is properly registered and injectable.
   *
   * @covers ::__construct
   */
  public function testServiceRegistration(): void {
    $this->assertInstanceOf(CookieService::class, $this->cookieService);
  }

  /**
   * Tests getting visitor ID from cookie with standard format.
   *
   * @covers ::getId
   */
  public function testGetIdWithStandardCookie(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_123', '123.456.789');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('123', $visitorId);
  }

  /**
   * Tests getting visitor ID from cookie with different prefix.
   *
   * @covers ::getId
   */
  public function testGetIdWithDifferentPrefix(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_abc', 'abc.def.ghi');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('abc', $visitorId);
  }

  /**
   * Tests getting visitor ID from cookie with multiple parts.
   *
   * @covers ::getId
   */
  public function testGetIdWithMultipleParts(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_999', '999.888.777.666.555');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('999', $visitorId);
  }

  /**
   * Tests getting visitor ID from cookie with single part.
   *
   * @covers ::getId
   */
  public function testGetIdWithSinglePart(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_single', 'single');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('single', $visitorId);
  }

  /**
   * Tests getting visitor ID from cookie with empty value.
   *
   * @covers ::getId
   */
  public function testGetIdWithEmptyCookie(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_empty', '');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('', $visitorId);
  }

  /**
   * Tests getting visitor ID when no _pk_id_ cookie exists.
   *
   * @covers ::getId
   */
  public function testGetIdWithNoPkIdCookie(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('other_cookie', 'value');
    $request->cookies->set('session_id', 'abc123');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertNull($visitorId);
  }

  /**
   * Tests getting visitor ID when no cookies exist.
   *
   * @covers ::getId
   */
  public function testGetIdWithNoCookies(): void {
    $request = Request::create('https://example.com');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertNull($visitorId);
  }

  /**
   * Tests getting visitor ID with multiple _pk_id_ cookies.
   *
   * @covers ::getId
   */
  public function testGetIdWithMultiplePkIdCookies(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_first', 'first.123');
    $request->cookies->set('_pk_id_second', 'second.456');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertNotNull($visitorId);
    $this->assertContains($visitorId, ['first', 'second']);
  }

  /**
   * Tests getting visitor ID with special characters in cookie value.
   *
   * @covers ::getId
   */
  public function testGetIdWithSpecialCharacters(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_special', 'special-chars_123.456.789');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('special-chars_123', $visitorId);
  }

  /**
   * Tests getting visitor ID with numeric cookie value.
   *
   * @covers ::getId
   */
  public function testGetIdWithNumericValue(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_999', '123.456.789');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('123', $visitorId);
  }

  /**
   * Tests getting visitor ID with alphanumeric cookie value.
   *
   * @covers ::getId
   */
  public function testGetIdWithAlphanumericValue(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_alphanumeric', 'abc123.def456.ghi789');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('abc123', $visitorId);
  }

  /**
   * Tests service dependency injection in kernel environment.
   */
  public function testServiceDependencyInjection(): void {
    $service = $this->container->get('visitors.cookie');
    $this->assertInstanceOf(CookieService::class, $service);
    $this->assertSame($this->cookieService, $service);
  }

  /**
   * Tests edge case with cookie value containing only dots.
   *
   * @covers ::getId
   */
  public function testGetIdWithOnlyDots(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_dots', '...');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('', $visitorId);
  }

  /**
   * Tests edge case with cookie value starting with dot.
   *
   * @covers ::getId
   */
  public function testGetIdStartingWithDot(): void {
    $request = Request::create('https://example.com');
    $request->cookies->set('_pk_id_start_dot', '.value.123');

    $this->setRequestOnService($request);

    $visitorId = $this->cookieService->getId();

    $this->assertEquals('', $visitorId);
  }

  /**
   * Helper method to set a request on the service using reflection.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The request to set.
   */
  private function setRequestOnService(Request $request): void {
    $reflection = new \ReflectionClass($this->cookieService);
    $property = $reflection->getProperty('request');
    $property->setAccessible(TRUE);
    $property->setValue($this->cookieService, $request);
  }

}
