<?php

namespace Drupal\Tests\eb\Kernel\Service;

use Drupal\eb\Service\FieldManagementService;
use Drupal\KernelTests\KernelTestBase;

/**
 * Kernel tests for FieldManagementService.
 *
 * @coversDefaultClass \Drupal\eb\Service\FieldManagementService
 * @group eb
 */
class FieldManagementServiceKernelTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'eb',
    'system',
    'user',
    'field',
    'node',
    'text',
  ];

  /**
   * The field management service.
   */
  protected FieldManagementService $service;

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

    $this->installEntitySchema('user');
    $this->installEntitySchema('node');
    $this->installConfig(['field', 'node']);

    $this->service = $this->container->get('eb.field_management');
  }

  /**
   * Tests fieldStorageExists returns false when storage does not exist.
   *
   * @covers ::fieldStorageExists
   */
  public function testFieldStorageExistsReturnsFalse(): void {
    $result = $this->service->fieldStorageExists('node', 'field_nonexistent');

    $this->assertFalse($result);
  }

  /**
   * Tests fieldConfigExists returns false when config does not exist.
   *
   * @covers ::fieldConfigExists
   */
  public function testFieldConfigExistsReturnsFalse(): void {
    $result = $this->service->fieldConfigExists('node', 'article', 'field_nonexistent');

    $this->assertFalse($result);
  }

  /**
   * Tests createFieldStorage and fieldStorageExists.
   *
   * @covers ::createFieldStorage
   * @covers ::fieldStorageExists
   * @covers ::loadFieldStorage
   */
  public function testCreateFieldStorage(): void {
    // First verify it doesn't exist.
    $this->assertFalse($this->service->fieldStorageExists('node', 'field_test_text'));

    // Create the field storage.
    $storage = $this->service->createFieldStorage([
      'field_name' => 'field_test_text',
      'entity_type' => 'node',
      'type' => 'text',
    ]);

    // Verify it was created.
    $this->assertNotNull($storage);
    $this->assertEquals('field_test_text', $storage->getName());
    $this->assertTrue($this->service->fieldStorageExists('node', 'field_test_text'));

    // Verify we can load it.
    $loadedStorage = $this->service->loadFieldStorage('node', 'field_test_text');
    $this->assertNotNull($loadedStorage);
    $this->assertEquals('field_test_text', $loadedStorage->getName());
  }

  /**
   * Tests loadFieldStorageById.
   *
   * @covers ::loadFieldStorageById
   */
  public function testLoadFieldStorageById(): void {
    // Create field storage.
    $this->service->createFieldStorage([
      'field_name' => 'field_test_id',
      'entity_type' => 'node',
      'type' => 'text',
    ]);

    // Load by ID.
    $storage = $this->service->loadFieldStorageById('node.field_test_id');
    $this->assertNotNull($storage);
    $this->assertEquals('field_test_id', $storage->getName());

    // Verify non-existent returns null.
    $nonExistent = $this->service->loadFieldStorageById('node.nonexistent');
    $this->assertNull($nonExistent);
  }

  /**
   * Tests createFieldConfig and fieldConfigExists.
   *
   * @covers ::createFieldConfig
   * @covers ::fieldConfigExists
   * @covers ::loadFieldConfig
   */
  public function testCreateFieldConfig(): void {
    // First create node type.
    $nodeType = $this->container->get('entity_type.manager')
      ->getStorage('node_type')
      ->create([
        'type' => 'article',
        'name' => 'Article',
      ]);
    $nodeType->save();

    // Create field storage first.
    $this->service->createFieldStorage([
      'field_name' => 'field_config_test',
      'entity_type' => 'node',
      'type' => 'text',
    ]);

    // Verify field config doesn't exist.
    $this->assertFalse($this->service->fieldConfigExists('node', 'article', 'field_config_test'));

    // Create field config.
    $fieldConfig = $this->service->createFieldConfig([
      'field_name' => 'field_config_test',
      'entity_type' => 'node',
      'bundle' => 'article',
      'label' => 'Test Field',
    ]);

    // Verify it was created.
    $this->assertNotNull($fieldConfig);
    $this->assertEquals('field_config_test', $fieldConfig->getName());
    $this->assertTrue($this->service->fieldConfigExists('node', 'article', 'field_config_test'));

    // Verify we can load it.
    $loadedConfig = $this->service->loadFieldConfig('node', 'article', 'field_config_test');
    $this->assertNotNull($loadedConfig);
    $this->assertEquals('Test Field', $loadedConfig->getLabel());
  }

  /**
   * Tests loadFieldConfigById.
   *
   * @covers ::loadFieldConfigById
   */
  public function testLoadFieldConfigById(): void {
    // Create node type and field.
    $nodeType = $this->container->get('entity_type.manager')
      ->getStorage('node_type')
      ->create([
        'type' => 'page',
        'name' => 'Page',
      ]);
    $nodeType->save();

    $this->service->createFieldStorage([
      'field_name' => 'field_id_test',
      'entity_type' => 'node',
      'type' => 'text',
    ]);

    $this->service->createFieldConfig([
      'field_name' => 'field_id_test',
      'entity_type' => 'node',
      'bundle' => 'page',
      'label' => 'ID Test Field',
    ]);

    // Load by ID.
    $config = $this->service->loadFieldConfigById('node.page.field_id_test');
    $this->assertNotNull($config);
    $this->assertEquals('ID Test Field', $config->getLabel());

    // Verify non-existent returns null.
    $nonExistent = $this->service->loadFieldConfigById('node.page.nonexistent');
    $this->assertNull($nonExistent);
  }

  /**
   * Tests deleteFieldStorage.
   *
   * @covers ::deleteFieldStorage
   */
  public function testDeleteFieldStorage(): void {
    // Create field storage.
    $storage = $this->service->createFieldStorage([
      'field_name' => 'field_delete_test',
      'entity_type' => 'node',
      'type' => 'text',
    ]);

    $this->assertTrue($this->service->fieldStorageExists('node', 'field_delete_test'));

    // Delete it.
    $this->service->deleteFieldStorage($storage);

    $this->assertFalse($this->service->fieldStorageExists('node', 'field_delete_test'));
  }

  /**
   * Tests deleteFieldConfig.
   *
   * @covers ::deleteFieldConfig
   */
  public function testDeleteFieldConfig(): void {
    // Create node type.
    $nodeType = $this->container->get('entity_type.manager')
      ->getStorage('node_type')
      ->create([
        'type' => 'blog',
        'name' => 'Blog',
      ]);
    $nodeType->save();

    // Create field storage and config.
    $this->service->createFieldStorage([
      'field_name' => 'field_blog_test',
      'entity_type' => 'node',
      'type' => 'text',
    ]);

    $fieldConfig = $this->service->createFieldConfig([
      'field_name' => 'field_blog_test',
      'entity_type' => 'node',
      'bundle' => 'blog',
      'label' => 'Blog Test Field',
    ]);

    $this->assertTrue($this->service->fieldConfigExists('node', 'blog', 'field_blog_test'));

    // Delete field config.
    $this->service->deleteFieldConfig($fieldConfig);

    $this->assertFalse($this->service->fieldConfigExists('node', 'blog', 'field_blog_test'));
  }

  /**
   * Tests clearCachedFieldDefinitions.
   *
   * @covers ::clearCachedFieldDefinitions
   */
  public function testClearCachedFieldDefinitions(): void {
    // This test just verifies the method doesn't throw an exception.
    $this->service->clearCachedFieldDefinitions();
    $this->assertTrue(TRUE);
  }

  /**
   * Tests getBundlesUsingField.
   *
   * @covers ::getBundlesUsingField
   */
  public function testGetBundlesUsingField(): void {
    // Create two node types.
    $nodeTypeStorage = $this->container->get('entity_type.manager')
      ->getStorage('node_type');

    $nodeTypeStorage->create([
      'type' => 'news',
      'name' => 'News',
    ])->save();

    $nodeTypeStorage->create([
      'type' => 'event',
      'name' => 'Event',
    ])->save();

    // Create field storage.
    $this->service->createFieldStorage([
      'field_name' => 'field_shared',
      'entity_type' => 'node',
      'type' => 'text',
    ]);

    // Create field config on both bundles.
    $this->service->createFieldConfig([
      'field_name' => 'field_shared',
      'entity_type' => 'node',
      'bundle' => 'news',
      'label' => 'Shared Field',
    ]);

    $this->service->createFieldConfig([
      'field_name' => 'field_shared',
      'entity_type' => 'node',
      'bundle' => 'event',
      'label' => 'Shared Field',
    ]);

    // Clear cache and check bundles.
    $this->service->clearCachedFieldDefinitions();

    $bundles = $this->service->getBundlesUsingField('node', 'field_shared');

    $this->assertContains('news', $bundles);
    $this->assertContains('event', $bundles);
    $this->assertCount(2, $bundles);
  }

  /**
   * Tests getBundlesUsingField returns empty for non-existent field.
   *
   * @covers ::getBundlesUsingField
   */
  public function testGetBundlesUsingFieldReturnsEmptyWhenNotFound(): void {
    $bundles = $this->service->getBundlesUsingField('node', 'field_nonexistent');

    $this->assertEquals([], $bundles);
  }

}
