<?php

namespace Drupal\Tests\eb\Traits;

use Drupal\eb\Result\ExecutionResult;
use Drupal\eb\Result\ValidationResult;

/**
 * Provides helper methods for testing Entity Builder operations.
 */
trait OperationTestTrait {

  /**
   * Creates operation data for a bundle creation.
   *
   * @param string $entityType
   *   The entity type (node, taxonomy_term, etc.).
   * @param string $bundleId
   *   The bundle machine name.
   * @param string $label
   *   The bundle label.
   * @param array<string, mixed> $extra
   *   Extra data to merge.
   *
   * @return array<string, mixed>
   *   The operation data.
   */
  protected function createBundleOperationData(
    string $entityType,
    string $bundleId,
    string $label,
    array $extra = [],
  ): array {
    return array_merge([
      'operation' => 'create_bundle',
      'entity_type' => $entityType,
      'bundle_id' => $bundleId,
      'label' => $label,
      'description' => "Test bundle: $label",
    ], $extra);
  }

  /**
   * Creates operation data for a field creation.
   *
   * @param string $entityType
   *   The entity type.
   * @param string $bundle
   *   The bundle.
   * @param string $fieldName
   *   The field machine name.
   * @param string $fieldType
   *   The field type (text, integer, entity_reference, etc.).
   * @param string $label
   *   The field label.
   * @param array<string, mixed> $extra
   *   Extra data to merge.
   *
   * @return array<string, mixed>
   *   The operation data.
   */
  protected function createFieldOperationData(
    string $entityType,
    string $bundle,
    string $fieldName,
    string $fieldType,
    string $label,
    array $extra = [],
  ): array {
    return array_merge([
      'operation' => 'create_field',
      'entity_type' => $entityType,
      'bundle' => $bundle,
      'field_name' => $fieldName,
      'field_type' => $fieldType,
      'label' => $label,
    ], $extra);
  }

  /**
   * Creates operation data for field update.
   *
   * @param string $entityType
   *   The entity type.
   * @param string $bundle
   *   The bundle.
   * @param string $fieldName
   *   The field name.
   * @param array<string, mixed> $updates
   *   The updates to apply.
   *
   * @return array<string, mixed>
   *   The operation data.
   */
  protected function updateFieldOperationData(
    string $entityType,
    string $bundle,
    string $fieldName,
    array $updates = [],
  ): array {
    return array_merge([
      'operation' => 'update_field',
      'entity_type' => $entityType,
      'bundle' => $bundle,
      'field_name' => $fieldName,
    ], $updates);
  }

  /**
   * Creates operation data for a role creation.
   *
   * @param string $roleId
   *   The role machine name.
   * @param string $label
   *   The role label.
   * @param array<string> $permissions
   *   The permissions to grant.
   *
   * @return array<string, mixed>
   *   The operation data.
   */
  protected function createRoleOperationData(
    string $roleId,
    string $label,
    array $permissions = [],
  ): array {
    return [
      'operation' => 'create_role',
      'role_id' => $roleId,
      'label' => $label,
      'permissions' => $permissions,
    ];
  }

  /**
   * Creates operation data for a menu creation.
   *
   * @param string $menuId
   *   The menu machine name.
   * @param string $label
   *   The menu label.
   * @param string $description
   *   The menu description.
   *
   * @return array<string, mixed>
   *   The operation data.
   */
  protected function createMenuOperationData(
    string $menuId,
    string $label,
    string $description = '',
  ): array {
    return [
      'operation' => 'create_menu',
      'menu_id' => $menuId,
      'label' => $label,
      'description' => $description ?: "Test menu: $label",
    ];
  }

  /**
   * Creates operation data for a menu link creation.
   *
   * @param string $menuId
   *   The parent menu ID.
   * @param string $title
   *   The link title.
   * @param string $uri
   *   The link URI.
   * @param array<string, mixed> $extra
   *   Extra data to merge.
   *
   * @return array<string, mixed>
   *   The operation data.
   */
  protected function createMenuLinkOperationData(
    string $menuId,
    string $title,
    string $uri,
    array $extra = [],
  ): array {
    return array_merge([
      'operation' => 'create_menu_link',
      'menu_id' => $menuId,
      'title' => $title,
      'link_uri' => $uri,
    ], $extra);
  }

