<?php

namespace Drupal\Tests\eb\Kernel\Integration;

use Drupal\eb\PluginInterfaces\PreviewableOperationInterface;
use Drupal\eb\Service\OperationBuilder;
use Drupal\eb\Service\OperationProcessor;
use Drupal\eb\Service\ValidationManager;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\Tests\eb\Kernel\EbKernelTestBase;
use Drupal\Tests\eb\Traits\FieldCreationTrait;
use Drupal\Tests\eb\Traits\OperationTestTrait;
use Drupal\Tests\eb\Traits\ValidationAssertionsTrait;

/**
 * Integration tests for complete entity builder workflows.
 *
 * @group eb
 */
class WorkflowIntegrationTest extends EbKernelTestBase {

  use FieldCreationTrait;
  use OperationTestTrait;
  use ValidationAssertionsTrait;

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

  /**
   * The operation processor service.
   */
  protected OperationProcessor $operationProcessor;

  /**
   * The validation manager service.
   */
  protected ValidationManager $validationManager;

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

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

  /**
   * Tests complete workflow: create bundle then create field.
   */
  public function testCreateBundleAndFieldWorkflow(): void {
    // Step 1: Create bundle.
    $bundleData = $this->createBundleOperationData('node', 'test_article', 'Test Article');
    $bundleOperation = $this->operationBuilder->buildOperation('create_bundle', $bundleData);

    $bundleValidation = $bundleOperation->validate();
    $this->assertValidationResultPasses($bundleValidation);

    $bundleResult = $this->operationProcessor->executeOperation($bundleOperation);
    $this->assertExecutionResultSuccess($bundleResult);
    $this->assertNodeTypeExists('test_article');

    // Step 2: Create field on the bundle.
    $fieldData = $this->createFieldOperationData(
      'node',
      'test_article',
      'field_subtitle',
      'text',
      'Subtitle'
    );
    $fieldOperation = $this->operationBuilder->buildOperation('create_field', $fieldData);

    $fieldValidation = $fieldOperation->validate();
    $this->assertValidationResultPasses($fieldValidation);

    $fieldResult = $this->operationProcessor->executeOperation($fieldOperation);
    $this->assertExecutionResultSuccess($fieldResult);
    $this->assertFieldConfigExists('node', 'test_article', 'field_subtitle');

    // Verify field is properly attached.
    $fieldConfig = FieldConfig::loadByName('node', 'test_article', 'field_subtitle');
    $this->assertNotNull($fieldConfig);
    $this->assertEquals('Subtitle', $fieldConfig->getLabel());
  }

  /**
   * Tests batch workflow: create bundle and fields in batch.
   */
  public function testBatchWorkflow(): void {
    $operations = [];

    // Bundle operation.
    $bundleData = $this->createBundleOperationData('node', 'batch_article', 'Batch Article');
    $operations[] = $this->operationBuilder->buildOperation('create_bundle', $bundleData);

    // Field operations.
    $fields = [
      ['name' => 'field_title_ext', 'type' => 'text', 'label' => 'Extended Title'],
      ['name' => 'field_summary', 'type' => 'text_long', 'label' => 'Summary'],
      ['name' => 'field_featured', 'type' => 'boolean', 'label' => 'Featured'],
    ];

    foreach ($fields as $field) {
      $fieldData = $this->createFieldOperationData(
        'node',
        'batch_article',
        $field['name'],
        $field['type'],
        $field['label']
      );
      $operations[] = $this->operationBuilder->buildOperation('create_field', $fieldData);
    }

    // Execute batch.
    $results = $this->operationProcessor->executeBatch($operations);

    // All should succeed.
    $this->assertCount(4, $results);
    foreach ($results as $result) {
      $this->assertTrue($result->isSuccess());
    }

    // Verify all entities created.
    $this->assertNodeTypeExists('batch_article');
    foreach ($fields as $field) {
      $this->assertFieldConfigExists('node', 'batch_article', $field['name']);
    }
  }

  /**
   * Tests validation fails early in workflow.
   */
  public function testValidationFailsEarlyWorkflow(): void {
    // Try to create field on non-existent bundle without creating bundle first.
    $fieldData = $this->createFieldOperationData(
      'node',
      'nonexistent_bundle',
      'field_test',
      'text',
      'Test Field'
    );

    $operation = $this->operationBuilder->buildOperation('create_field', $fieldData);

    // Use validationManager to get full validation including validator plugins.
    // Bundle existence is checked by DependencyValidator, not the operation.
    $validation = $this->validationManager->validateOperation($operation);

    // Validation should fail due to non-existent bundle.
    $this->assertValidationResultFails($validation);
  }

  /**
   * Tests update field workflow.
   */
  public function testUpdateFieldWorkflow(): void {
    // Setup: Create bundle and field.
    $this->createNodeType('update_test', 'Update Test');
    $this->createTextField('node', 'update_test', 'field_update_test', 'Original Label');

    // Verify original label.
    $originalField = FieldConfig::loadByName('node', 'update_test', 'field_update_test');
    $this->assertEquals('Original Label', $originalField->getLabel());

    // Update field.
    $updateData = $this->updateFieldOperationData(
      'node',
      'update_test',
      'field_update_test',
      [
        'label' => 'Updated Label',
        'required' => TRUE,
      ]
    );

    $operation = $this->operationBuilder->buildOperation('update_field', $updateData);
    $validation = $operation->validate();
    $this->assertValidationResultPasses($validation);

    $result = $this->operationProcessor->executeOperation($operation);
    $this->assertExecutionResultSuccess($result);

    // Verify update.
    \Drupal::entityTypeManager()->getStorage('field_config')->resetCache();
    $updatedField = FieldConfig::loadByName('node', 'update_test', 'field_update_test');
    $this->assertEquals('Updated Label', $updatedField->getLabel());
    $this->assertTrue($updatedField->isRequired());
  }

