<?php

namespace Drupal\Tests\eb\Kernel;

use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\eb\Entity\EbDefinition;

/**
 * Kernel test for round-trip capability.
 *
 * Tests that importing a definition, applying it, generating a new definition
 * from the resulting Drupal entities, and exporting produces equivalent output.
 *
 * @group eb
 */
class RoundTripTest extends EbKernelTestBase {

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

  /**
   * The definition factory service.
   *
   * @var \Drupal\eb\Service\DefinitionFactory
   */
  protected $definitionFactory;

  /**
   * The YAML parser service.
   *
   * @var \Drupal\eb\Service\YamlParser
   */
  protected $yamlParser;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected EntityTypeManagerInterface $entityTypeManager;

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

    $this->definitionGenerator = $this->container->get('eb.definition_generator');
    $this->definitionFactory = $this->container->get('eb.definition_factory');
    $this->yamlParser = $this->container->get('eb.yaml_parser');
    $this->entityTypeManager = $this->container->get('entity_type.manager');
  }

  /**
   * Tests round-trip with a simple definition.
   *
   * Creates a simple definition, applies it, generates from the resulting
   * entities, and compares the structures.
   */
  public function testSimpleRoundTrip(): void {
    // 1. Create a simple definition programmatically.
    $originalData = [
      'version' => '1.0',
      'id' => 'round_trip_test',
      'label' => 'Round Trip Test',
      'bundle_definitions' => [
        [
          'entity_type' => 'node',
          'bundle_id' => 'round_trip_article',
          'label' => 'Round Trip Article',
          'description' => 'Test content type for round-trip testing',
        ],
      ],
      'field_definitions' => [
        [
          'entity_type' => 'node',
          'bundle' => 'round_trip_article',
          'field_name' => 'field_rt_body',
          'field_type' => 'text_long',
          'label' => 'Body',
          'required' => TRUE,
        ],
      ],
    ];

    // 2. Create definition entity.
    $definition = $this->definitionFactory->createFromYaml($originalData);
    $definition->save();

    // 3. Apply the definition to create Drupal entities.
    $this->applyDefinition($definition);

    // 4. Verify entities were created.
    $this->assertNodeTypeExists('round_trip_article');
    $this->assertFieldConfigExists('node', 'round_trip_article', 'field_rt_body');

    // 5. Generate a new definition from the Drupal entities.
    $generatedData = $this->definitionGenerator->generate([
      'node' => ['round_trip_article'],
    ], [
      'include_extensions' => FALSE,
      'include_displays' => FALSE,
      'normalize_settings' => TRUE,
    ]);

    // 6. Compare bundle definitions.
    $this->assertCount(1, $generatedData['bundle_definitions']);
    $genBundle = $generatedData['bundle_definitions'][0];
    $this->assertEquals('node', $genBundle['entity_type']);
    $this->assertEquals('round_trip_article', $genBundle['bundle_id']);
    $this->assertEquals('Round Trip Article', $genBundle['label']);

    // 7. Compare field definitions.
    $this->assertNotEmpty($generatedData['field_definitions']);

    $bodyField = NULL;
    foreach ($generatedData['field_definitions'] as $fieldDef) {
      if ($fieldDef['field_name'] === 'field_rt_body') {
        $bodyField = $fieldDef;
        break;
      }
    }

    $this->assertNotNull($bodyField);
    $this->assertEquals('text_long', $bodyField['field_type']);
    $this->assertEquals('Body', $bodyField['label']);
    $this->assertTrue($bodyField['required']);
  }

  /**
   * Tests toExportArray produces import-compatible output.
   */
  public function testExportArrayFormat(): void {
    // Create a definition with various components.
    $originalData = [
      'version' => '1.0',
      'id' => 'export_test',
      'label' => 'Export Test',
      'description' => 'Testing export format',
      'bundle_definitions' => [
        [
          'entity_type' => 'node',
          'bundle_id' => 'export_bundle',
          'label' => 'Export Bundle',
        ],
      ],
      'field_definitions' => [
        [
          'entity_type' => 'node',
          'bundle' => 'export_bundle',
          'field_name' => 'field_export_text',
          'field_type' => 'text',
          'label' => 'Export Text',
        ],
      ],
    ];

    // Create and save definition.
    $definition = $this->definitionFactory->createFromYaml($originalData);
    $definition->save();

    // Export to array.
    $exportArray = $definition->toExportArray();

    // Verify structure.
    $this->assertEquals('1.0', $exportArray['version']);
    $this->assertEquals('export_test', $exportArray['id']);
    $this->assertEquals('Export Test', $exportArray['label']);
    $this->assertEquals('Testing export format', $exportArray['description']);
    $this->assertArrayHasKey('bundle_definitions', $exportArray);
    $this->assertArrayHasKey('field_definitions', $exportArray);

    // Re-import the exported data should succeed.
    $reimported = $this->definitionFactory->createFromYaml($exportArray);
    $this->assertEquals('export_test', $reimported->id());
    $this->assertCount(1, $reimported->getBundleDefinitions());
    $this->assertCount(1, $reimported->getFieldDefinitions());
  }

  /**
   * Tests YAML serialization round-trip.
   */
  public function testYamlSerializationRoundTrip(): void {
    // Create a definition.
    $originalData = [
      'version' => '1.0',
      'id' => 'yaml_test',
      'label' => 'YAML Test',
      'bundle_definitions' => [
        [
          'entity_type' => 'node',
          'bundle_id' => 'yaml_bundle',
          'label' => 'YAML Bundle',
        ],
      ],
      'field_definitions' => [],
    ];

    $definition = $this->definitionFactory->createFromYaml($originalData);
    $definition->save();

    // Export to YAML.
    $exportArray = $definition->toExportArray();
    $yamlContent = $this->yamlParser->export($exportArray);

    // Parse the YAML back.
    $parsedData = $this->yamlParser->parse($yamlContent);

    // Create a new definition from parsed data.
    // Need to use a different ID since original exists.
    $parsedData['id'] = 'yaml_test_reimport';
    $reimported = $this->definitionFactory->createFromYaml($parsedData);

    // Compare key elements.
    $this->assertEquals($definition->label(), $reimported->label());
    $this->assertEquals(
      count($definition->getBundleDefinitions()),
      count($reimported->getBundleDefinitions())
    );
  }

  /**
   * Applies a definition by creating the required operations.
   *
   * This is a simplified version that creates bundles and fields directly
   * without using the full operation processor.
   *
   * @param \Drupal\eb\Entity\EbDefinition $definition
   *   The definition to apply.
   */
  protected function applyDefinition(EbDefinition $definition): void {
    // Create bundles.
    foreach ($definition->getBundleDefinitions() as $bundleDef) {
      $entityType = $bundleDef['entity_type'];
      $bundleId = $bundleDef['bundle_id'];

      if ($entityType === 'node') {
        $this->createNodeType($bundleId, $bundleDef['label'], [
          'description' => $bundleDef['description'] ?? '',
        ]);
      }
    }

    // Create fields.
    foreach ($definition->getFieldDefinitions() as $fieldDef) {
      $fieldStorage = FieldStorageConfig::create([
        'field_name' => $fieldDef['field_name'],
        'entity_type' => $fieldDef['entity_type'],
        'type' => $fieldDef['field_type'],
        'cardinality' => $fieldDef['cardinality'] ?? 1,
        'settings' => $fieldDef['field_storage_settings'] ?? [],
      ]);
      $fieldStorage->save();

      $fieldConfig = FieldConfig::create([
        'field_storage' => $fieldStorage,
        'bundle' => $fieldDef['bundle'],
        'label' => $fieldDef['label'],
        'required' => $fieldDef['required'] ?? FALSE,
        'description' => $fieldDef['description'] ?? '',
        'settings' => $fieldDef['settings'] ?? [],
      ]);
      $fieldConfig->save();
    }

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

    // Mark definition as applied.
    $definition->markAsApplied();
    $definition->save();
  }

}
