<?php

namespace Drupal\Tests\eb\Kernel\Service;

use Drupal\KernelTests\KernelTestBase;
use Drupal\eb\Service\ChangeDetection\ChangeDetector;
use Drupal\node\Entity\NodeType;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\field\Entity\FieldConfig;

/**
 * Kernel tests for ChangeDetector with real Drupal entities.
 *
 * @group eb
 */
class ChangeDetectorKernelTest extends KernelTestBase {

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

  /**
   * The change detector service.
   */
  protected ChangeDetector $changeDetector;

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

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

    $this->changeDetector = \Drupal::service('eb.change_detector');
  }

  /**
   * Tests sync mode with non-existent entity.
   */
  public function testSyncModeCreate(): void {
    $operations = [
      [
        'operation' => 'create_bundle',
        'entity_type' => 'node',
        'bundle_id' => 'test_bundle',
        'label' => 'Test Bundle',
      ],
    ];

    $result = $this->changeDetector->detectChanges($operations, 'sync');

    // Should create the bundle.
    $this->assertCount(1, $result);
    $this->assertEquals('create_bundle', $result[0]['operation']);
  }

  /**
   * Tests sync mode with unchanged existing entity.
   */
  public function testSyncModeSkipUnchanged(): void {
    // Create a bundle.
    $bundle = NodeType::create([
      'type' => 'test_bundle',
      'name' => 'Test Bundle',
    ]);
    $bundle->save();

    $operations = [
      [
        'operation' => 'create_bundle',
        'entity_type' => 'node',
        'bundle_id' => 'test_bundle',
        'label' => 'Test Bundle',
      ],
    ];

    $result = $this->changeDetector->detectChanges($operations, 'sync');

    // Should skip unchanged bundle.
    $this->assertEmpty($result);
  }

  /**
   * Tests sync mode with changed existing entity.
   */
  public function testSyncModeUpdateChanged(): void {
    // Create a bundle.
    $bundle = NodeType::create([
      'type' => 'test_bundle',
      'name' => 'Test Bundle',
    ]);
    $bundle->save();

    $operations = [
      [
        'operation' => 'create_bundle',
        'entity_type' => 'node',
        'bundle_id' => 'test_bundle',
        'label' => 'Test Bundle Updated',
      ],
    ];

    $result = $this->changeDetector->detectChanges($operations, 'sync');

    // Should update the bundle.
    $this->assertCount(1, $result);
    $this->assertEquals('update_bundle', $result[0]['operation']);
    $this->assertEquals('Test Bundle Updated', $result[0]['label']);
  }

  /**
   * Tests create_only mode.
   */
  public function testCreateOnlyMode(): void {
    $operations = [
      [
        'operation' => 'create_bundle',
        'entity_type' => 'node',
        'bundle_id' => 'new_bundle',
        'label' => 'New Bundle',
      ],
    ];

    $result = $this->changeDetector->detectChanges($operations, 'create_only');

    // Should create new bundle.
    $this->assertCount(1, $result);
    $this->assertEquals('create_bundle', $result[0]['operation']);
    $this->assertEquals('new_bundle', $result[0]['bundle_id']);
  }

  /**
   * Tests update_only mode.
   */
  public function testUpdateOnlyMode(): void {
    // Create a bundle.
    $bundle = NodeType::create([
      'type' => 'test_bundle',
      'name' => 'Test Bundle',
    ]);
    $bundle->save();

    $operations = [
      [
        'operation' => 'create_bundle',
        'entity_type' => 'node',
        'bundle_id' => 'test_bundle',
        'label' => 'Test Bundle Updated',
      ],
    ];

    $result = $this->changeDetector->detectChanges($operations, 'update_only');

    // Should update existing bundle.
    $this->assertCount(1, $result);
    $this->assertEquals('update_bundle', $result[0]['operation']);
    $this->assertEquals('test_bundle', $result[0]['bundle_id']);
  }

  /**
   * Tests replace mode.
   */
  public function testReplaceMode(): void {
    // Create a bundle.
    $bundle = NodeType::create([
      'type' => 'test_bundle',
      'name' => 'Test Bundle',
    ]);
    $bundle->save();

    $operations = [
      [
        'operation' => 'create_bundle',
        'entity_type' => 'node',
        'bundle_id' => 'test_bundle',
        'label' => 'Test Bundle Replaced',
      ],
    ];

    $result = $this->changeDetector->detectChanges($operations, 'replace');

    // Replace mode returns array of operation arrays for each operation.
    // Flatten the result to get all operations.
    $flatResult = [];
    /** @var array<int|string, mixed> $ops */
    foreach ($result as $ops) {
      if (is_array($ops) && isset($ops[0]) && is_array($ops[0])) {
        $flatResult = array_merge($flatResult, $ops);
      }
      else {
        $flatResult[] = $ops;
      }
    }

    // Should have delete and create operations.
    $this->assertGreaterThan(0, count($flatResult));
    $hasDelete = FALSE;
    $hasCreate = FALSE;
    foreach ($flatResult as $op) {
      if (($op['operation'] ?? '') === 'delete_bundle') {
        $hasDelete = TRUE;
      }
      if (($op['operation'] ?? '') === 'create_bundle') {
        $hasCreate = TRUE;
      }
    }
    $this->assertTrue($hasDelete || $hasCreate, 'Should have delete or create operation');
  }

  /**
   * Tests field change detection.
   */
  public function testFieldChangeDetection(): void {
    // Create bundle and field.
    $bundle = NodeType::create([
      'type' => 'test_bundle',
      'name' => 'Test Bundle',
    ]);
    $bundle->save();

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

    $fieldConfig = FieldConfig::create([
      'field_storage' => $fieldStorage,
      'bundle' => 'test_bundle',
      'label' => 'Test Field',
    ]);
    $fieldConfig->save();

    // Try to update field with different label.
    $operations = [
      [
        'operation' => 'create_field',
        'entity_type' => 'node',
        'bundle' => 'test_bundle',
        'field_name' => 'field_test',
        'field_type' => 'string',
        'label' => 'Test Field Updated',
      ],
    ];

    $result = $this->changeDetector->detectChanges($operations, 'sync');

    // Should detect change and update.
    $this->assertCount(1, $result);
    $this->assertEquals('update_field', $result[0]['operation']);
    $this->assertEquals('Test Field Updated', $result[0]['label']);
  }

}
