<?php

namespace Drupal\Tests\eb\Kernel\Form;

use Drupal\eb\Entity\EbDefinition;
use Drupal\eb\Service\OperationBuilder;
use Drupal\eb\Service\ValidationManager;
use Drupal\Tests\eb\Kernel\EbKernelTestBase;

/**
 * Kernel tests for EbDefinitionApplyForm functionality.
 *
 * Tests the buildOperationData() method and batch validation integration
 * to ensure definitions with bundles, fields, and display configurations
 * can be properly validated and applied.
 *
 * @group eb
 */
class DefinitionApplyFormTest extends EbKernelTestBase {

  /**
   * The operation builder service.
   */
  protected OperationBuilder $operationBuilder;

  /**
   * The validation manager service.
   */
  protected ValidationManager $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 that display operations are created per-field.
   *
   * Tests that display_field_definitions (flat format) creates one operation
   * per field configuration.
   *
   * @covers \Drupal\eb\Form\EbDefinitionApplyForm::buildOperationData
   */
  public function testDisplayOperationsCreatedPerField(): void {
    $definition = EbDefinition::create([
      'id' => 'display_test',
      'label' => 'Display Test',
      'target_entity_type' => 'node',
      'bundle_definitions' => [
        [
          'bundle_id' => 'test_bundle',
          'label' => 'Test Bundle',
          'entity_type' => 'node',
        ],
      ],
      'field_definitions' => [
        [
          'field_name' => 'field_one',
          'field_type' => 'string',
          'label' => 'Field One',
          'bundle' => 'test_bundle',
          'entity_type' => 'node',
        ],
        [
          'field_name' => 'field_two',
          'field_type' => 'string',
          'label' => 'Field Two',
          'bundle' => 'test_bundle',
          'entity_type' => 'node',
        ],
      ],
      // Flat format: one entry per field per display mode.
      'display_field_definitions' => [
        [
          'entity_type' => 'node',
          'bundle' => 'test_bundle',
          'display_type' => 'form',
          'mode' => 'default',
          'field_name' => 'field_one',
          'widget' => 'string_textfield',
          'weight' => 0,
        ],
        [
          'entity_type' => 'node',
          'bundle' => 'test_bundle',
          'display_type' => 'form',
          'mode' => 'default',
          'field_name' => 'field_two',
          'widget' => 'string_textfield',
          'weight' => 1,
        ],
      ],
    ]);
    $definition->save();

    // Build operation data using the same logic as the form.
    $operations = $this->buildOperationData($definition);

    // Count display operations - should be 2 (one per field).
    $displayOps = array_filter($operations, fn($op) => $op['operation'] === 'configure_form_mode');
    $this->assertCount(2, $displayOps, 'Should create one display operation per field.');

    // Verify each operation has field_name and widget.
    foreach ($displayOps as $op) {
      $this->assertArrayHasKey('field_name', $op, 'Display operation should have field_name.');
      $this->assertArrayHasKey('widget', $op, 'Form display operation should have widget.');
      $this->assertNotEmpty($op['field_name'], 'field_name should not be empty.');
      $this->assertNotEmpty($op['widget'], 'widget should not be empty.');
    }
  }

  /**
   * Tests view mode operations have formatter instead of widget.
   */
  public function testViewModeOperationsHaveFormatter(): void {
    $definition = EbDefinition::create([
      'id' => 'view_mode_test',
      'label' => 'View Mode Test',
      'target_entity_type' => 'node',
      'bundle_definitions' => [
        [
          'bundle_id' => 'view_bundle',
          'label' => 'View Bundle',
          'entity_type' => 'node',
        ],
      ],
      'field_definitions' => [
        [
          'field_name' => 'field_view_test',
          'field_type' => 'string',
          'label' => 'View Test Field',
          'bundle' => 'view_bundle',
          'entity_type' => 'node',
        ],
      ],
      // Flat format for view display.
      'display_field_definitions' => [
        [
          'entity_type' => 'node',
          'bundle' => 'view_bundle',
          'display_type' => 'view',
          'mode' => 'default',
          'field_name' => 'field_view_test',
          'formatter' => 'string',
          'weight' => 0,
          'label_display' => 'above',
        ],
      ],
    ]);
    $definition->save();

    $operations = $this->buildOperationData($definition);
    $viewOps = array_filter($operations, fn($op) => $op['operation'] === 'configure_view_mode');

    $this->assertCount(1, $viewOps, 'Should have one view mode operation.');

    $viewOp = reset($viewOps);
    $this->assertArrayHasKey('formatter', $viewOp, 'View display operation should have formatter.');
    $this->assertArrayNotHasKey('widget', $viewOp, 'View display operation should not have widget.');
    $this->assertEquals('string', $viewOp['formatter']);
  }

