<?php
namespace Drupal\acquiadam_asset_import\Drush\Commands;

use Drupal\acquiadam_asset_import\Services\AssetQueueService;
use Drupal\Core\Queue\QueueFactory;
use Drupal\Core\Queue\QueueWorkerManagerInterface;
use Drupal\Core\Queue\SuspendQueueException;
use Drupal\Core\Queue\DelayedRequeueException;
use Drush\Attributes as CLI;
use Drush\Commands\DrushCommands;

/**
 * Drush command to bulk import Acquia DAM assets.
 */
final class AssetImportDrushCommands extends DrushCommands {

  /**
   * The service for managing the asset queue.
   *
   * @var \Drupal\acquiadam_asset_import\Services\AssetQueueService
   */
  private AssetQueueService $assetQueueService;

  /**
   * The queue factory service.
   *
   * @var \Drupal\Core\Queue\QueueFactory
   */
  private QueueFactory $queueFactory;

  /**
   * The queue worker manager service.
   *
   * @var \Drupal\Core\Queue\QueueWorkerManagerInterface
   */
  private QueueWorkerManagerInterface $queueWorkerManager;

  public function __construct(
    AssetQueueService $asset_queue_service,
    QueueFactory $queue_factory,
    QueueWorkerManagerInterface $queue_worker_manager
  ) {
    parent::__construct();
    $this->assetQueueService = $asset_queue_service;
    $this->queueFactory = $queue_factory;
    $this->queueWorkerManager = $queue_worker_manager;
  }

  /**
   * Queue Acquia DAM assets for import.
   *
   * This command fetches available assets from Acquia DAM and adds them to the
   * import queue for processing. Assets are queued but not immediately imported.
   * Use the process-queue or import-assets commands to actually import the queued assets.
   *
   * @command acquia-dam:queue-import-assets
   * @aliases ad-qia
   * @usage acquia-dam:queue-import-assets
   *   Queue all available Acquia DAM assets for import.
   * @help This command discovers Acquia DAM assets and adds them to the processing queue.
   *   No assets are actually imported by this command - it only queues them for later processing.
   *   This is useful for separating the asset discovery phase from the import processing phase,
   *   especially when dealing with large numbers of assets.
   */
  #[CLI\Command(name: 'acquia-dam:queue-import-assets', aliases: ['ad-qia'])]
  #[CLI\Usage(name: 'acquia-dam:queue-import-assets', description: 'Queue all available Acquia DAM assets for import.')]
  #[CLI\Help(description: 'This command discovers Acquia DAM assets and adds them to the processing queue.
      No assets are actually imported by this command - it only queues them for later processing.
      This is useful for separating the asset discovery phase from the import processing phase,
      especially when dealing with large numbers of assets.')]
  public function queueAssets(): void {
    try {
      $this->output()->writeln('Queueing the Acquia DAM assets...');
      $import_details = $this->assetQueueService->addAssetsToQueue();

      if (empty($import_details)) {
        $this->output()->writeln('❌ No assets were queued.');
        return;
      }

      $this->output()->writeln('Assets queued successfully:');

      // First, print the summary for each type
      foreach ($import_details as $type => $details) {
        $total_assets = 0;
        $queued_assets = 0;

        $this->output()->writeln(sprintf(
          '✅ For %s',
          $type === 'asset_groups' ? "Asset Groups" : "Categories"
        ));
        // Then, print the breakdown for each source within the type.
        foreach ($details as $name => $counts) {
          $queued = $counts['queued'] ?? 0;
          $total = $counts['total'] ?? 0;
          $total_assets += $total;
          $queued_assets += $queued;

          $this->output()->writeln(sprintf(
            ' - %s: %d queued, %d skipped (out of %d total)',
            $name,
            $queued,
            $total - $queued,
            $total
          ));
        }

        $this->output()->writeln(sprintf(
          '%d total queued, %d total skipped (out of %d total).',
          $queued_assets,
          $total_assets - $queued_assets,
          $total_assets
        ));
      }
    }
    catch (\Exception $e) {
      $this->output()->writeln("❌ An error occurred while queueing assets: " . $e->getMessage());
    }
  }

