<?php

namespace Drupal\Tests\eb\Kernel\Service;

use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\eb\Kernel\EbKernelTestBase;

/**
 * Kernel tests for DefinitionGenerator service.
 *
 * Tests the definition generator against real Drupal entities to verify
 * that bundle, field, and display configurations are correctly extracted.
 *
 * @coversDefaultClass \Drupal\eb\Service\DefinitionGenerator
 * @group eb
 */
class DefinitionGeneratorKernelTest extends EbKernelTestBase {

  /**
   * The definition generator service.
   *
   * @var \Drupal\eb\Service\DefinitionGeneratorInterface
   */
  protected $definitionGenerator;

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

    $this->definitionGenerator = $this->container->get('eb.definition_generator');
  }

  /**
   * Tests generating definition from a simple node type.
   *
   * @covers ::generateBundle
   * @covers ::extractBundleDefinition
   */
  public function testGenerateBundleFromNodeType(): void {
    // Create a node type.
    $this->createNodeType('test_article', 'Test Article');

    // Generate definition.
    $result = $this->definitionGenerator->generateBundle('node', 'test_article');

    // Verify bundle definition.
    $this->assertArrayHasKey('bundle_definitions', $result);
    $this->assertCount(1, $result['bundle_definitions']);

    $bundleDef = $result['bundle_definitions'][0];
    $this->assertEquals('node', $bundleDef['entity_type']);
    $this->assertEquals('test_article', $bundleDef['bundle_id']);
    $this->assertEquals('Test Article', $bundleDef['label']);
  }

  /**
   * Tests generating definition with custom fields.
   *
   * @covers ::generateBundle
   * @covers ::extractFieldDefinitions
   */
  public function testGenerateBundleWithFields(): void {
    // Create a node type.
    $this->createNodeType('test_bundle', 'Test Bundle');

    // Create a text field.
    $fieldStorage = FieldStorageConfig::create([
      'field_name' => 'field_test_text',
      'entity_type' => 'node',
      'type' => 'text',
      'cardinality' => 1,
    ]);
    $fieldStorage->save();

    $fieldConfig = FieldConfig::create([
      'field_storage' => $fieldStorage,
      'bundle' => 'test_bundle',
      'label' => 'Test Text',
      'required' => TRUE,
      'description' => 'A test text field',
    ]);
    $fieldConfig->save();

    // Clear caches to ensure field is visible.
    $this->clearCaches();

    // Generate definition.
    $result = $this->definitionGenerator->generateBundle('node', 'test_bundle');

    // Verify field definitions.
    $this->assertArrayHasKey('field_definitions', $result);
    $this->assertNotEmpty($result['field_definitions']);

    // Find our test field.
    $testField = NULL;
    foreach ($result['field_definitions'] as $fieldDef) {
      if ($fieldDef['field_name'] === 'field_test_text') {
        $testField = $fieldDef;
        break;
      }
    }

    $this->assertNotNull($testField, 'Field definition for field_test_text should exist');
    $this->assertEquals('text', $testField['field_type']);
    $this->assertEquals('Test Text', $testField['label']);
    $this->assertTrue($testField['required']);
  }

  /**
   * Tests generating definition with entity reference field.
   *
   * @covers ::extractEntityReferenceSettings
   */
  public function testGenerateWithEntityReferenceField(): void {
    // Create a vocabulary for target.
    $this->createVocabulary('test_tags', 'Test Tags');

    // Create a node type.
    $this->createNodeType('test_ref', 'Test Ref');

    // Create an entity reference field.
    $fieldStorage = FieldStorageConfig::create([
      'field_name' => 'field_test_ref',
      'entity_type' => 'node',
      'type' => 'entity_reference',
      'cardinality' => -1,
      'settings' => [
        'target_type' => 'taxonomy_term',
      ],
    ]);
    $fieldStorage->save();

    $fieldConfig = FieldConfig::create([
      'field_storage' => $fieldStorage,
      'bundle' => 'test_ref',
      'label' => 'Tags Reference',
      'settings' => [
        'handler' => 'default:taxonomy_term',
        'handler_settings' => [
          'target_bundles' => [
            'test_tags' => 'test_tags',
          ],
        ],
      ],
    ]);
    $fieldConfig->save();

    // Clear caches.
    $this->clearCaches();

    // Generate definition.
    $result = $this->definitionGenerator->generateBundle('node', 'test_ref');

    // Find our entity reference field.
    $refField = NULL;
    foreach ($result['field_definitions'] as $fieldDef) {
      if ($fieldDef['field_name'] === 'field_test_ref') {
        $refField = $fieldDef;
        break;
      }
    }

    $this->assertNotNull($refField, 'Entity reference field should exist');
    $this->assertEquals('entity_reference', $refField['field_type']);
    $this->assertEquals(-1, $refField['cardinality']);
    $this->assertArrayHasKey('settings', $refField);
    $this->assertEquals('taxonomy_term', $refField['settings']['target_type']);
  }

  /**
   * Tests generate with multiple bundles.
   *
   * @covers ::generate
   */
  public function testGenerateMultipleBundles(): void {
    // Create two node types.
    $this->createNodeType('bundle_one', 'Bundle One');
    $this->createNodeType('bundle_two', 'Bundle Two');

    // Generate definition for both.
    $result = $this->definitionGenerator->generate([
      'node' => ['bundle_one', 'bundle_two'],
    ]);

    // Verify both bundles included.
    $this->assertArrayHasKey('bundle_definitions', $result);
    $this->assertCount(2, $result['bundle_definitions']);

    $bundleIds = array_column($result['bundle_definitions'], 'bundle_id');
    $this->assertContains('bundle_one', $bundleIds);
    $this->assertContains('bundle_two', $bundleIds);
  }

  /**
   * Tests generate with include_fields option disabled.
   *
   * @covers ::generateBundle
   */
  public function testGenerateWithoutFields(): void {
    // Create a node type with a field.
    $this->createNodeType('test_no_fields', 'Test No Fields');

    $fieldStorage = FieldStorageConfig::create([
      'field_name' => 'field_should_skip',
      'entity_type' => 'node',
      'type' => 'text',
    ]);
    $fieldStorage->save();

    FieldConfig::create([
      'field_storage' => $fieldStorage,
      'bundle' => 'test_no_fields',
      'label' => 'Should Skip',
    ])->save();

    $this->clearCaches();

    // Generate without fields.
    $result = $this->definitionGenerator->generateBundle('node', 'test_no_fields', [
      'include_fields' => FALSE,
    ]);

    // Should have bundle but no fields.
    $this->assertArrayHasKey('bundle_definitions', $result);
    $this->assertCount(1, $result['bundle_definitions']);
    $this->assertEmpty($result['field_definitions'] ?? []);
  }

  /**
   * Tests that output format is compatible with DefinitionFactory.
   *
   * @covers ::generate
   */
  public function testOutputFormatCompatibleWithFactory(): void {
    // Create a node type.
    $this->createNodeType('test_compat', 'Test Compatibility');

    // Generate definition.
    $result = $this->definitionGenerator->generate([
      'node' => ['test_compat'],
    ], [
      'include_extensions' => FALSE,
    ]);

    // Add required metadata.
    $result['id'] = 'test_definition';
    $result['label'] = 'Test Definition';

    // Should be valid input for DefinitionFactory.
    $factory = $this->container->get('eb.definition_factory');

    // This should not throw an exception.
    $definition = $factory->createFromYaml($result);

    $this->assertEquals('test_definition', $definition->id());
    $this->assertEquals('Test Definition', $definition->label());
    $this->assertCount(1, $definition->getBundleDefinitions());
  }

}
