<?php

declare(strict_types=1);

namespace Drupal\Tests\ai\Kernel\AiPlatform;

use Drupal\KernelTests\KernelTestBase;
use Symfony\AI\Platform\Message\Message;
use Symfony\AI\Platform\Message\MessageBag;
use Symfony\AI\Platform\Result\DeferredResult;
use Drupal\ai\OperationType\Chat\ChatInput;
use Drupal\ai\OperationType\Chat\ChatMessage;
use Drupal\ai\OperationType\Chat\ChatOutput;
use Drupal\ai\PlatformProxy;
use Symfony\AI\Platform\Tool\ExecutionReference;
use Symfony\AI\Platform\Tool\Tool;

/**
 * Tests the AiPlatform plugin system and BC bridge.
 *
 * @group ai
 */
class AiPlatformTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'ai',
    'ai_test',
    'key',
    'file',
    'user',
    'field',
    'system',
    'symfony_http_client',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->installEntitySchema('user');
    $this->installEntitySchema('file');
    $this->installSchema('file', ['file_usage']);
    $this->installConfig(['ai', 'ai_test']);
    $this->installEntitySchema('ai_mock_provider_result');
  }

  /**
   * Tests that the echo platform config entity is created.
   */
  public function testPlatformConfigEntityExists(): void {
    $entity = \Drupal::entityTypeManager()
      ->getStorage('ai_platform')
      ->load('echo_test');
    $this->assertNotNull($entity, 'Echo test platform config entity exists.');
    $this->assertEquals('Echo Test', $entity->label());
    $this->assertEquals('echo', $entity->getPlatformId());
  }

  /**
   * Tests invoking the platform directly.
   */
  public function testPlatformInvoke(): void {
    /** @var \Drupal\ai\Entity\AiPlatformConfigInterface $entity */
    $entity = \Drupal::entityTypeManager()
      ->getStorage('ai_platform')
      ->load('echo_test');
    $platform = $entity->getPlatformInstance();
    $this->assertNotNull($platform);

    $messages = new MessageBag(
      Message::ofUser('What is the capital of France?')
    );

    $result = $platform->invoke('echo-model', $messages);

    $this->assertInstanceOf(DeferredResult::class, $result);
    $text = $result->asText();
    $this->assertStringContainsString('What is the capital of France?', $text);
  }

  /**
   * Tests invoke with system and user messages.
   */
  public function testPlatformInvokeWithSystemMessage(): void {
    /** @var \Drupal\ai\Entity\AiPlatformConfigInterface $entity */
    $entity = \Drupal::entityTypeManager()
      ->getStorage('ai_platform')
      ->load('echo_test');
    $platform = $entity->getPlatformInstance();

    $messages = new MessageBag(
      Message::forSystem('You are a helpful assistant.'),
      Message::ofUser('Hello'),
    );

    $result = $platform->invoke('echo-model', $messages);

    $this->assertInstanceOf(DeferredResult::class, $result);
    $text = $result->asText();
    $this->assertStringContainsString('Hello', $text);
  }

  /**
   * Tests invoke with tools returns tool calls.
   */
  public function testPlatformInvokeWithTools(): void {
    /** @var \Drupal\ai\Entity\AiPlatformConfigInterface $entity */
    $entity = \Drupal::entityTypeManager()
      ->getStorage('ai_platform')
      ->load('echo_test');
    $platform = $entity->getPlatformInstance();

    $messages = new MessageBag(
      Message::ofUser('What is the weather?')
    );

    $tools = [
      new Tool(
        new ExecutionReference(self::class, 'noOp'),
        'get_weather',
        'Get the weather',
        [
          'type' => 'object',
          'properties' => [
            'location' => ['type' => 'string'],
          ],
        ],
      ),
    ];

    $result = $platform->invoke('echo-model', $messages, ['tools' => $tools]);

    $this->assertInstanceOf(DeferredResult::class, $result);
    $toolCalls = $result->asToolCalls();
    $this->assertNotEmpty($toolCalls);
    $this->assertEquals('get_weather', $toolCalls[0]->getName());
  }

  /**
   * Tests that platforms appear in getProvidersForOperationType().
   */
  public function testPlatformProviderDiscovery(): void {
    $manager = \Drupal::service('ai.provider');

    $providers = $manager->getProvidersForOperationType('chat', FALSE);
    $this->assertArrayHasKey('ai_platform:echo_test', $providers, 'Platform appears as provider for chat.');
  }

  /**
   * Tests that createInstance() with platform ID returns PlatformProxy.
   */
  public function testPlatformProviderChat(): void {
    $manager = \Drupal::service('ai.provider');
    $provider = $manager->createInstance('ai_platform:echo_test');

    $this->assertInstanceOf(PlatformProxy::class, $provider);

    $input = new ChatInput([
      new ChatMessage('user', 'Hello via platform proxy'),
    ]);

    $output = $provider->chat($input, 'echo-model');

    $this->assertInstanceOf(ChatOutput::class, $output);
    $normalized = $output->getNormalized();
    $this->assertInstanceOf(ChatMessage::class, $normalized);
    $this->assertEquals('assistant', $normalized->getRole());
    $this->assertStringContainsString('Hello via platform proxy', $normalized->getText());
  }

  /**
   * Tests that platforms appear in getSimpleProviderModelOptions().
   */
  public function testPlatformModelOptions(): void {
    $manager = \Drupal::service('ai.provider');
    $options = $manager->getSimpleProviderModelOptions('chat', FALSE, FALSE);

    // The echo platform has no model catalog entries, so it won't appear
    // in model options. This test verifies the method doesn't break.
    $this->assertIsArray($options);
  }

  /**
   * No-op method for tool execution reference.
   */
  public static function noOp(): string {
    return '';
  }

}