  /**
   * Tests delete field workflow.
   */
  public function testDeleteFieldWorkflow(): void {
    // Setup: Create bundle and field.
    $this->createNodeType('delete_test', 'Delete Test');
    $this->createTextField('node', 'delete_test', 'field_delete_test', 'Delete Me');

    $this->assertFieldConfigExists('node', 'delete_test', 'field_delete_test');

    // Delete field.
    $deleteData = [
      'operation' => 'delete_field',
      'entity_type' => 'node',
      'bundle' => 'delete_test',
      'field_name' => 'field_delete_test',
    ];

    $operation = $this->operationBuilder->buildOperation('delete_field', $deleteData);
    $validation = $operation->validate();
    $this->assertValidationResultPasses($validation);

    $result = $this->operationProcessor->executeOperation($operation);
    $this->assertExecutionResultSuccess($result);

    // Verify deletion.
    $this->assertFieldConfigNotExists('node', 'delete_test', 'field_delete_test');
  }

  /**
   * Tests workflow with multiple field types.
   */
  public function testMultipleFieldTypesWorkflow(): void {
    // Create bundle.
    $this->createNodeType('multi_fields', 'Multi Fields');

    $fieldSpecs = [
      ['name' => 'field_text', 'type' => 'text', 'label' => 'Text'],
      ['name' => 'field_long', 'type' => 'text_long', 'label' => 'Long Text'],
      ['name' => 'field_int', 'type' => 'integer', 'label' => 'Integer'],
      ['name' => 'field_bool', 'type' => 'boolean', 'label' => 'Boolean'],
      ['name' => 'field_link', 'type' => 'link', 'label' => 'Link'],
      ['name' => 'field_date', 'type' => 'datetime', 'label' => 'Date'],
    ];

    foreach ($fieldSpecs as $spec) {
      $fieldData = $this->createFieldOperationData(
        'node',
        'multi_fields',
        $spec['name'],
        $spec['type'],
        $spec['label']
      );

      $operation = $this->operationBuilder->buildOperation('create_field', $fieldData);
      $result = $this->operationProcessor->executeOperation($operation);
      $this->assertExecutionResultSuccess($result, "Field {$spec['name']} should be created successfully");
    }

    // Verify all fields exist with correct types.
    foreach ($fieldSpecs as $spec) {
      $storage = FieldStorageConfig::loadByName('node', $spec['name']);
      $this->assertNotNull($storage, "Storage for {$spec['name']} should exist");
      $this->assertEquals($spec['type'], $storage->getType());
    }
  }

  /**
   * Tests rollback data is stored correctly.
   */
  public function testRollbackDataStored(): void {
    // Create bundle.
    $bundleData = $this->createBundleOperationData('node', 'rollback_test', 'Rollback Test');
    $operation = $this->operationBuilder->buildOperation('create_bundle', $bundleData);
    $result = $this->operationProcessor->executeOperation($operation);

    $this->assertExecutionResultSuccess($result);
    $this->assertExecutionHasRollbackData($result);

    $rollbackData = $result->getRollbackData();
    $this->assertArrayHasKey('bundle_entity_type', $rollbackData);
    $this->assertArrayHasKey('bundle_id', $rollbackData);
    $this->assertEquals('rollback_test', $rollbackData['bundle_id']);
  }

  /**
   * Tests validation batch context.
   */
  public function testValidationBatchContext(): void {
    // Validate batch where field depends on bundle being created.
    $operations = [];

    $bundleData = $this->createBundleOperationData('node', 'context_test', 'Context Test');
    $operations[] = $this->operationBuilder->buildOperation('create_bundle', $bundleData);

    $fieldData = $this->createFieldOperationData(
      'node',
      'context_test',
      'field_context_test',
      'text',
      'Context Test Field'
    );
    $operations[] = $this->operationBuilder->buildOperation('create_field', $fieldData);

    // Validate as batch.
    $batchResult = $this->validationManager->validateBatch($operations);

    // Both should pass when validated as batch (field depends on bundle).
    $this->assertTrue($batchResult->isValid());
  }

  /**
   * Tests preview before execution.
   */
  public function testPreviewBeforeExecution(): void {
    $bundleData = $this->createBundleOperationData('node', 'preview_test', 'Preview Test');
    $operation = $this->operationBuilder->buildOperation('create_bundle', $bundleData);

    // Get preview.
    $this->assertInstanceOf(PreviewableOperationInterface::class, $operation);
    $preview = $operation->preview();

    $this->assertPreviewHasOperations($preview);
    $this->assertPreviewHasOperationType($preview, 'create');

    // Details are added to the description via addDetails().
    $description = $preview->getDescription();
    $this->assertStringContainsString('Entity Type: node', $description);
    $this->assertStringContainsString('Bundle ID: preview_test', $description);

    // Node type should NOT exist yet.
    $this->assertNodeTypeNotExists('preview_test');
  }

}
