<?php

declare(strict_types=1);

namespace Drupal\Tests\acquia_dam\Kernel;

use Drupal\media_library\MediaLibraryState;

/**
 * Tests that AssetLibraryBuilder renders links with correct button CSS classes.
 *
 * Covers the UX improvement patch that replaced raw inline <a> markup with
 * proper render-array #type => 'link' elements carrying acquia-dam-button
 * CSS classes and role="button".
 *
 * We test the protected buildMediaLibraryView() method directly via reflection
 * so we can exercise each auth-state branch in isolation without the overhead
 * of a full browser.
 *
 * @group acquia_dam
 * @coversDefaultClass \Drupal\acquia_dam\AssetLibraryBuilder
 */
class AcquiaDamLibraryButtonTest extends AcquiaDamKernelTestBase {

  /**
   * The asset library builder under test.
   *
   * @var \Drupal\acquia_dam\AssetLibraryBuilder
   */
  protected $assetLibraryBuilder;

  /**
   * A \ReflectionMethod for AssetLibraryBuilder::buildMediaLibraryView().
   *
   * @var \ReflectionMethod
   */
  protected \ReflectionMethod $buildMediaLibraryView;

  /**
   * The machine name of the DAM media type created in setUp().
   *
   * @var string
   */
  private string $mediaTypeId;

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

    // Create a PDF media type so a valid MediaLibraryState can be built
    // with an acquia_dam source type (triggers the auth-check branches).
    $media_type = $this->createPdfMediaType();
    $this->mediaTypeId = $media_type->id();

    $this->assetLibraryBuilder = $this->container->get('media_library.ui_builder');

    // Expose the protected method via reflection so kernel tests can call it
    // without routing through the full buildUi() stack.
    $this->buildMediaLibraryView = new \ReflectionMethod(
      $this->assetLibraryBuilder,
      'buildMediaLibraryView'
    );
    $this->buildMediaLibraryView->setAccessible(TRUE);
  }

  // ---------------------------------------------------------------------------
  // Data provider
  // ---------------------------------------------------------------------------

  /**
   * Provides one row per auth-state branch.
   *
   * Each row contains:
   *   - the name of the per-scenario setup method to invoke,
   *   - the expected substring that must appear in the link #title, and
   *   - the expected substring that must appear in the fieldset #title.
   *
   * @return array<string, array{string, string, string}>
   */
  public static function authStateProvider(): array {
    return [
      'not_configured'         => ['setupNotConfigured',       'Configure',    'Site is not configured for Acquia DAM. Please configure it to browse assets.'],
      'site_not_authenticated' => ['setupSiteNotAuthenticated', 'Authenticate', 'Site is not authenticated with Acquia DAM. Please authenticate it to browse assets. Once successfully authenticated, close this modal and reopen it to browse Acquia DAM assets.'],
      'user_not_authenticated' => ['setupUserNotAuthenticated', 'Authenticate', 'This user account is not authenticated with Acquia DAM. Please authenticate to browse assets.'],
    ];
  }

  // ---------------------------------------------------------------------------
  // Test
  // ---------------------------------------------------------------------------

  /**
   * Verifies the button render-array for every authentication-state branch.
   *
   * @dataProvider authStateProvider
   * @covers ::buildMediaLibraryView
   */
  public function testButtonRendering(string $setupMethod, string $expectedTitle, string $expectedFieldsetTitle): void {
    $this->{$setupMethod}();

    $build = $this->invokeBuildMediaLibraryView();

    $this->assertSame('fieldset', $build['#type']);
    $this->assertStringContainsString($expectedFieldsetTitle, (string) $build['#title']);
    $this->assertArrayHasKey('link', $build);
    $this->assertSame('link', $build['link']['#type']);
    $this->assertContains(
      'acquia_dam/acquia_dam.button',
      $build['link']['#attached']['library'],
      'acquia_dam/acquia_dam.button library must be attached.'
    );
    $this->assertSame(
      'button',
      $build['link']['#attributes']['role'],
      'Link must have role="button".'
    );
    $this->assertPrimaryButtonClasses($build['link']['#attributes']['class']);
    $this->assertStringContainsString(
      $expectedTitle,
      (string) $build['link']['#title']
    );
  }

  // ---------------------------------------------------------------------------
  // Per-scenario setup helpers
  // ---------------------------------------------------------------------------

  /**
   * Sets up the not-configured state (no domain / auth_type in config).
   */
  private function setupNotConfigured(): void {
    $this->clearDamDomain();
  }

  /**
   * Sets up DAM configured but site not authenticated.
   */
  private function setupSiteNotAuthenticated(): void {
    $this->disconnectDamSite();
    $this->addDamDomain();
  }

  /**
   * Sets up site authenticated but the current user without a DAM token.
   */
  private function setupUserNotAuthenticated(): void {
    $this->drupalSetUpCurrentUser([], [], FALSE);
    $this->revokeCurrentUserDamToken();
  }

  // ---------------------------------------------------------------------------
  // Low-level helpers
  // ---------------------------------------------------------------------------

  /**
   * Invokes buildMediaLibraryView() with a minimal but valid MediaLibraryState.
   *
   * @return array
   *   The render array returned by the method under test.
   */
  private function invokeBuildMediaLibraryView(): array {
    $state = MediaLibraryState::create(
      'media_library.opener.field_widget',
      [$this->mediaTypeId],
      $this->mediaTypeId,
      1
    );

    return $this->buildMediaLibraryView->invoke(
      $this->assetLibraryBuilder,
      $state
    );
  }

  /**
   * Clears the DAM domain config to simulate a not-configured state.
   *
   * isConfigured() in AcquiadamAuthService checks that both `domain` and
   * `auth_type` are non-empty, so removing the domain is sufficient.
   */
  private function clearDamDomain(): void {
    $this->container
      ->get('config.factory')
      ->getEditable('acquia_dam.settings')
      ->set('domain', '')
      ->save();
  }

  /**
   * Adds the DAM domain config to simulate a not-authenticated state.
   *
   * isConfigured() in AcquiadamAuthService checks that both `domain` and
   * `auth_type` are non-empty, so adding the domain is sufficient.
   */
  private function addDamDomain(): void {
    $this->container
      ->get('config.factory')
      ->getEditable('acquia_dam.settings')
      ->set('domain', 'test.widencollective.com')
      ->save();
  }

  /**
   * Removes the DAM token from the current user's user_data row so that
   * isAuthenticated($uid) returns FALSE for them while the site token remains.
   */
  private function revokeCurrentUserDamToken(): void {
    $uid = (int) $this->container->get('current_user')->id();
    $this->container->get('user.data')->delete('acquia_dam', $uid);
  }

  /**
   * Asserts that a class list contains the full set of primary button classes
   * introduced by the UX-improvement patch.
   *
   * Expected classes:
   *   - acquia-dam-button          (base DAM class)
   *   - acquia-dam-button--primary (DAM variant modifier)
   *   - button                     (Drupal core base)
   *   - button--primary            (Drupal core variant)
   *
   * @param string[] $classes
   *   The #attributes['class'] array from the render element.
   */
  private function assertPrimaryButtonClasses(array $classes): void {
    foreach (
      [
        'acquia-dam-button',
        'acquia-dam-button--primary',
        'button',
        'button--primary',
      ] as $expected
    ) {
      $this->assertContains(
        $expected,
        $classes,
        sprintf('Missing expected CSS class "%s".', $expected)
      );
    }
  }

}
