<?php

declare(strict_types=1);

namespace Drupal\Tests\visitors\Kernel\Service;

use Drupal\KernelTests\KernelTestBase;
use Drupal\visitors\Service\AiAssistantsService;

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

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

  /**
   * The AI assistants service.
   *
   * @var \Drupal\visitors\Service\AiAssistantsService
   */
  private AiAssistantsService $aiAssistantsService;

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

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

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

  /**
   * Tests basic configuration loading.
   */
  public function testConfigurationBasics(): void {
    // Test that configuration is loaded.
    $reflection = new \ReflectionClass($this->aiAssistantsService);
    $method = $reflection->getMethod('getConfig');
    $method->setAccessible(TRUE);
    $config = $method->invoke($this->aiAssistantsService);

    $this->assertIsArray($config);
    $this->assertNotEmpty($config);

    // Find ChatGPT in the configuration.
    $chatgpt = NULL;
    foreach ($config as $item) {
      if ($item['label'] === 'ChatGPT') {
        $chatgpt = $item;
        break;
      }
    }

    $this->assertNotNull($chatgpt, 'ChatGPT should be found in configuration');
    $this->assertIsArray($chatgpt['hosts']);
    $this->assertContains('chatgpt.com', $chatgpt['hosts']);
    $this->assertContains('chat.openai.com', $chatgpt['hosts']);
  }

  /**
   * Tests matching ChatGPT hostnames.
   *
   * @covers ::match
   */
  public function testMatchChatGpt(): void {
    $result = $this->aiAssistantsService->match('chatgpt.com');
    $this->assertEquals('ChatGPT', $result);

    $result = $this->aiAssistantsService->match('chat.openai.com');
    $this->assertEquals('ChatGPT', $result);

    $result = $this->aiAssistantsService->match('labs.openai.com');
    $this->assertEquals('ChatGPT', $result);
  }

  /**
   * Tests matching Copilot hostnames.
   *
   * @covers ::match
   */
  public function testMatchCopilot(): void {
    $result = $this->aiAssistantsService->match('copilot.microsoft.com');
    $this->assertEquals('Copilot', $result);
  }

  /**
   * Tests matching Gemini hostnames.
   *
   * @covers ::match
   */
  public function testMatchGemini(): void {
    $result = $this->aiAssistantsService->match('gemini.google.com');
    $this->assertEquals('Gemini', $result);

    $result = $this->aiAssistantsService->match('bard.google.com');
    $this->assertEquals('Gemini', $result);
  }

  /**
   * Tests matching Claude hostnames.
   *
   * @covers ::match
   */
  public function testMatchClaude(): void {
    $result = $this->aiAssistantsService->match('claude.ai');
    $this->assertEquals('Claude', $result);
  }

  /**
   * Tests matching other AI assistant hostnames.
   *
   * @covers ::match
   */
  public function testMatchOtherAssistants(): void {
    $result = $this->aiAssistantsService->match('iask.ai');
    $this->assertEquals('iAsk', $result);

    $result = $this->aiAssistantsService->match('chat.mistral.ai');
    $this->assertEquals('Le Chat', $result);

    $result = $this->aiAssistantsService->match('meta.ai');
    $this->assertEquals('Meta AI', $result);

    $result = $this->aiAssistantsService->match('perplexity.ai');
    $this->assertEquals('Perplexity', $result);
  }

  /**
   * Tests case-insensitive matching.
   *
   * @covers ::match
   */
  public function testCaseInsensitiveMatching(): void {
    $result = $this->aiAssistantsService->match('CHATGPT.COM');
    $this->assertEquals('ChatGPT', $result);

    $result = $this->aiAssistantsService->match('Chat.OpenAI.Com');
    $this->assertEquals('ChatGPT', $result);
  }

  /**
   * Tests matching with whitespace.
   *
   * @covers ::match
   */
  public function testMatchWithWhitespace(): void {
    $result = $this->aiAssistantsService->match('  chatgpt.com  ');
    $this->assertEquals('ChatGPT', $result);

    $result = $this->aiAssistantsService->match('  copilot.microsoft.com  ');
    $this->assertEquals('Copilot', $result);
  }

  /**
   * Tests matching non-existent hostnames.
   *
   * @covers ::match
   */
  public function testMatchNonExistentHostnames(): void {
    $result = $this->aiAssistantsService->match('nonexistent.com');
    $this->assertNull($result);

    $result = $this->aiAssistantsService->match('google.com');
    $this->assertNull($result);

    $result = $this->aiAssistantsService->match('facebook.com');
    $this->assertNull($result);
  }

  /**
   * Tests matching with empty hostname.
   *
   * @covers ::match
   */
  public function testMatchEmptyHostname(): void {
    $result = $this->aiAssistantsService->match('');
    $this->assertNull($result);
  }

  /**
   * Tests service dependency injection in kernel environment.
   */
  public function testServiceDependencyInjection(): void {
    // Verify the service can be retrieved from the container.
    $service = $this->container->get('visitors.ai_assistants');
    $this->assertInstanceOf(AiAssistantsService::class, $service);

    // Verify it's the same instance.
    $this->assertSame($this->aiAssistantsService, $service);
  }

  /**
   * Tests configuration caching.
   */
  public function testConfigurationCaching(): void {
    // First call should load from config.
    $result1 = $this->aiAssistantsService->match('chatgpt.com');
    $this->assertEquals('ChatGPT', $result1);

    // Second call should use cached config.
    $result2 = $this->aiAssistantsService->match('chatgpt.com');
    $this->assertEquals('ChatGPT', $result2);

    // Verify both results are the same.
    $this->assertEquals($result1, $result2);
  }

}
