<?php

declare(strict_types=1);

namespace Drupal\Tests\acquiadam_asset_import\FunctionalJavascript;

use Drupal\Tests\acquia_dam\FunctionalJavascript\AcquiaDamWebDriverTestBase;
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;

// Workaround to support tests against both Drupal 10.1 and Drupal 11.0.
// @todo Remove once we depend on Drupal 10.2.
if (!trait_exists(EntityReferenceFieldCreationTrait::class)) {
  class_alias('\Drupal\Tests\field\Traits\EntityReferenceTestTrait', EntityReferenceFieldCreationTrait::class);
}

/**
 * Tests config form submission.
 *
 * @tests \Drupal\acquiadam_asset_import\Form\BulkImportConfigForm
 *
 * @group acquia_dam
 */
final class BulkImportConfigFormSubmitTest extends AcquiaDamWebDriverTestBase {

  use EntityReferenceFieldCreationTrait;

  /**
   * {@inheritdoc}
   */
  protected $defaultTheme = 'starterkit_theme';

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'acquia_dam',
    'acquiadam_asset_import',
  ];

  /**
   * Media bundle selectors for testing.
   */
  private const MEDIA_BUNDLE_SELECTORS = [
    'audio' => '[data-drupal-selector="edit-media-bundles-acquia-dam-audio-asset"]',
    'documents' => '[data-drupal-selector="edit-media-bundles-acquia-dam-documents-asset"]',
    'generic' => '[data-drupal-selector="edit-media-bundles-acquia-dam-generic-asset"]',
    'image' => '[data-drupal-selector="edit-media-bundles-acquia-dam-image-asset"]',
    'pdf' => '[data-drupal-selector="edit-media-bundles-acquia-dam-pdf-asset"]',
    'spinset' => '[data-drupal-selector="edit-media-bundles-acquia-dam-spinset-asset"]',
    'video' => '[data-drupal-selector="edit-media-bundles-acquia-dam-video-asset"]',
  ];

  /**
   * Tests that the form can be submitted.
   *
   * @param string $source_type_id
   *   The source type id.
   * @param string $source_type_name
   *   The source type name.
   * @param string $name
   *   The name of the option to select.
   * @param array $expected_Selectors
   *   The list of media types to select.
   *
   * @throws \Behat\Mink\Exception\ElementNotFoundException
   * @throws \Drupal\Core\Entity\EntityStorageException|\Behat\Mink\Exception\ResponseTextException
   *
   * @dataProvider categorySelectionProvider
   */
  public function testFormSubmit(string $source_type_id, string $source_type_name, string $name, array $expected_Selectors): void {
    $this->setupUserAndTokens();
    $this->drupalGet('/admin/config/acquia-dam/bulk-import');

    $assert_session = $this->assertSession();
    $this->verifyInitialFormState($assert_session);

    // Test initial category selection and table operations
    $this->selectSourceTypeAndAsset($source_type_id, $source_type_name, $name, $assert_session);
    $this->testTableOperations($source_type_name, $name, $assert_session);

    // Test filtering with subset of media types
    $this->selectSourceTypeAndAsset($source_type_id, $source_type_name, $name, $assert_session);
    $this->enableFilteringAndSelectMediaTypes(['documents', 'image', 'pdf'], $assert_session);
    $this->addAndSaveConfiguration($assert_session);

    // Test updating with all media types
    $this->selectSourceTypeAndAsset($source_type_id, $source_type_name, $name, $assert_session);
    $this->verifyExistingSelections(['documents', 'image', 'pdf'], $assert_session);
    $this->selectAdditionalMediaTypes(['spinset', 'video'], $assert_session);
    $this->updateAndSaveConfiguration($assert_session);

    // Final verification of all selections
    $this->selectSourceTypeAndAsset($source_type_id, $source_type_name, $name, $assert_session);
    $this->verifyAllSelectionsChecked($expected_Selectors, $assert_session);
  }

  /**
   * Sets up user authentication and tokens.
   */
  private function setupUserAndTokens(): void {
    $user = $this->createUser(['administer site configuration']);
    $this->drupalLogin($user);
    $this->grantSiteRegistrationToken();
    $this->grantCurrentUserDamToken();
  }

  /**
   * Verifies the initial state of the form.
   */
  private function verifyInitialFormState($assert_session): void {
    $assert_session->pageTextContains('Acquia DAM bulk import');
    $assert_session->pageTextContains('Assets to import from');
    $assert_session->selectExists('source_type');
  }

  /**
   * Selects source type and asset/category.
   */
  private function selectSourceTypeAndAsset(string $source_type_id, string $source_type_name, string $name, $assert_session): void {
    $source_select_list = $assert_session->selectExists('source_type');
    $source_select_list->selectOption($source_type_name);
    $assert_session->assertWaitOnAjaxRequest();

    $this->selectAssetOrCategory($source_type_id, $name, $assert_session);
    $assert_session->assertWaitOnAjaxRequest();

    $assert_session->pageTextContains("List of $source_type_name in the remote DAM system available for the authorized user account. Please choose which of them the media assets should be imported from.");
  }

  /**
   * Selects asset from dropdown or category from input field.
   */
  private function selectAssetOrCategory(string $source_type_id, string $name, $assert_session): void {
    if ($source_type_id !== 'categories') {
      $asset_select_list = $assert_session->selectExists("{$source_type_id}_uuid");
      $asset_select_list->selectOption($name);
    } else {
      $this->selectCategoryFromDropdown($name, $assert_session);
    }
  }

  /**
   * Handles category selection from dropdown interface.
   */
  private function selectCategoryFromDropdown(string $name, $assert_session): void {
    $asset_select_list = $assert_session->elementExists('css', '[placeholder="Select category"]');
    $asset_select_list->click();

    $assert_session->waitForElementVisible('css', '.widen-category-menu:not([hidden])');
    $assert_session->elementExists('css', 'li[data-crumbs="' . $name . '"] button')->click();

    $this->assertEquals($name, $assert_session->hiddenFieldExists('categories_uuid')->getValue());
    sleep(2); // Wait for AJAX completion
  }

  /**
   * Tests table operations (add/remove).
   */
  private function testTableOperations(string $source_type_name, string $name, $assert_session): void {
    $assert_session->elementExists('css', '#selected-data-table');
    $assert_session->pageTextContains('No category or asset group has been selected yet.');

    // Enable filtering and add to table
    $assert_session->elementExists('css', '[data-drupal-selector="edit-enable-filter"]')->check();
    $assert_session->pageTextContains('Import only assets which would be assigned to these media types');
    $assert_session->buttonExists('+ Add')->click();
    $assert_session->assertWaitOnAjaxRequest();

    // Verify table content
    $this->verifyTableContent($source_type_name, $name, $assert_session);
    $assert_session->buttonExists('Save')->click();

    // Test remove functionality
    $assert_session->buttonExists('Remove')->click();
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->pageTextContains('No category or asset group has been selected yet.');
    $assert_session->buttonExists('Save')->click();
  }

  /**
   * Verifies table content after adding an entry.
   */
  private function verifyTableContent(string $source_type_name, string $name, $assert_session): void {
    $assert_session->elementTextContains('css', 'table#selected-data-table > tbody > tr:nth-child(1) > td:nth-child(1)', $source_type_name);
    $assert_session->elementTextContains('css', 'table#selected-data-table > tbody > tr:nth-child(1) > td:nth-child(2)', $name);
    $assert_session->elementTextContains('css', 'table#selected-data-table > tbody > tr:nth-child(1) > td:nth-child(3)', 'All assets (no filtering)');
  }

  /**
   * Enables filtering and selects specified media types.
   */
  private function enableFilteringAndSelectMediaTypes(array $media_types, $assert_session): void {
    $assert_session->elementExists('css', '[data-drupal-selector="edit-enable-filter"]')->check();

    foreach ($media_types as $type) {
      if (isset(self::MEDIA_BUNDLE_SELECTORS[$type])) {
        $assert_session->elementExists('css', self::MEDIA_BUNDLE_SELECTORS[$type])->check();
      }
    }
  }

  /**
   * Selects additional media types.
   */
  private function selectAdditionalMediaTypes(array $media_types, $assert_session): void {
    foreach ($media_types as $type) {
      if (isset(self::MEDIA_BUNDLE_SELECTORS[$type])) {
        $assert_session->elementExists('css', self::MEDIA_BUNDLE_SELECTORS[$type])->check();
      }
    }
  }

  /**
   * Verifies that specified media types are already selected.
   */
  private function verifyExistingSelections(array $media_types, $assert_session): void {
    foreach ($media_types as $type) {
      if (isset(self::MEDIA_BUNDLE_SELECTORS[$type])) {
        $this->assertTrue($assert_session->elementExists('css', self::MEDIA_BUNDLE_SELECTORS[$type])->isChecked());
      }
    }
  }

  /**
   * Adds configuration and saves.
   */
  private function addAndSaveConfiguration($assert_session): void {
    $assert_session->buttonExists('+ Add')->click();
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->buttonExists('Save')->click();
    $assert_session->pageTextContains('The configuration settings have been successfully saved.');
  }

  /**
   * Updates configuration and saves.
   */
  private function updateAndSaveConfiguration($assert_session): void {
    $assert_session->buttonExists('Update')->click();
    $assert_session->assertWaitOnAjaxRequest();
    $assert_session->buttonExists('Save')->click();
  }

  /**
   * Verifies all expected selectors are checked.
   */
  private function verifyAllSelectionsChecked(array $expected_selectors, $assert_session): void {
    foreach ($expected_selectors as $selector) {
      $this->assertTrue($assert_session->elementExists('css', $selector)->isChecked());
    }
  }

  /**
   * Data provider for testFormSubmit.
   */
  public static function categorySelectionProvider(): array {
    return [
      [
        'categories',
        'Categories',
        'Testing',
        [
          '[data-drupal-selector="edit-enable-filter"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-documents-asset"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-image-asset"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-pdf-asset"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-spinset-asset"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-video-asset"]',
        ],
      ],
      [
        'asset_groups',
        'Asset Groups',
        'WidenQA',
        [
          '[data-drupal-selector="edit-enable-filter"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-documents-asset"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-image-asset"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-pdf-asset"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-spinset-asset"]',
          '[data-drupal-selector="edit-media-bundles-acquia-dam-video-asset"]',
        ],
      ],
    ];
  }

}