  /**
   * Process the Acquia DAM asset import queue.
   *
   * This command processes items from the acquia_dam_asset_import queue, importing
   * the actual assets into Drupal. It automatically queues assets first if the queue
   * is empty, then processes them in configurable batches. Failed items are tracked
   * and reported in the summary.
   *
   * @command acquia-dam:process-import-queue
   * @aliases ad-piq
   * @option batch-size
   *   Number of queue items to process in each batch. Larger batches may be faster
   *   but use more memory. Default: 10.
   * @option limit
   *   Maximum number of items to process in this execution. If not specified,
   *   all items in the queue will be processed. Default: 0 (no limit).
   * @usage acquia-dam:process-import-queue
   *   Process all items in the acquia_dam_asset_import queue using default batch size of 10.
   * @usage acquia-dam:process-import-queue --batch-size=50
   *   Process queue items in batches of 50 for faster processing.
   * @usage acquia-dam:process-import-queue --batch-size=5
   *   Process queue items in smaller batches of 5 to reduce memory usage.
   * @usage acquia-dam:process-import-queue --limit=100
   *   Process only the first 100 items from the queue.
   * @usage acquia-dam:process-import-queue --limit=50 --batch-size=10
   *   Process 50 items maximum, in batches of 10.
   * @help This command processes the Acquia DAM asset import queue and performs the actual
   *   import of assets into Drupal. It handles various queue exceptions gracefully:
   *   - DelayedRequeueException: Items are released back to the queue for retry
   *   - SuspendQueueException: Processing stops temporarily
   *   - General exceptions: Items are marked as failed and removed from queue
   *
   *   The command provides detailed progress reporting including batch-by-batch status
   *   and a final summary of processed, failed, and remaining items.
   *
   *   Use the --limit option to process only a specific number of items per execution.
   *   This is useful for:
   *   - Testing with a small subset of items
   *   - Gradual processing of large queues
   *   - Scheduled processing in smaller chunks
   *   - Avoiding timeouts on large queues
   *
   *   If the queue is empty when this command runs, it will automatically queue assets first.
   */
  #[CLI\Command(name: 'acquia-dam:process-import-queue', aliases: ['ad-piq'])]
  #[CLI\Option(name: 'batch-size', description: 'Restrict command list to those commands defined in the specified file. Omit value to choose from a list of names.')]
  #[CLI\Option(name: 'limit', description: 'Show a simple table of command names and descriptions.')]
  #[CLI\Usage(name: 'acquia-dam:process-import-queue', description: 'Process all items in the acquia_dam_asset_import queue using default batch size of 10.')]
  #[CLI\Usage(name: 'acquia-dam:process-import-queue --batch-size=50', description: 'Process queue items in batches of 50 for faster processing.')]
  #[CLI\Usage(name: 'acquia-dam:process-import-queue --batch-size=5', description: 'Process queue items in smaller batches of 5 to reduce memory usage.')]
  #[CLI\Usage(name: 'acquia-dam:process-import-queue --limit=100', description: 'Process only the first 100 items from the queue.')]
  #[CLI\Usage(name: 'acquia-dam:process-import-queue --limit=50 --batch-size=10', description: 'Process 50 items maximum, in batches of 10.')]
  #[CLI\Help(description: 'This command processes the Acquia DAM asset import queue and performs the actual
      import of assets into Drupal. It handles various queue exceptions gracefully:
      - DelayedRequeueException: Items are released back to the queue for retry
      - SuspendQueueException: Processing stops temporarily
      - General exceptions: Items are marked as failed and removed from queue

      The command provides detailed progress reporting including batch-by-batch status
      and a final summary of processed, failed, and remaining items.

      Use the --limit option to process only a specific number of items per execution.
      This is useful for:
      - Testing with a small subset of items
      - Gradual processing of large queues
      - Scheduled processing in smaller chunks
      - Avoiding timeouts on large queues