  /**
   * Tests batch validation passes for operations with dependencies.
   *
   * This tests the fix where display operations reference bundles/fields
   * that will be created earlier in the same batch. The batch validation
   * should pass because the DependencyValidator has context awareness.
   *
   * @covers \Drupal\eb\Service\ValidationManager::validateBatch
   */
  public function testBatchValidationWithDependencies(): void {
    $definition = EbDefinition::create([
      'id' => 'batch_validation_test',
      'label' => 'Batch Validation Test',
      'target_entity_type' => 'node',
      'bundle_definitions' => [
        [
          'bundle_id' => 'batch_bundle',
          'label' => 'Batch Bundle',
          'entity_type' => 'node',
        ],
      ],
      'field_definitions' => [
        [
          'field_name' => 'field_batch_test',
          'field_type' => 'string',
          'label' => 'Batch Test Field',
          'bundle' => 'batch_bundle',
          'entity_type' => 'node',
        ],
      ],
      // Flat format for display.
      'display_field_definitions' => [
        [
          'entity_type' => 'node',
          'bundle' => 'batch_bundle',
          'display_type' => 'form',
          'mode' => 'default',
          'field_name' => 'field_batch_test',
          'widget' => 'string_textfield',
          'weight' => 0,
        ],
      ],
    ]);
    $definition->save();

    // Build operation data and objects.
    $operationData = $this->buildOperationData($definition);
    $operationObjects = $this->operationBuilder->buildBatch($operationData);

    // Verify we have operations: 1 bundle + 1 field + 1 display = 3.
    $this->assertCount(3, $operationObjects, 'Should have 3 operations.');

    // Batch validation should pass even though bundle/field don't exist yet.
    $result = $this->validationManager->validateBatch($operationObjects);
    $this->assertTrue($result->isValid(), 'Batch validation should pass: ' . implode(', ', array_map(fn($e) => $e['message'] ?? '', $result->getErrors())));
  }

  /**
   * Tests that field operations use correct keys (field_name, field_type).
   *
   * This tests the fix for the bug where the controller and form used
   * different keys (machine_name vs field_name, type vs field_type).
   */
  public function testFieldOperationsUseCorrectKeys(): void {
    $definition = EbDefinition::create([
      'id' => 'field_keys_test',
      'label' => 'Field Keys Test',
      'target_entity_type' => 'node',
      'bundle_definitions' => [
        [
          'bundle_id' => 'keys_bundle',
          'label' => 'Keys Bundle',
          'entity_type' => 'node',
        ],
      ],
      'field_definitions' => [
        [
          'field_name' => 'field_test_keys',
          'field_type' => 'string',
          'label' => 'Test Keys',
          'bundle' => 'keys_bundle',
          'entity_type' => 'node',
          'required' => TRUE,
        ],
      ],
    ]);
    $definition->save();

    $operations = $this->buildOperationData($definition);
    $fieldOps = array_filter($operations, fn($op) => $op['operation'] === 'create_field');

    $this->assertCount(1, $fieldOps);
    $fieldOp = reset($fieldOps);

    // Verify correct keys are used.
    $this->assertArrayHasKey('field_name', $fieldOp);
    $this->assertArrayHasKey('field_type', $fieldOp);
    $this->assertEquals('field_test_keys', $fieldOp['field_name']);
    $this->assertEquals('string', $fieldOp['field_type']);

    // Verify old/incorrect keys are NOT used.
    $this->assertArrayNotHasKey('machine_name', $fieldOp);
    $this->assertArrayNotHasKey('type', $fieldOp);
  }

  /**
   * Tests that bundle operations use correct keys (bundle_id).
   */
  public function testBundleOperationsUseCorrectKeys(): void {
    $definition = EbDefinition::create([
      'id' => 'bundle_keys_test',
      'label' => 'Bundle Keys Test',
      'target_entity_type' => 'node',
      'bundle_definitions' => [
        [
          'bundle_id' => 'correct_bundle',
          'label' => 'Correct Bundle',
          'entity_type' => 'node',
          'description' => 'Test description',
        ],
      ],
    ]);
    $definition->save();

    $operations = $this->buildOperationData($definition);
    $bundleOps = array_filter($operations, fn($op) => $op['operation'] === 'create_bundle');

    $this->assertCount(1, $bundleOps);
    $bundleOp = reset($bundleOps);

    $this->assertArrayHasKey('bundle_id', $bundleOp);
    $this->assertEquals('correct_bundle', $bundleOp['bundle_id']);
    $this->assertArrayNotHasKey('machine_name', $bundleOp);
  }

