<?php

declare(strict_types=1);

namespace Drupal\Tests\acquia_dam\Functional\Drush\Commands;

use Drupal\media\Entity\Media;
use Drupal\Tests\BrowserTestBase;
use Drush\TestTraits\DrushTestTrait;

/**
 * Tests for Acquia DAM Asset Update Drush Commands.
 *
 * @group acquia_dam
 */
class AcquiaDamAssetUpdateDrushCommandsTest extends BrowserTestBase {

  use DrushTestTrait;

  /**
   * {@inheritdoc}
   */
  protected bool $useOneTimeLoginLinks = FALSE;

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

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

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

    // Create a Image media entity.
    $imageMedia = Media::create([
      'bundle' => 'acquia_dam_image_asset',
      'name' => 'Wheel Illustration.ai',
      'acquia_dam_asset_id' => [
        'asset_id' => '56ff14de-02cd-41b5-9a73-c917eab19abf',
        'version_id' => '7b67948f-ee7e-405c-a0cd-344a24d8afb2',
      ],
    ]);
    $imageMedia->save();

    // Create a PDF media entity.
    $pdfMedia = Media::create([
      'bundle' => 'acquia_dam_pdf_asset',
      'name' => 'bobsCarsAgileExercises.md',
      'acquia_dam_asset_id' => [
        'asset_id' => 'a56fb261-8ad5-4e0d-8323-0e8a3659ed38',
        'version_id' => '3f656c07-6a08-47b3-9403-16082d2fcda7',
      ],
    ]);
    $pdfMedia->save();
  }

  /**
   * Data provider for queue update assets command tests.
   */
  public static function queueUpdateAssetsCommandProvider(): array {
    return [
      'default' => [
        'options' => [],
        'expected_verified' => 2,
        'expected_queued' => 2,
      ],
      'batch_size' => [
        'options' => ['batch-size' => 1],
        'expected_verified' => 2,
        'expected_queued' => 2,
      ],
      'media_type' => [
        'options' => ['media-type' => 'acquia_dam_image_asset'],
        'expected_verified' => 1,
        'expected_queued' => 1,
      ],
      'finalized_version' => [
        'options' => ['finalized-version' => TRUE],
        'expected_verified' => 2,
        'expected_queued' => 2,
      ],
    ];
  }

  /**
   * Tests the acquia-dam:queue-update-assets drush command with various options.
   *
   * @dataProvider queueUpdateAssetsCommandProvider
   */
  public function testQueueUpdateAssetsCommandVariants(array $options, int $expected_verified, int $expected_queued): void {
    $this->drush('acquia-dam:queue-update-assets', [], $options);
    $this->assertQueueSummary($expected_verified, $expected_queued);
    $this->assertStringNotContainsString("error", strtolower($this->getOutput()));
  }

  /**
   * Tests the acquia-dam:process-update-queue drush command.
   */
  public function testProcessUpdateQueueCommand(): void {
    // First queue some items.
    $this->drush('acquia-dam:queue-update-assets');
    // Then process the queue.
    $this->drush('acquia-dam:process-update-queue');
    $this->assertProcessSummary(1, 1, 0);
  }

  /**
   * Tests the process queue command with limit option.
   */
  public function testProcessUpdateQueueWithLimit(): void {
    // First queue some items.
    $this->drush('acquia-dam:queue-update-assets');
    $this->assertQueueSummary(2, 2);

    // Process with limit.
    $this->drush('acquia-dam:process-update-queue', [], ['limit' => 1]);
    $this->assertProcessSummary(1, 1, 0);
  }

  /**
   * Tests processing queue when it's empty.
   */
  public function testProcessEmptyQueue(): void {
    $this->drush('acquia-dam:process-update-queue');
    $this->assertProcessSummary(0, 0, 0);
  }

  /**
   * Tests the acquia-dam:update-assets combined command.
   */
  public function testUpdateAssetsCommand(): void {
    $this->drush('acquia-dam:update-assets');

    // Should contain messages from both queue and process operations.
    $this->assertStringContainsString("=== STARTING DAM ASSET UPDATE AND PROCESSING ===", $this->getOutput());
    $this->assertStringContainsString("Found 1 media items to process for type: acquia_dam_image_asset", $this->getOutput());
    $this->assertStringContainsString("[1/1] Queued update for media item: Wheel Illustration.ai (ID: 1)", $this->getOutput());
    $this->assertStringContainsString("Progress: 1/1 items processed", $this->getOutput());
    $this->assertStringContainsString("Found 1 media items to process for type: acquia_dam_pdf_asset", $this->getOutput());
    $this->assertStringContainsString("[1/1] Queued update for media item: bobsCarsAgileExercises.md (ID: 2)", $this->getOutput());
    $this->assertStringContainsString("Remaining items in queue: 0", $this->getOutput());
    $this->assertStringContainsString("=== DAM ASSET UPDATE AND PROCESSING COMPLETED ===", $this->getOutput());
  }

  /**
   * Asserts that the queue summary output is correct.
   *
   * @param int $verified
   *   The expected number of verified items.
   * @param int $queued
   *   The expected number of queued items.
   */
  private function assertQueueSummary(int $verified, int $queued): void {
    $this->assertStringContainsString("=== UPDATE SUMMARY ===", $this->getOutput());
    $this->assertStringContainsString("Media items verified for update: {$verified}", $this->getOutput());
    $this->assertStringContainsString("Media items queued for update: {$queued}", $this->getOutput());
    $this->assertStringContainsString("Updates have been queued and will be processed during the next cron run or when the queue is manually processed.", $this->getOutput());
  }

  /**
   * Asserts that the process summary output is correct.
   *
   * @param int $queued
   *   The expected number of items in the queue at start.
   * @param int $processed
   *   The expected number of processed items.
   * @param int $remaining
   *   The expected number of remaining items in the queue.
   */
  private function assertProcessSummary(int $queued, int $processed, int $remaining): void {
    $this->assertStringContainsString("Processing DAM media update queue (items in queue: {$queued})", $this->getOutput());
    $this->assertStringContainsString("=== QUEUE PROCESSING SUMMARY ===", $this->getOutput());
    $this->assertStringContainsString("Items processed: {$processed}", $this->getOutput());
    $this->assertStringContainsString("Remaining items in queue: {$remaining}", $this->getOutput());
  }

  /**
   * Tests the combined command with all options.
   */
  public function testUpdateAssetsWithAllOptions(): void {
    $this->drush('acquia-dam:update-assets', [], [
      'batch-size' => 10,
      'limit' => 50,
      'media-type' => 'acquia_dam_image_asset',
      'finalized-version' => TRUE,
    ]);

    $this->assertStringContainsString("=== STARTING DAM ASSET UPDATE AND PROCESSING ===", $this->getOutput());
    $this->assertStringContainsString("Found 1 DAM media type(s): acquia_dam_image_asset", $this->getOutput());
    $this->assertStringContainsString("Found 1 media items to process for type: acquia_dam_image_asset", $this->getOutput());
    $this->assertStringContainsString("[1/1] Queued update for media item: Wheel Illustration.ai (ID: 1)", $this->getOutput());
    $this->assertStringContainsString("Progress: 1/1 items processed", $this->getOutput());
    $this->assertStringContainsString("Remaining items in queue: 0", $this->getOutput());
    $this->assertStringContainsString("=== DAM ASSET UPDATE AND PROCESSING COMPLETED ===", $this->getOutput());
  }

  /**
   * Tests command aliases work correctly.
   */
  public function testCommandAliases(): void {
    // Test queue command alias.
    $this->drush('adqua');
    $this->assertStringContainsString("UPDATE SUMMARY", $this->getOutput());

    // Test process command alias.
    $this->drush('adpuq');
    $this->assertStringContainsString("QUEUE PROCESSING SUMMARY", $this->getOutput());

    // Test combined command alias.
    $this->drush('adua');
    $this->assertStringContainsString("STARTING DAM ASSET UPDATE", $this->getOutput());
    $this->assertStringContainsString("COMPLETED", $this->getOutput());

    // Test resolve media type command alias.
    $this->drush('adramt');
    $this->assertStringContainsString("Processing 2 media items in batches of 50\nProcessing chunk 1/1 (2 items)\n\n=== MEDIA TYPE RESOLUTION SUMMARY ===\nMedia items processed: 2\nMedia items updated: 0", $this->getOutput());
  }

  /**
   * Tests the acquia-dam:resolve-asset-media-type command.
   */
  public function testResolveAssetMediaTypeCommand(): void {
    $this->drush('acquia-dam:resolve-asset-media-type');

    $output = $this->getOutput();
    $this->assertStringContainsString("Processing 2 media items in batches of 50\nProcessing chunk 1/1 (2 items)\n\n=== MEDIA TYPE RESOLUTION SUMMARY ===\nMedia items processed: 2\nMedia items updated: 0", $output);
  }

  /**
   * Tests resolve media type command with batch-size option.
   */
  public function testResolveAssetMediaTypeWithBatchSize(): void {
    $this->drush('acquia-dam:resolve-asset-media-type', [], ['batch-size' => 1]);

    $output = $this->getOutput();
    $this->assertStringContainsString("Processing 2 media items in batches of 1\nProcessing chunk 1/2 (1 items)\nProcessing chunk 2/2 (1 items)", $output);
  }

  /**
   * Tests resolve media type command with media-type filter.
   */
  public function testResolveAssetMediaTypeWithMediaTypeFilter(): void {
    // Create a Image media entity with asset of type video.
    $imageMedia = Media::create([
      'bundle' => 'acquia_dam_image_asset',
      'name' => 'Wheel Illustration.ai',
      'acquia_dam_asset_id' => [
        'asset_id' => 'efb03f75-3c42-497b-baa9-5ec79d1f56af',
        'version_id' => '77b3dd66-e590-4259-82db-25e7f4e62778',
      ],
    ]);
    $imageMedia->save();
    $mid = $imageMedia->id();
    $media_storage = \Drupal::entityTypeManager()->getStorage('media');
    $media = $media_storage->load($mid);
    $this->assertSame('acquia_dam_image_asset', $media->bundle());

    $this->drush('acquia-dam:resolve-asset-media-type', [], ['media-type' => 'acquia_dam_image_asset']);
    $output = $this->getOutput();
    $this->assertStringContainsString("Processing 2 media items in batches of 50\nProcessing chunk 1/1 (2 items)\nUpdated media 3 from acquia_dam_image_asset to acquia_dam_video_asset\n\n=== MEDIA TYPE RESOLUTION SUMMARY ===\nMedia items processed: 2\nMedia items updated: 1", $output);

    // Test needs to clear its local cache for the entity to see the changes.
    $media_storage->resetCache([$mid]);
    // Validate that media is now of type acquia_dam_video_asset.
    $media = $media_storage->load($mid);
    $this->assertSame('acquia_dam_video_asset', $media->bundle());
  }

  /**
   * Tests resolve media type command with no media items.
   */
  public function testResolveAssetMediaTypeWithNoMediaItems(): void {
    // Delete all media items.
    $media_storage = \Drupal::entityTypeManager()->getStorage('media');
    $media_ids = $media_storage->getQuery()
      ->accessCheck(FALSE)
      ->execute();

    foreach ($media_ids as $media_id) {
      $media = $media_storage->load($media_id);
      $media->delete();
    }

    $this->drush('acquia-dam:resolve-asset-media-type');

    $output = $this->getOutput();
    $this->assertStringContainsString("No media items found to process", $output);
  }

}