      If the queue is empty when this command runs, it will automatically queue assets first.')]
  public function processQueue($options = ['batch-size' => 10, 'limit' => 0]): void {

    $queue_name = 'acquia_dam_asset_import';
    $batch_size = (int) $options['batch-size'];
    $limit = (int) $options['limit'];

    // Get the queue and queue worker.
    $queue = $this->queueFactory->get($queue_name);
    $queue_worker = $this->queueWorkerManager->createInstance($queue_name);

    // Get the total number of items in the queue.
    $total_items = $queue->numberOfItems();
    $items_to_process = $limit > 0 ? min($limit, $total_items) : $total_items;

    $this->output()->writeln('Total items in queue: ' . $total_items);
    if ($limit > 0) {
      $this->output()->writeln("Processing limit: $limit items");
    }

    // If there are no items in the queue, we can exit early.
    if ($total_items === 0) {
      $this->output()->writeln('⚠️ No items in the queue to process.');
      return;
    }

    $this->output()->writeln("🚀 Starting to process $items_to_process items from the $queue_name queue...");

    $processed = 0;
    $failed = 0;
    $batch_count = 0;

    // Process the queue in batches.
    while ($queue->numberOfItems() > 0 && ($processed + $failed < $items_to_process)) {
      $batch_count++;
      $remaining_to_process = $items_to_process - ($processed + $failed);
      $current_batch_size = min($batch_size, $remaining_to_process);

      $this->output()->writeln("Processing batch $batch_count (up to $current_batch_size items)...");

      // Process the batch.
      [$batch_processed, $batch_failed] = $this->processBatch($queue, $queue_worker, $current_batch_size);

      $processed += $batch_processed;
      $failed += $batch_failed;

      $this->output()->writeln("Batch $batch_count completed: $batch_processed processed, $batch_failed failed.");

      // Check if we've reached our limit or if there are more items to process.
      if ($processed + $failed >= $items_to_process || $queue->numberOfItems() === 0) {
        break;
      }
    }

    $remaining = $queue->numberOfItems();

    $this->output()->writeln("✅ Queue processing completed!");
    $this->output()->writeln("📊 Summary:");
    $this->output()->writeln("  - Items processed: $processed");
    $this->output()->writeln("  - Items failed: $failed");
    $this->output()->writeln("  - Items remaining in queue: $remaining");

    if ($limit > 0 && $remaining > 0) {
      $this->output()->writeln("  - Processing was limited to $limit items. Run again to process more.");
    }
  }

  /**
   * Import Acquia DAM assets (queue and process in one command).
   *
   * This is a convenience command that combines the functionality of queue-assets
   * and process-queue into a single operation. It first queues all available
   * Acquia DAM assets, then immediately processes the queue to import them into Drupal.
   * This is the recommended command for most import scenarios.
   *
   * @command acquia-dam:import-assets
   * @aliases ad-ia
   * @option batch-size
   *   Number of queue items to process in each batch. Larger batches may be faster
   *   but use more memory. Default: 10.
   * @option limit
   *   Maximum number of items to process in this execution. If not specified,
   *   all items in the queue will be processed. Default: 0 (no limit).
   * @usage acquia-dam:import-assets
   *   Queue and import all Acquia DAM assets using default batch size of 10.
   * @usage acquia-dam:import-assets --batch-size=50
   *   Queue and import assets processing in batches of 50 for faster imports.
   * @usage acquia-dam:import-assets --batch-size=1
   *   Queue and import assets one at a time for maximum stability and debugging.
   * @usage acquia-dam:import-assets --limit=25
   *   Queue all assets but only import the first 25 items.
   * @usage acquia-dam:import-assets --limit=100 --batch-size=20
   *   Queue all assets and import 100 items in batches of 20.
   * @help This command provides a complete end-to-end asset import workflow by combining
   *   the queue-assets and process-queue commands. It's the most convenient way to perform
   *   a full import of Acquia DAM assets.
   *
   *   The command flow:
   *   1. Discovers and queues all available Acquia DAM assets
   *   2. Processes the queue in batches to import assets into Drupal
   *   3. Provides comprehensive reporting on the import results
   *
   *   This command is equivalent to running:
   *   - drush acquia-dam:queue-assets
   *   - drush acquia-dam:process-import-queue --batch-size=X --limit=Y
   *
   *   Use the --batch-size option to control memory usage and processing speed:
   *   - Larger batch sizes (50-100): Faster processing, higher memory usage
   *   - Smaller batch sizes (1-10): Slower processing, lower memory usage, better for debugging
   *   - Default (10): Good balance for most scenarios
   *
   *   Use the --limit option to control how many items are processed:
   *   - For testing: --limit=5 or --limit=10
   *   - For gradual imports: --limit=100 or --limit=500
   *   - For full imports: omit the --limit option
   */
  #[CLI\Command(name: 'acquia-dam:import-assets', aliases: ['ad-ia'])]
  #[CLI\Option(name: 'batch-size', description: 'Number of queue items to process in each batch. Larger batches may be faster but use more memory. Default: 10.')]
  #[CLI\Option(name: 'limit', description: 'Maximum number of items to process in this execution. If not specified, all items in the queue will be processed. Default: 0 (no limit).')]
  #[CLI\Usage(name: 'acquia-dam:import-assets', description: 'Queue and import all Acquia DAM assets using default batch size of 10.')]
  #[CLI\Usage(name: 'acquia-dam:import-assets --batch-size=50', description: 'Queue and import assets processing in batches of 50 for faster imports.')]
  #[CLI\Usage(name: 'acquia-dam:import-assets --batch-size=1', description: 'Queue and import assets one at a time for maximum stability and debugging.')]
  #[CLI\Usage(name: 'acquia-dam:import-assets --limit=25', description: 'Queue all assets but only import the first 25 items.')]
  #[CLI\Usage(name: 'acquia-dam:import-assets --limit=100 --batch-size=20', description: 'Queue all assets and import 100 items in batches of 20.')]
  #[CLI\Help(description: 'This command provides a complete end-to-end asset import workflow by combining
    the queue-assets and process-queue commands. Its the most convenient way to perform a full import of Acquia DAM assets.