  /**
   * Tests that menu operations use correct keys (menu_id).
   */
  public function testMenuOperationsUseCorrectKeys(): void {
    $definition = EbDefinition::create([
      'id' => 'menu_keys_test',
      'label' => 'Menu Keys Test',
      'target_entity_type' => 'node',
      'menu_definitions' => [
        [
          'menu_id' => 'test_menu',
          'label' => 'Test Menu',
          'description' => 'A test menu',
        ],
      ],
    ]);
    $definition->save();

    $operations = $this->buildOperationData($definition);
    $menuOps = array_filter($operations, fn($op) => $op['operation'] === 'create_menu');

    $this->assertCount(1, $menuOps);
    $menuOp = reset($menuOps);

    $this->assertArrayHasKey('menu_id', $menuOp);
    $this->assertEquals('test_menu', $menuOp['menu_id']);
    $this->assertArrayNotHasKey('machine_name', $menuOp);
  }

  /**
   * Tests complete definition apply with all operation types.
   *
   * This is an integration test that verifies a full definition with
   * bundles, fields, displays, roles, and menus can be built and validated.
   */
  public function testCompleteDefinitionOperationBuilding(): void {
    $definition = EbDefinition::create([
      'id' => 'complete_test',
      'label' => 'Complete Test',
      'target_entity_type' => 'node',
      'bundle_definitions' => [
        [
          'bundle_id' => 'complete_bundle',
          'label' => 'Complete Bundle',
          'entity_type' => 'node',
        ],
      ],
      'field_definitions' => [
        [
          'field_name' => 'field_complete_one',
          'field_type' => 'string',
          'label' => 'Complete One',
          'bundle' => 'complete_bundle',
          'entity_type' => 'node',
        ],
        [
          'field_name' => 'field_complete_two',
          'field_type' => 'text_long',
          'label' => 'Complete Two',
          'bundle' => 'complete_bundle',
          'entity_type' => 'node',
        ],
      ],
      // Flat format: one entry per field per display mode.
      'display_field_definitions' => [
        // Form display fields.
        [
          'entity_type' => 'node',
          'bundle' => 'complete_bundle',
          'display_type' => 'form',
          'mode' => 'default',
          'field_name' => 'field_complete_one',
          'widget' => 'string_textfield',
          'weight' => 0,
        ],
        [
          'entity_type' => 'node',
          'bundle' => 'complete_bundle',
          'display_type' => 'form',
          'mode' => 'default',
          'field_name' => 'field_complete_two',
          'widget' => 'text_textarea',
          'weight' => 1,
        ],
        // View display fields.
        [
          'entity_type' => 'node',
          'bundle' => 'complete_bundle',
          'display_type' => 'view',
          'mode' => 'default',
          'field_name' => 'field_complete_one',
          'formatter' => 'string',
          'weight' => 0,
        ],
        [
          'entity_type' => 'node',
          'bundle' => 'complete_bundle',
          'display_type' => 'view',
          'mode' => 'default',
          'field_name' => 'field_complete_two',
          'formatter' => 'text_default',
          'weight' => 1,
        ],
      ],
      'menu_definitions' => [
        [
          'menu_id' => 'complete_menu',
          'label' => 'Complete Menu',
        ],
      ],
    ]);
    $definition->save();

    $operations = $this->buildOperationData($definition);

    // Expected: 1 bundle + 2 fields + 2 form + 2 view displays + 1 menu = 8.
    $this->assertCount(8, $operations, 'Should have 8 operations total.');

    // Verify operation types.
    $opTypes = array_count_values(array_column($operations, 'operation'));
    $this->assertEquals(1, $opTypes['create_bundle'] ?? 0);
    $this->assertEquals(2, $opTypes['create_field'] ?? 0);
    $this->assertEquals(2, $opTypes['configure_form_mode'] ?? 0);
    $this->assertEquals(2, $opTypes['configure_view_mode'] ?? 0);
    $this->assertEquals(1, $opTypes['create_menu'] ?? 0);

    // Build and validate.
    $operationObjects = $this->operationBuilder->buildBatch($operations);
    $result = $this->validationManager->validateBatch($operationObjects);
    $this->assertTrue($result->isValid(), 'Complete definition should validate: ' . implode(', ', array_map(fn($e) => $e['message'] ?? '', $result->getErrors())));
  }

