<?php

namespace Drupal\acquia_contenthub\Commands;

use Consolidation\AnnotatedCommand\CommandError;
use Drupal\acquia_contenthub\Commands\Traits\OutputFormatterTrait;
use Drupal\acquia_contenthub\PubSubModuleStatusChecker;
use Drupal\acquia_contenthub\QueueInspectorInterface;
use Drupal\acquia_contenthub_publisher\PublisherExportQueueInspector;
use Drupal\acquia_contenthub_subscriber\SubscriberImportQueueInspector;
use Drush\Commands\DrushCommands;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;

/**
 * Drush command to print queue items.
 */
class AcquiaContentHubQueueDetailsCommands extends DrushCommands {

  use OutputFormatterTrait;

  public const INVALID_OPTION_ERROR = 1;
  public const VALIDATION_ERROR = 2;

  /**
   * The publisher subscriber checker.
   *
   * @var \Drupal\acquia_contenthub\PubSubModuleStatusChecker
   */
  protected PubSubModuleStatusChecker $pubSubChecker;

  /**
   * The publisher queue inspector.
   *
   * @var \Drupal\acquia_contenthub_publisher\PublisherExportQueueInspector
   */
  protected PublisherExportQueueInspector $pubQueueInspector;

  /**
   * The subscriber queue inspector.
   *
   * @var \Drupal\acquia_contenthub_subscriber\SubscriberImportQueueInspector
   */
  protected SubscriberImportQueueInspector $subQueueInspector;

  /**
   * The AcquiaContentHubQueueDetailsCommands constructor.
   *
   * @param \Drupal\acquia_contenthub\PubSubModuleStatusChecker $pub_sub_checker
   *   The publisher/subscriber site configurations checker.
   */
  public function __construct(PubSubModuleStatusChecker $pub_sub_checker) {
    $this->pubSubChecker = $pub_sub_checker;
  }

  /**
   * Sets publisher queue inspector if available.
   *
   * @param \Drupal\acquia_contenthub_publisher\PublisherExportQueueInspector $pub_queue_inspector
   *   The publisher queue inspector.
   */
  public function setPublisherExportQueueInspector(PublisherExportQueueInspector $pub_queue_inspector): void {
    $this->pubQueueInspector = $pub_queue_inspector;
  }

  /**
   * Sets subscriber queue inspector if available.
   *
   * @param \Drupal\acquia_contenthub_subscriber\SubscriberImportQueueInspector $sub_queue_inspector
   *   The subscriber queue inspector.
   */
  public function setSubscriberImportQueueInspector(SubscriberImportQueueInspector $sub_queue_inspector): void {
    $this->subQueueInspector = $sub_queue_inspector;
  }

  /**
   * Validates command.
   *
   * @hook validate acquia:contenthub-list-queue-items
   *
   * @return \Consolidation\AnnotatedCommand\CommandError|null
   *   NULL or CommandError if the execution failed.
   */
  public function validate(): ?CommandError {
    if (!$this->pubSubChecker->isPublisher() && !$this->pubSubChecker->isSubscriber()) {
      return new CommandError('The site should have either the publisher or subscriber module enabled to run this command.', self::VALIDATION_ERROR);
    }
    return NULL;
  }

  /**
   * Prints list of queue items.
   *
   * @option limit
   *   Limit of number of queue items to be printed.
   * @default limit
   * @option type
   *   The entity type.
   * @default type
   * @option uuid
   *   The entity UUID.
   * @default uuid
   *
   * @usage ach-lqi
   *   Lists queue items from export/import queue.
   * @usage ach-lqi --type=node
   *   Lists queue items of entity type node from export/import queue.
   * @usage ach-lqi --uuid=00000aa0-a00a-000a-aa00-aaa000a0a0aa
   *   Lists queue item matching the specified uuid from export/import queue.
   * @usage ach-lqi --limit=10
   *   Lists 10 items from export/import queue.
   *
   * @command acquia:contenthub-list-queue-items
   * @aliases ach-lqi
   *
   * @return int
   *   The exit code.
   *
   * @throws \Exception
   */
  public function listQueueItems(): int {
    try {
      $queue_data = $this->getQueueData();
    }
    catch (\InvalidArgumentException $e) {
      $this->io()->error($e->getMessage());
      return $e->getCode();
    }

    if (empty($queue_data)) {
      $this->io()->note('No data available in queue.');
      return self::EXIT_SUCCESS;
    }
    $message = 'Queue Items:';
    $this->printTableOutput($message, array_keys($queue_data[0]), $queue_data, $this->output);
    return self::EXIT_SUCCESS;
  }

  /**
   * Adds the --queue option to the command if it is a publisher+subscriber.
   *
   * @hook option acquia:contenthub-list-queue-items
   */
  public function publisherSubscriber(Command $command): void {
    if (!$this->pubSubChecker->siteHasDualConfiguration()) {
      return;
    }

    $command->addOption(
      'queue',
      '',
      InputOption::VALUE_REQUIRED,
      'The queue name (import|export) to inspect. The site has both publisher and subscriber module enabled.',
      'export'
    );
  }

  /**
   * Provides the queue data depending on the site configurations.
   *
   * @return array
   *   The queue data.
   *
   * @throws \Exception
   */
  protected function getQueueData(): array {
    $limit = is_numeric($this->input->getOption('limit')) ? (int) $this->input->getOption('limit') : -1;
    $type = $this->input->getOption('type');
    $uuid = $this->input->getOption('uuid');
    $queue_inspector = $this->getQueueInspector();
    return $queue_inspector->getQueueData($type, $uuid, $limit);
  }

  /**
   * Returns the appropriate queue inspector service.
   *
   * @return \Drupal\acquia_contenthub\QueueInspectorInterface
   *   The queue inspector.
   */
  public function getQueueInspector(): QueueInspectorInterface {
    $queue = $this->getQueueOptionValue();
    if (!$queue) {
      $queue = $this->pubSubChecker->isPublisher() ? 'export' : 'import';
    }

    if ($queue === 'export') {
      return $this->pubQueueInspector;
    }
    return $this->subQueueInspector;
  }

  /**
   * Checks for --queue option and returns its value if it exists.
   *
   * @return string
   *   Either import | export otherwise an exception.
   *
   * @throws \InvalidArgumentException
   */
  protected function getQueueOptionValue(): string {
    if (!$this->input()->hasOption('queue')) {
      return '';
    }

    $option = $this->input()->getOption('queue');
    if (!in_array($option, ['import', 'export'], TRUE)) {
      throw new \InvalidArgumentException('Invalid value specified for --queue: ' . $option, self::INVALID_OPTION_ERROR);
    }

    return $option;
  }

}