    The command flow:
    1. Discovers and queues all available Acquia DAM assets
    2. Processes the queue in batches to import assets into Drupal
    3. Provides comprehensive reporting on the import results

    This command is equivalent to running:
    - drush acquia-dam:queue-assets
    - drush acquia-dam:process-import-queue --batch-size=X --limit=Y

    Use the --batch-size option to control memory usage and processing speed:
      - Larger batch sizes (50-100): Faster processing, higher memory usage
      - Smaller batch sizes (1-10): Slower processing, lower memory usage, better for debugging
      - Default (10): Good balance for most scenarios

    Use the --limit option to control how many items are processed:
      - For testing: --limit=5 or --limit=10
      - For gradual imports: --limit=100 or --limit=500
      - For full imports: omit the --limit option')]
  public function importAssets($options = ['batch-size' => 10, 'limit' => 0]): void {
    // Ensure the assets are queued before processing.
    $this->queueAssets();
    // Process the queue with the specified options.
    $this->processQueue($options);
  }

  /**
   * Processes a batch of items from the queue.
   *
   * @param \Drupal\Core\Queue\QueueInterface $queue
   *   The queue to process items from.
   * @param \Drupal\Core\Queue\QueueWorkerInterface $queue_worker
   *   The queue worker responsible for processing items.
   * @param int $batch_size
   *   The number of items to process in the batch.
   *
   * @return array
   *   An array containing the number of processed and failed items:
   *   - 0: Number of successfully processed items.
   *   - 1: Number of failed items.
   */
  private function processBatch($queue, $queue_worker, int $batch_size): array {
    $batch_processed = 0;
    $batch_failed = 0;

    // Loop through the queue items and process them.
    for ($i = 0; $i < $batch_size; $i++) {
      $item = $queue->claimItem();

      if (!$item) {
        // No more items to process.
        break;
      }

      // Process the item using the queue worker.
      try {
        $queue_worker->processItem($item->data);
        $queue->deleteItem($item);
        $batch_processed++;
      }
      catch (DelayedRequeueException $e) {
        // The worker requested the task to be delayed.
        $queue->releaseItem($item);
        $this->output()->writeln("⏳ Item delayed: " . $e->getMessage());
      }
      catch (SuspendQueueException $e) {
        // The worker requested the whole queue to be delayed.
        $queue->releaseItem($item);
        $this->output()->writeln("⏸️ Queue processing suspended: " . $e->getMessage());
        break;
      }
      catch (\Exception $e) {
        // Handle any other exceptions.
        $queue->deleteItem($item);
        $batch_failed++;
        $this->output()->writeln("❌ Failed to process item: " . $e->getMessage());
      }
    }

    return [$batch_processed, $batch_failed];
  }

}
