<?php

declare(strict_types=1);

namespace Drupal\Tests\acquia_dam\FunctionalJavascript;

use Drupal\Tests\ckeditor5\Traits\CKEditor5TestTrait;
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
use Drupal\Tests\media\Traits\MediaTypeCreationTrait;
use Drupal\Tests\TestFileCreationTrait;

// 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 the Acquia DAM configuration form with key-based authentication.
 *
 * @group acquia_dam_functional_javascript_integration
 *
 * @see \Drupal\Tests\media_library\FunctionalJavascript\MediaLibraryTestBase
 * @see \Drupal\Tests\media_library\FunctionalJavascript\CKEditorIntegrationTest
 */
class AcquiaDamKeyIntegrationTest extends AcquiaDamWebDriverTestBase {

  use CKEditor5TestTrait;
  use TestFileCreationTrait;
  use MediaTypeCreationTrait;
  use EntityReferenceFieldCreationTrait;

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'acquia_dam',
    'acquia_dam_test',
    'key',
    'block',
    'ckeditor5',
    'node',
    'field_ui',
    'dblog',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    // @todo temporarily skip this test due to unresolved key authentication
    // issue. This test will remain disabled until the underlying
    // authentication mechanism is fixed.
    // @see: https://www.drupal.org/project/acquia_dam/issues/3558564
    $this->markTestSkipped('Mark test skip until unresolved key authentication auth issue(#3558564) gets fixed.');
    parent::setUp();
    // Create a user with necessary permissions.
    $user = $this->drupalCreateUser([
      'administer site configuration',
      'administer keys',
      'access administration pages',
      'access content',
      'administer media',
      'create media',
      'view media',
      'use text format test_format',
      'access media overview',
      'create page content',
    ]);
    // Login the user for all tests.
    $this->drupalLogin($user);
    $this->grantCurrentUserDamToken();
  }

  /**
   * Test form field states and visibility with JavaScript.
   *
   * @dataProvider formFieldStatesProvider
   */
  public function testFormFieldStates(string $auth_type, bool $domain_visible, bool $key_id_visible, bool $access_token_visible): void {
    // Visit the configuration form
    $this->drupalGet('/admin/config/acquia-dam');
    $assert_session = $this->assertSession();
    $page = $this->getSession()->getPage();

    // Verify initial field visibility
    $assert_session->fieldExists('auth_type');
    $assert_session->fieldExists('domain');

    // Select the authentication type
    $page->selectFieldOption('auth_type', $auth_type);

    // Verify domain field visibility
    $domain_field = $assert_session->fieldExists('domain');
    $this->assertEquals(
      $domain_visible,
      $domain_field->isVisible(),
      sprintf('Domain field should be %s when %s is selected.',
        $domain_visible ? 'visible' : 'hidden',
        $auth_type
      )
    );

    // Verify key_id field visibility
    $key_field = $assert_session->fieldExists('key_id');
    $this->assertEquals(
      $key_id_visible,
      $key_field->isVisible(),
      sprintf('Key ID field should be %s when %s is selected.',
        $key_id_visible ? 'visible' : 'hidden',
        $auth_type
      )
    );

    // Verify access_token field visibility
    $access_token_field = $assert_session->fieldExists('access_token');
    $this->assertEquals(
      $access_token_visible,
      $access_token_field->isVisible(),
      sprintf('Key ID field should be %s when %s is selected.',
        $access_token_visible ? 'visible' : 'hidden',
        $auth_type
      )
    );
  }

  /**
   * Data provider for testing form field states.
   *
   * @return array
   *   Test cases data for field states.
   */
  public static function formFieldStatesProvider(): array {
    return [
      'key_based_auth' => [
        'auth_type' => 'key_based',
        'domain_visible' => FALSE,
        'key_id_visible' => TRUE,
        'access_token_visible' => FALSE,
      ],
      'authorization_code_auth' => [
        'auth_type' => 'authorization_code',
        'domain_visible' => TRUE,
        'key_id_visible' => FALSE,
        'access_token_visible' => FALSE,
      ],
      'api_token_auth' => [
        'auth_type' => 'api_token',
        'domain_visible' => TRUE,
        'key_id_visible' => FALSE,
        'access_token_visible' => TRUE,
      ],
      'refresh_token_auth' => [
        'auth_type' => 'refresh_token',
        'domain_visible' => TRUE,
        'key_id_visible' => FALSE,
        'access_token_visible' => FALSE,
      ],
    ];
  }

  /**
   * Test key-based authentication for valid key and value.
   */
  public function testValidKey(): void {
    // Get current module path.
    $file_location = \Drupal::service('module_handler')->getModule('acquia_dam')->getPath() . '/tests/fixtures/key_auth/key_valid_credentials.json';

    // Create a key entity for testing.
    $this->createKeyEntity($file_location);

    // Visit the configuration form
    $this->drupalGet('/admin/config/acquia-dam');
    $page = $this->getSession()->getPage();
    $assert_session = $this->assertSession();

    // Submit the form with key-based authentication
    $page->selectFieldOption('auth_type', 'key_based');
    $page->selectFieldOption('key_id', 'acquia_dam_credentials');
    $page->pressButton('Authenticate Site');
    $assert_session->waitForText('The configuration options have been saved.');

    // Validate media creation with key-based authentication.
    $this->drupalGet('/node/add/page');
    $this->getSession()->getPage()->fillField('Title', 'CKEditor 5 Media Test');
    $this->waitForEditor();
    $this->pressEditorButton('Insert Media');
    $assert_session->assertWaitOnAjaxRequest();

    // Wait for modal.
    $modal = $assert_session->waitForElementVisible('css', '#drupal-modal');
    $this->assertNotNull($modal, 'Media library dialog opened successfully.');
    // Select the PDF tab
    $modal->clickLink('PDF');
    $assert_session->assertWaitOnAjaxRequest();

    // Verify assets are loaded in the library
    $media_elements = $modal->findAll('css', '.js-media-library-item');
    self::assertCount(2, $media_elements);

    // Select a PDF asset
    $asset_id = '0324b0b2-5293-4aa0-b0aa-c85b003395e2';
    $media_checkbox = $assert_session->elementExists('css', "[value='$asset_id']", $modal);
    $media_checkbox->check();

    // Proceed to format selection
    $this->pressDialogButton('Next: Select Format');
    $assert_session->waitForText('Choose a format for your media');
    // Choose the inline view format
    $assert_session->elementExists('css', "[value=inline_view]")->click();
    $assert_session->assertWaitOnAjaxRequest();

    // Insert the media into the editor
    $this->pressDialogButton('Insert selected');
    // Verify the media was inserted in the editor
    $media_preview_selector = '.ck-content .ck-widget.drupal-media';
    $inserted_media = $assert_session->waitForElementVisible('css', $media_preview_selector);
    $this->assertNotNull($inserted_media, 'Media was inserted in the editor');

    // Save the node and verify the media renders on the page
    $this->getSession()->getPage()->pressButton('Save');

    // Validate the site disconnect functionality.
    $this->drupalGet('/admin/config/acquia-dam');
    $assert_session->pageTextContains('Site is authenticated with Acquia DAM.');
    $this->getSession()->getPage()->clickLink('Disconnect site');
    $assert_session->waitForText('Are you sure you want to disconnect this site from Acquia DAM?');
    $this->getSession()->getPage()->pressButton('Disconnect');
    $assert_session->waitForText('Site successfully disconnected from Acquia DAM.');
  }

  /**
   * Test key-based authentication for invalid key and value.
   */
  public function testInvalidKey(): void {
    // Get current module path.
    $file_location = \Drupal::service('module_handler')->getModule('acquia_dam')->getPath() . '/tests/fixtures/key_auth/key_invalid_credentials.json';

    // Create a key entity for testing.
    $this->createKeyEntity($file_location);

    // Visit the configuration form
    $this->drupalGet('/admin/config/acquia-dam');
    $page = $this->getSession()->getPage();
    $assert_session = $this->assertSession();

    // Submit the form with key-based authentication
    $page->selectFieldOption('auth_type', 'key_based');
    $page->selectFieldOption('key_id', 'acquia_dam_credentials');
    $page->pressButton('Authenticate Site');
    // Check for message if expected.
    $assert_session->pageTextContains('Selected key does not contain valid credentials.');
  }

  /**
   * Create key entity for testing.
   *
   * @param string $file_path
   *   The file path to use for the key entity.
   */
  protected function createKeyEntity(string $file_path): void {
    // Create a key entity
    $key_storage = \Drupal::entityTypeManager()->getStorage('key');
    $key = $key_storage->create([
      'id' => 'acquia_dam_credentials',
      'label' => 'Acquia DAM Credentials',
      'key_type' => 'authentication',
      'key_provider' => 'file',
      'key_provider_settings' => [
        'file_location' => $file_path,
        'strip_line_breaks' => FALSE,
      ],
    ]);
    $key->save();
  }
}