  /**
   * Replicates the buildOperationData() method from the form.
   *
   * This is a direct copy of the logic to test it without
   * instantiating the full form.
   *
   * @param \Drupal\eb\Entity\EbDefinition $definition
   *   The definition entity.
   *
   * @return array<int, array<string, mixed>>
   *   Array of operation data arrays.
   */
  protected function buildOperationData(EbDefinition $definition): array {
    $operations = [];

    // Build bundle operations.
    foreach ($definition->getBundleDefinitions() as $bundle_data) {
      $operations[] = [
        'operation' => 'create_bundle',
        'entity_type' => $bundle_data['entity_type'] ?? '',
        'bundle_id' => $bundle_data['bundle_id'] ?? $bundle_data['machine_name'] ?? '',
        'label' => $bundle_data['label'] ?? '',
        'description' => $bundle_data['description'] ?? '',
        'settings' => $bundle_data['settings'] ?? [],
      ];
    }

    // Build field operations.
    foreach ($definition->getFieldDefinitions() as $field_data) {
      $operation_data = [
        'operation' => 'create_field',
        'entity_type' => $field_data['entity_type'] ?? '',
        'bundle' => $field_data['bundle'] ?? '',
        'field_name' => $field_data['field_name'] ?? $field_data['machine_name'] ?? '',
        'label' => $field_data['label'] ?? '',
        'field_type' => $field_data['field_type'] ?? $field_data['type'] ?? '',
        'required' => $field_data['required'] ?? FALSE,
        'translatable' => $field_data['translatable'] ?? FALSE,
        'cardinality' => $field_data['cardinality'] ?? 1,
        'description' => $field_data['description'] ?? '',
      ];

      if (!empty($field_data['widget'])) {
        $operation_data['widget'] = $field_data['widget'];
      }
      if (!empty($field_data['formatter'])) {
        $operation_data['formatter'] = $field_data['formatter'];
      }
      if (!empty($field_data['widget_settings'])) {
        $operation_data['widget_settings'] = $field_data['widget_settings'];
      }
      if (!empty($field_data['formatter_settings'])) {
        $operation_data['formatter_settings'] = $field_data['formatter_settings'];
      }
      if (!empty($field_data['field_storage_settings'])) {
        $operation_data['field_storage_settings'] = $field_data['field_storage_settings'];
      }
      if (!empty($field_data['field_config_settings'])) {
        $operation_data['field_config_settings'] = $field_data['field_config_settings'];
      }
      if (!empty($field_data['default_value'])) {
        $operation_data['default_value'] = $field_data['default_value'];
      }

      $settings = [];
      if (!empty($field_data['field_settings'])) {
        $settings = array_merge($settings, $field_data['field_settings']);
      }
      if (!empty($field_data['instance_settings'])) {
        $settings = array_merge($settings, $field_data['instance_settings']);
      }
      if (!empty($settings)) {
        $operation_data['settings'] = $settings;
      }

      $operations[] = $operation_data;
    }

    // Build display operations - one per display field definition row.
    foreach ($definition->getDisplayFieldDefinitions() as $display_field) {
      $display_type = $display_field['display_type'] ?? 'form';
      $operation_id = $display_type === 'form' ? 'configure_form_mode' : 'configure_view_mode';

      $operation_data = [
        'operation' => $operation_id,
        'entity_type' => $display_field['entity_type'] ?? '',
        'bundle' => $display_field['bundle'] ?? '',
        'mode' => $display_field['mode'] ?? 'default',
        'field_name' => $display_field['field_name'] ?? '',
      ];

      if ($display_type === 'form') {
        $operation_data['widget'] = $display_field['widget'] ?? '';
        if (!empty($display_field['widget_settings'])) {
          $operation_data['widget_settings'] = $display_field['widget_settings'];
        }
      }
      else {
        $operation_data['formatter'] = $display_field['formatter'] ?? '';
        if (!empty($display_field['formatter_settings'])) {
          $operation_data['formatter_settings'] = $display_field['formatter_settings'];
        }
        if (isset($display_field['label_display'])) {
          $operation_data['label_display'] = $display_field['label_display'];
        }
      }

      if (isset($display_field['weight'])) {
        $operation_data['weight'] = $display_field['weight'];
      }

      if (!empty($display_field['group'])) {
        $operation_data['group'] = $display_field['group'];
      }

      $operations[] = $operation_data;
    }

    // Build menu operations.
    foreach ($definition->getMenuDefinitions() as $menu_data) {
      $operations[] = [
        'operation' => 'create_menu',
        'menu_id' => $menu_data['menu_id'] ?? $menu_data['machine_name'] ?? '',
        'label' => $menu_data['label'] ?? '',
        'description' => $menu_data['description'] ?? '',
        'settings' => $menu_data['settings'] ?? [],
      ];
    }

    return $operations;
  }

}
