<?php

namespace Drupal\Tests\eb\Unit;

use Drupal\eb\PluginManager\EbExtensionPluginManager;
use Drupal\eb\Service\Dependency\DependencyResolver;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use PHPUnit\Framework\TestCase;

/**
 * Tests for DependencyResolver service.
 *
 * @coversDefaultClass \Drupal\eb\Service\Dependency\DependencyResolver
 * @group eb
 */
class DependencyResolverTest extends TestCase {

  /**
   * The dependency resolver service.
   */
  protected DependencyResolver $dependencyResolver;

  /**
   * Mock entity type manager.
   */
  protected EntityTypeManagerInterface $entityTypeManager;

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

    // Mock entity type manager.
    $this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);

    $extensionManager = $this->createMock(EbExtensionPluginManager::class);
    $extensionManager->method('getExtensions')->willReturn([]);
    $this->dependencyResolver = new DependencyResolver($this->entityTypeManager, $extensionManager);
  }

  /**
   * Tests resolving operations with no dependencies.
   *
   * @covers ::resolve
   */
  public function testResolveNoDependencies(): void {
    $operations = [
          [
            'operation' => 'create_bundle',
            'entity_type' => 'node',
            'bundle_id' => 'article',
          ],
          [
            'operation' => 'create_bundle',
            'entity_type' => 'node',
            'bundle_id' => 'page',
          ],
    ];

    $sorted = $this->dependencyResolver->resolve($operations, FALSE);

    // Should return same order since no dependencies.
    $this->assertCount(2, $sorted);
  }

  /**
   * Tests resolving operations with simple field dependency.
   *
   * @covers ::resolve
   */
  public function testResolveSimpleFieldDependency(): void {
    $operations = [
          [
            'operation' => 'create_field',
            'entity_type' => 'node',
            'bundle' => 'article',
            'field_name' => 'field_title',
            'field_type' => 'string',
          ],
          [
            'operation' => 'create_bundle',
            'entity_type' => 'node',
            'bundle_id' => 'article',
          ],
    ];

    $sorted = $this->dependencyResolver->resolve($operations, FALSE);

    // Bundle should come before field.
    $this->assertEquals('create_bundle', $sorted[0]['operation']);
    $this->assertEquals('create_field', $sorted[1]['operation']);
  }

  /**
   * Tests resolving operations with entity reference dependency.
   *
   * @covers ::resolve
   */
  public function testResolveEntityReferenceDependency(): void {
    $operations = [
          [
            'operation' => 'create_field',
            'entity_type' => 'node',
            'bundle' => 'product',
            'field_name' => 'field_category',
            'field_type' => 'entity_reference',
            'field_storage_settings' => [
              'target_type' => 'taxonomy_term',
            ],
            'field_config_settings' => [
              'handler_settings' => [
                'target_bundles' => [
                  'categories' => 'categories',
                ],
              ],
            ],
          ],
          [
            'operation' => 'create_bundle',
            'entity_type' => 'node',
            'bundle_id' => 'product',
          ],
          [
            'operation' => 'create_bundle',
            'entity_type' => 'taxonomy_term',
            'bundle_id' => 'categories',
          ],
    ];

    $sorted = $this->dependencyResolver->resolve($operations, FALSE);

    // Both bundles should come before the field.
    // Verify we have 3 operations.
    $this->assertCount(3, $sorted);

    // Find indices of each operation.
    $taxonomyBundleIndex = NULL;
    $nodeBundleIndex = NULL;
    $fieldIndex = NULL;

    foreach ($sorted as $index => $operation) {
      if ($operation['operation'] === 'create_bundle') {
        if ($operation['entity_type'] === 'taxonomy_term') {
          $taxonomyBundleIndex = $index;
        }
        elseif ($operation['entity_type'] === 'node') {
          $nodeBundleIndex = $index;
        }
      }
      elseif ($operation['operation'] === 'create_field') {
        $fieldIndex = $index;
      }
    }

    // Both bundles should come before field.
    $this->assertLessThan($fieldIndex, $taxonomyBundleIndex);
    $this->assertLessThan($fieldIndex, $nodeBundleIndex);
  }

  /**
   * Tests resolving operations with display dependency.
   *
   * @covers ::resolve
   */
  public function testResolveDisplayDependency(): void {
    $operations = [
          [
            'operation' => 'configure_form_mode',
            'entity_type' => 'node',
            'bundle' => 'article',
            'field_name' => 'field_title',
            'widget' => 'string_textfield',
          ],
          [
            'operation' => 'create_field',
            'entity_type' => 'node',
            'bundle' => 'article',
            'field_name' => 'field_title',
            'field_type' => 'string',
          ],
          [
            'operation' => 'create_bundle',
            'entity_type' => 'node',
            'bundle_id' => 'article',
          ],
    ];

    $sorted = $this->dependencyResolver->resolve($operations, FALSE);

    // Bundle, then field, then display.
    $this->assertEquals('create_bundle', $sorted[0]['operation']);
    $this->assertEquals('create_field', $sorted[1]['operation']);
    $this->assertEquals('configure_form_mode', $sorted[2]['operation']);
  }

  /**
   * Tests detecting circular dependencies.
   *
   * @covers ::resolve
   */
  public function testCircularDependencyDetection(): void {
    // This would require a self-referencing entity reference
    // which isn't possible in the same operation set without existing entities.
    // The resolver should detect this in validation.
    // For now, test that resolve completes without circular deps.
    $operations = [
          [
            'operation' => 'create_bundle',
            'entity_type' => 'node',
            'bundle_id' => 'article',
          ],
    ];

    $sorted = $this->dependencyResolver->resolve($operations, FALSE);
    $this->assertCount(1, $sorted);
  }

  /**
   * Tests resolving complex dependency chain.
   *
   * @covers ::resolve
   */
  public function testResolveComplexDependencyChain(): void {
    $operations = [
          // Display config (depends on field).
          [
            'operation' => 'configure_view_mode',
            'entity_type' => 'node',
            'bundle' => 'product',
            'field_name' => 'field_category',
            'formatter' => 'entity_reference_label',
          ],
          // Field (depends on both bundles).
          [
            'operation' => 'create_field',
            'entity_type' => 'node',
            'bundle' => 'product',
            'field_name' => 'field_category',
            'field_type' => 'entity_reference',
            'field_storage_settings' => [
              'target_type' => 'taxonomy_term',
            ],
            'field_config_settings' => [
              'handler_settings' => [
                'target_bundles' => [
                  'categories' => 'categories',
                ],
              ],
            ],
          ],
          // Node bundle (no dependencies).
          [
            'operation' => 'create_bundle',
            'entity_type' => 'node',
            'bundle_id' => 'product',
          ],
          // Taxonomy bundle (no dependencies).
          [
            'operation' => 'create_bundle',
            'entity_type' => 'taxonomy_term',
            'bundle_id' => 'categories',
          ],
    ];

    $sorted = $this->dependencyResolver->resolve($operations, FALSE);

    // Verify correct order: bundles first, then field, then display.
    $bundleIndices = [];
    $fieldIndex = NULL;
    $displayIndex = NULL;

    foreach ($sorted as $index => $operation) {
      if ($operation['operation'] === 'create_bundle') {
        $bundleIndices[] = $index;
      }
      elseif ($operation['operation'] === 'create_field') {
        $fieldIndex = $index;
      }
      elseif ($operation['operation'] === 'configure_view_mode') {
        $displayIndex = $index;
      }
    }

    // All bundles should come before field.
    foreach ($bundleIndices as $bundleIndex) {
      $this->assertLessThan($fieldIndex, $bundleIndex);
    }

    // Field should come before display.
    $this->assertLessThan($displayIndex, $fieldIndex);
  }

  /**
   * Tests resolving operations with multiple target bundles.
   *
   * @covers ::resolve
   */
  public function testResolveMultipleTargetBundles(): void {
    $operations = [
          [
            'operation' => 'create_field',
            'entity_type' => 'node',
            'bundle' => 'article',
            'field_name' => 'field_tags',
            'field_type' => 'entity_reference',
            'field_storage_settings' => [
              'target_type' => 'taxonomy_term',
            ],
            'field_config_settings' => [
              'handler_settings' => [
                'target_bundles' => [
                  'tags' => 'tags',
                  'categories' => 'categories',
                ],
              ],
            ],
          ],
          [
            'operation' => 'create_bundle',
            'entity_type' => 'node',
            'bundle_id' => 'article',
          ],
          [
            'operation' => 'create_bundle',
            'entity_type' => 'taxonomy_term',
            'bundle_id' => 'tags',
          ],
          [
            'operation' => 'create_bundle',
            'entity_type' => 'taxonomy_term',
            'bundle_id' => 'categories',
          ],
    ];

    $sorted = $this->dependencyResolver->resolve($operations, FALSE);

    // All three bundles should come before the field.
    $fieldIndex = NULL;
    $bundleIndices = [];

    foreach ($sorted as $index => $operation) {
      if ($operation['operation'] === 'create_bundle') {
        $bundleIndices[] = $index;
      }
      elseif ($operation['operation'] === 'create_field') {
        $fieldIndex = $index;
      }
    }

    foreach ($bundleIndices as $bundleIndex) {
      $this->assertLessThan($fieldIndex, $bundleIndex);
    }

    $this->assertCount(4, $sorted);
  }

}