  /**
   * Creates operation data for form mode configuration.
   *
   * @param string $entityType
   *   The entity type.
   * @param string $bundle
   *   The bundle.
   * @param string $mode
   *   The form mode (default, etc.).
   * @param array<string, mixed> $fieldSettings
   *   Field display settings.
   *
   * @return array<string, mixed>
   *   The operation data.
   */
  protected function configureFormModeOperationData(
    string $entityType,
    string $bundle,
    string $mode,
    array $fieldSettings = [],
  ): array {
    return [
      'operation' => 'configure_form_mode',
      'entity_type' => $entityType,
      'bundle' => $bundle,
      'mode' => $mode,
      'fields' => $fieldSettings,
    ];
  }

  /**
   * Creates operation data for view mode configuration.
   *
   * @param string $entityType
   *   The entity type.
   * @param string $bundle
   *   The bundle.
   * @param string $mode
   *   The view mode (default, teaser, etc.).
   * @param array<string, mixed> $fieldSettings
   *   Field display settings.
   *
   * @return array<string, mixed>
   *   The operation data.
   */
  protected function configureViewModeOperationData(
    string $entityType,
    string $bundle,
    string $mode,
    array $fieldSettings = [],
  ): array {
    return [
      'operation' => 'configure_view_mode',
      'entity_type' => $entityType,
      'bundle' => $bundle,
      'mode' => $mode,
      'fields' => $fieldSettings,
    ];
  }

  /**
   * Asserts that a validation result is valid (no errors).
   *
   * @param \Drupal\eb\Result\ValidationResult $result
   *   The validation result.
   * @param string $message
   *   Optional assertion message.
   */
  protected function assertValidationPasses(ValidationResult $result, string $message = ''): void {
    $message = $message ?: 'Validation should pass without errors.';
    $errors = $result->getErrors();
    $errorMessages = array_map(fn($e) => $e['message'] ?? 'Unknown error', $errors);
    $this->assertTrue($result->isValid(), $message . ' Errors: ' . implode(', ', $errorMessages));
  }

  /**
   * Asserts that a validation result is invalid (has errors).
   *
   * @param \Drupal\eb\Result\ValidationResult $result
   *   The validation result.
   * @param string $message
   *   Optional assertion message.
   */
  protected function assertValidationFails(ValidationResult $result, string $message = ''): void {
    $message = $message ?: 'Validation should fail with errors.';
    $this->assertFalse($result->isValid(), $message);
  }

  /**
   * Asserts that a validation result contains a specific error message.
   *
   * @param \Drupal\eb\Result\ValidationResult $result
   *   The validation result.
   * @param string $expectedMessage
   *   The expected error message (partial match).
   * @param string $message
   *   Optional assertion message.
   */
  protected function assertValidationError(ValidationResult $result, string $expectedMessage, string $message = ''): void {
    $message = $message ?: "Validation should contain error: $expectedMessage";
    $errors = $result->getErrors();

    $found = FALSE;
    foreach ($errors as $error) {
      $errorMessage = (string) ($error['message'] ?? '');
      if (str_contains($errorMessage, $expectedMessage)) {
        $found = TRUE;
        break;
      }
    }

    $this->assertTrue($found, $message);
  }

  /**
   * Asserts that an execution result is successful.
   *
   * @param \Drupal\eb\Result\ExecutionResult $result
   *   The execution result.
   * @param string $message
   *   Optional assertion message.
   */
  protected function assertExecutionSuccess(ExecutionResult $result, string $message = ''): void {
    $message = $message ?: 'Execution should succeed.';
    $errors = $result->getErrors();
    $this->assertTrue($result->isSuccess(), $message . ' Errors: ' . implode(', ', $errors));
  }

  /**
   * Asserts that an execution result failed.
   *
   * @param \Drupal\eb\Result\ExecutionResult $result
   *   The execution result.
   * @param string $message
   *   Optional assertion message.
   */
  protected function assertExecutionFailure(ExecutionResult $result, string $message = ''): void {
    $message = $message ?: 'Execution should fail.';
    $this->assertFalse($result->isSuccess(), $message);
  }

  /**
   * Asserts that an execution result contains rollback data.
   *
   * @param \Drupal\eb\Result\ExecutionResult $result
   *   The execution result.
   * @param string $message
   *   Optional assertion message.
   */
  protected function assertHasRollbackData(ExecutionResult $result, string $message = ''): void {
    $message = $message ?: 'Result should contain rollback data.';
    $this->assertNotEmpty($result->getRollbackData(), $message);
  }

}
