<?php

namespace Drupal\Tests\eb\Kernel\Operation;

use Drupal\Tests\eb\Kernel\EbKernelTestBase;

/**
 * Kernel tests for display configuration operations.
 *
 * Tests ConfigureFormModeOperation and ConfigureViewModeOperation
 * to ensure they properly validate and execute, especially in batch
 * contexts where bundles/fields are created in the same batch.
 *
 * @group eb
 */
class DisplayOperationsKernelTest extends EbKernelTestBase {

  /**
   * The operation builder service.
   *
   * @var \Drupal\eb\Service\OperationBuilder
   */
  protected $operationBuilder;

  /**
   * The validation manager service.
   *
   * @var \Drupal\eb\Service\ValidationManager
   */
  protected $validationManager;

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

    $this->operationBuilder = $this->container->get('eb.operation_builder');
    $this->validationManager = $this->container->get('eb.validation_manager');
  }

  /**
   * Tests ConfigureFormModeOperation validates only widget existence.
   *
   * The operation should NOT validate bundle/field existence in its own
   * validate() method - that's delegated to DependencyValidator which
   * has batch context awareness.
   *
   * @covers \Drupal\eb\Plugin\EbOperation\ConfigureFormModeOperation::validate
   */
  public function testConfigureFormModeValidatesWidgetOnly(): void {
    // Create operation with non-existent bundle and field but valid widget.
    $operations = $this->operationBuilder->buildBatch([
      [
        'operation' => 'configure_form_mode',
        'entity_type' => 'node',
        'bundle' => 'nonexistent_bundle',
        'field_name' => 'field_nonexistent',
        'widget' => 'string_textfield',
        'mode' => 'default',
      ],
    ]);

    $this->assertCount(1, $operations);
    $operation = reset($operations);

    // The operation's own validate() should pass because widget exists.
    // Bundle/field validation is done by DependencyValidator.
    $result = $operation->validate();

    // If this test fails, it means the operation is still validating
    // bundle/field existence, which breaks batch operations.
    $errors = $result->getErrors();
    $bundleErrors = array_filter($errors, fn($e) => str_contains($e['message'] ?? '', 'Bundle'));
    $fieldErrors = array_filter($errors, fn($e) => str_contains($e['message'] ?? '', 'Field'));

    $this->assertEmpty($bundleErrors, 'ConfigureFormModeOperation should not validate bundle existence.');
    $this->assertEmpty($fieldErrors, 'ConfigureFormModeOperation should not validate field existence.');
  }

  /**
   * Tests ConfigureFormModeOperation fails for invalid widget.
   */
  public function testConfigureFormModeFailsForInvalidWidget(): void {
    $operations = $this->operationBuilder->buildBatch([
      [
        'operation' => 'configure_form_mode',
        'entity_type' => 'node',
        'bundle' => 'article',
        'field_name' => 'field_test',
        'widget' => 'nonexistent_widget_that_does_not_exist',
        'mode' => 'default',
      ],
    ]);

    $operation = reset($operations);
    $result = $operation->validate();

    $this->assertFalse($result->isValid(), 'Should fail for invalid widget.');
    $errors = $result->getErrors();
    $widgetErrors = array_filter($errors, fn($e) => str_contains($e['message'] ?? '', 'Widget'));
    $this->assertNotEmpty($widgetErrors, 'Should have widget-related error.');
  }

  /**
   * Tests ConfigureViewModeOperation validates only formatter existence.
   *
   * @covers \Drupal\eb\Plugin\EbOperation\ConfigureViewModeOperation::validate
   */
  public function testConfigureViewModeValidatesFormatterOnly(): void {
    $operations = $this->operationBuilder->buildBatch([
      [
        'operation' => 'configure_view_mode',
        'entity_type' => 'node',
        'bundle' => 'nonexistent_bundle',
        'field_name' => 'field_nonexistent',
        'formatter' => 'string',
        'mode' => 'default',
      ],
    ]);

    $this->assertCount(1, $operations);
    $operation = reset($operations);

    $result = $operation->validate();
    $errors = $result->getErrors();

    $bundleErrors = array_filter($errors, fn($e) => str_contains($e['message'] ?? '', 'Bundle'));
    $fieldErrors = array_filter($errors, fn($e) => str_contains($e['message'] ?? '', 'Field'));

    $this->assertEmpty($bundleErrors, 'ConfigureViewModeOperation should not validate bundle existence.');
    $this->assertEmpty($fieldErrors, 'ConfigureViewModeOperation should not validate field existence.');
  }

  /**
   * Tests ConfigureViewModeOperation fails for invalid formatter.
   */
  public function testConfigureViewModeFailsForInvalidFormatter(): void {
    $operations = $this->operationBuilder->buildBatch([
      [
        'operation' => 'configure_view_mode',
        'entity_type' => 'node',
        'bundle' => 'article',
        'field_name' => 'field_test',
        'formatter' => 'nonexistent_formatter_xyz',
        'mode' => 'default',
      ],
    ]);

    $operation = reset($operations);
    $result = $operation->validate();

    $this->assertFalse($result->isValid(), 'Should fail for invalid formatter.');
    $errors = $result->getErrors();
    $formatterErrors = array_filter($errors, fn($e) => str_contains($e['message'] ?? '', 'Formatter'));
    $this->assertNotEmpty($formatterErrors, 'Should have formatter-related error.');
  }

  /**
   * Tests batch validation passes when bundle is created earlier.
   *
   * This tests the critical fix: display operations should validate
   * successfully when the bundle they reference will be created
   * earlier in the same batch.
   */
  public function testBatchValidationWithBundleCreatedEarlier(): void {
    $operations = $this->operationBuilder->buildBatch([
      // First: create the bundle.
      [
        'operation' => 'create_bundle',
        'entity_type' => 'node',
        'bundle_id' => 'batch_test_bundle',
        'label' => 'Batch Test Bundle',
      ],
      // Second: create a field on that bundle.
      [
        'operation' => 'create_field',
        'entity_type' => 'node',
        'bundle' => 'batch_test_bundle',
        'field_name' => 'field_batch_test',
        'field_type' => 'string',
        'label' => 'Batch Test Field',
      ],
      // Third: configure form display (references bundle from step 1).
      [
        'operation' => 'configure_form_mode',
        'entity_type' => 'node',
        'bundle' => 'batch_test_bundle',
        'field_name' => 'field_batch_test',
        'widget' => 'string_textfield',
        'mode' => 'default',
      ],
    ]);

    $this->assertCount(3, $operations);

    // Batch validation should pass.
    $result = $this->validationManager->validateBatch($operations);
    $this->assertTrue($result->isValid(), 'Batch should validate when bundle is created earlier: ' . implode(', ', array_map(fn($e) => $e['message'] ?? '', $result->getErrors())));
  }

  /**
   * Tests required fields validation for ConfigureFormModeOperation.
   */
  public function testConfigureFormModeRequiredFields(): void {
    // Missing widget.
    $operations = $this->operationBuilder->buildBatch([
      [
        'operation' => 'configure_form_mode',
        'entity_type' => 'node',
        'bundle' => 'article',
        'field_name' => 'field_test',
        // Missing 'widget'.
        'mode' => 'default',
      ],
    ]);

    $operation = reset($operations);
    $result = $operation->validate();

    $this->assertFalse($result->isValid());
    $errors = $result->getErrors();
    $widgetMissing = array_filter($errors, fn($e) => str_contains($e['message'] ?? '', 'widget'));
    $this->assertNotEmpty($widgetMissing, 'Should report missing widget.');
  }

  /**
   * Tests required fields validation for ConfigureViewModeOperation.
   */
  public function testConfigureViewModeRequiredFields(): void {
    // Missing formatter.
    $operations = $this->operationBuilder->buildBatch([
      [
        'operation' => 'configure_view_mode',
        'entity_type' => 'node',
        'bundle' => 'article',
        'field_name' => 'field_test',
        // Missing 'formatter'.
        'mode' => 'default',
      ],
    ]);

    $operation = reset($operations);
    $result = $operation->validate();

    $this->assertFalse($result->isValid());
    $errors = $result->getErrors();
    $formatterMissing = array_filter($errors, fn($e) => str_contains($e['message'] ?? '', 'formatter'));
    $this->assertNotEmpty($formatterMissing, 'Should report missing formatter.');
  }

  /**
   * Tests display operation execution with existing bundle and field.
   */
  public function testDisplayOperationExecutionWithExistingResources(): void {
    // Create a real bundle and field first.
    $this->createNodeType('display_exec_test', 'Display Exec Test');

    // Create a field on the bundle.
    $fieldOps = $this->operationBuilder->buildBatch([
      [
        'operation' => 'create_field',
        'entity_type' => 'node',
        'bundle' => 'display_exec_test',
        'field_name' => 'field_display_exec',
        'field_type' => 'string',
        'label' => 'Display Exec Field',
      ],
    ]);
    $fieldOp = reset($fieldOps);
    $fieldResult = $fieldOp->execute();
    $this->assertTrue($fieldResult->isSuccess(), 'Field should be created.');

    $this->clearCaches();

    // Now configure form display.
    $displayOps = $this->operationBuilder->buildBatch([
      [
        'operation' => 'configure_form_mode',
        'entity_type' => 'node',
        'bundle' => 'display_exec_test',
        'field_name' => 'field_display_exec',
        'widget' => 'string_textfield',
        'mode' => 'default',
        'weight' => 5,
      ],
    ]);

    $displayOp = reset($displayOps);

    // Validate should pass.
    $valResult = $displayOp->validate();
    $this->assertTrue($valResult->isValid(), 'Display operation should validate.');

    // Execute should succeed.
    $execResult = $displayOp->execute();
    $this->assertTrue($execResult->isSuccess(), 'Display operation should execute: ' . implode(', ', $execResult->getMessages()));
  }

}
