<?php

namespace Drupal\acquia_contenthub\Commands;

use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
use Drupal\acquia_contenthub\AcquiaContentHubEvents;
use Drupal\acquia_contenthub\Client\CdfMetricsManager;
use Drupal\acquia_contenthub\Client\ClientFactory;
use Drupal\acquia_contenthub\ContentHubConnectionManager;
use Drupal\acquia_contenthub\Event\AcquiaContentHubUnregisterEvent;
use Drupal\Core\Url;
use Drush\Commands\DrushCommands;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Drush commands for interacting with Acquia Content Hub webhooks.
 *
 * @package Drupal\acquia_contenthub\Commands
 */
class AcquiaContentHubWebhookCommands extends DrushCommands {

  /**
   * The client factory.
   *
   * @var \Drupal\acquia_contenthub\Client\ClientFactory
   */
  protected $clientFactory;

  /**
   * The event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * The acquia_contenthub.connection_manager service.
   *
   * @var \Drupal\acquia_contenthub\ContentHubConnectionManager
   */
  protected $connectionManager;

  /**
   * The acquia_contenthub.cdf_metrics_manager service.
   *
   * @var \Drupal\acquia_contenthub\Client\CdfMetricsManager
   */
  protected $metrics;

  /**
   * AcquiaContentHubWebhookCommands constructor.
   *
   * @param \Drupal\acquia_contenthub\Client\ClientFactory $client_factory
   *   ACH client factory.
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
   *   Symfony event dispatcher.
   * @param \Drupal\acquia_contenthub\ContentHubConnectionManager $connection_manager
   *   The acquia_contenthub.connection_manager service.
   * @param \Drupal\acquia_contenthub\Client\CdfMetricsManager $metrics
   *   The acquia_contenthub.cdf_metrics_manager service.
   */
  public function __construct(ClientFactory $client_factory, EventDispatcherInterface $dispatcher, ContentHubConnectionManager $connection_manager, CdfMetricsManager $metrics) {
    $this->clientFactory = $client_factory;
    $this->eventDispatcher = $dispatcher;
    $this->connectionManager = $connection_manager;
    $this->metrics = $metrics;
  }

  /**
   * Display list of registered webhooks.
   *
   * @command acquia:contenthub-webhooks-list
   * @aliases ach-wh-list,acquia-contenthub-webhooks-list
   * @field-labels
   *    url: URL
   *    uuid: UUID
   *    client_uuid: Client UUID
   *    suppressed: Suppressed
   *    suppressed_until: Suppressed Until
   * @default-fields url,uuid,suppressed,suppressed_until
   *
   * @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
   *   Object of RowsOfFields.
   */
  public function contenthubWebhooksList() {
    $client = $this->clientFactory->getClient();
    if (!$client) {
      throw new \Exception(dt('The Content Hub client is not connected so the webhook list could not be retrieved.'));
    }

    $webhooks = $client->getWebHooks();
    if (empty($webhooks)) {
      $this->logger->warning(dt('You have no webhooks.'));
      return new RowsOfFields([]);
    }

    $rows_mapper = function ($webhook) {
      $suppressed_until = $webhook->getSuppressedUntil();
      return [
        'url' => $webhook->getUrl(),
        'uuid' => $webhook->getUuid(),
        'client_uuid' => $webhook->getClientUuid(),
        'suppressed' => !empty($suppressed_until),
        'suppressed_until' => !empty($suppressed_until) ? date('m/d/Y H:i:s', $webhook->getSuppressedUntil()) : NULL,
      ];
    };

    $result = array_map($rows_mapper, $webhooks);
    return new RowsOfFields($result);
  }

  /**
   * Perform a webhook management operation.
   *
   * @param string $op
   *   The operation to use. Options are: register, unregister, list.
   *
   * @command acquia:contenthub-webhooks
   * @aliases ach-wh,acquia-contenthub-webhooks
   *
   * @option webhook_url
   *   The webhook URL to register or unregister.
   * @default webhook_url null
   *
   * @usage acquia:contenthub-webhooks list
   *   Displays list of registered  webhooks.
   * @usage acquia:contenthub-webhooks register
   *   Registers new webhook. Current site url will be used.
   * @usage acquia:contenthub-webhooks register --webhook_url=http://example.com/acquia-contenthub/webhook
   *   Registers new webhook.
   * @usage acquia:contenthub-webhooks unregister
   *   Unregisters specified webhook. Current site url will be used.
   * @usage acquia:contenthub-webhooks unregister --webhook_url=http://example.com/acquia-contenthub/webhook
   *   Unregisters specified webhook.
   *
   * @throws \Exception
   */
  public function contenthubWebhooks($op) {
    $options = $this->input()->getOptions();

    $client = $this->clientFactory->getClient();
    if (!$client) {
      throw new \Exception(dt('The Content Hub client is not connected so the webhook operations could not be performed.'));
    }

    $webhook_url = $options['webhook_url'];
    if (empty($webhook_url)) {
      $webhook_url = Url::fromUri('internal:/acquia-contenthub/webhook', ['absolute' => TRUE])->toString();
    }

    switch ($op) {
      case 'register':
        $response = $this->connectionManager->registerWebhook($webhook_url);
        if (empty($response)) {
          return;
        }
        if (isset($response['success']) && FALSE === $response['success']) {
          $error_code = $response['error']['code'];
          if ($error_code === 4010) {
            $message = dt('Webhook was already registered (Code = @code): "@reason"', [
              '@code' => $response['error']['code'],
              '@reason' => $response['error']['message'],
            ]);
            $this->logger->notice($message);
          }
          else {
            $message = dt('Registering webhooks encountered an error. Error code: @code, Error Message: "@reason"', [
              '@code' => $response['error']['code'],
              '@reason' => $response['error']['message'],
            ]);
            $this->logger->error($message);
          }
          return;
        }
        $this->metrics->sendClientCdfUpdates();
        $this->logger->notice(
          dt('Registered Content Hub Webhook: @url | @uuid',
            ['@url' => $webhook_url, '@uuid' => $response['uuid']]
         ));
        break;

      case 'unregister':
        $webhooks = $client->getWebHooks();
        if (empty($webhooks)) {
          $this->logger->warning(dt('You have no webhooks.'));
          return;
        }

        /** @var \Acquia\ContentHubClient\Webhook|array $webhook */
        $webhook = $client->getWebHook($webhook_url);
        if (empty($webhook)) {
          $this->logger->warning(dt('Webhook @url not found', ['@url' => $webhook_url]));
          return;
        }

        $event = new AcquiaContentHubUnregisterEvent($webhook->getUuid(), '', TRUE);
        $this->eventDispatcher->dispatch($event, AcquiaContentHubEvents::ACH_UNREGISTER);

        $io = $this->io();
        $orphaned_filters = $event->getOrphanedFilters();
        if (!empty($orphaned_filters)) {
          $rows = [];

          foreach ($orphaned_filters as $name => $uuid) {
            $rows[] = [$name, $uuid];
          }

          $io->table(['Filter name', 'Filter UUID'], $rows);
          $answer = $io->choice(
            'What would you like to do with the filters? These can also be modified using the Discovery Interface',
            [
              'both' => 'Delete webhook and filters',
              'webhook' => 'Delete webhook only',
            ]
          );
        }

        $success = FALSE;

        // This runs if user picks "delete webhook only" or
        // there are no orphaned filters.
        if (!isset($answer) || $answer === 'webhook') {
          $success = $this->connectionManager->unregisterWebhook($event);
        }

        // This runs only if user picked "delete webhooks and filters" and
        // there are orphaned filters which should be deleted.
        if (isset($answer) && $answer === 'both') {
          $success = $this->connectionManager->unregisterWebhook($event, TRUE);
        }

        if (!$success) {
          $this->logger->warning(dt('There was an error unregistering the URL: @url', ['@url' => $webhook_url]));
          return;
        }

        $this->metrics->sendClientCdfUpdates();
        $this->logger->notice(dt('Successfully unregistered Content Hub Webhook: @url', ['@url' => $webhook_url]));
        break;

      case 'list':
        $webhooks = $client->getWebHooks();
        if (empty($webhooks)) {
          $this->logger->warning(dt('You have no webhooks.'));
          return;
        }

        $rows_mapper = function ($webhook, $index) {
          return [
            $index + 1,
            $webhook->getUrl(),
            $webhook->getUuid(),
            !empty($webhook->getSuppressedUntil()) ? 'Suppressed until ' . date('m/d/Y H:i:s', $webhook->getSuppressedUntil()) : 'NO',
          ];
        };
        $rows = array_map($rows_mapper, $webhooks, array_keys($webhooks));

        (new Table($this->output()))
          ->setHeaders(['#', 'URL', 'UUID', 'Suppressed'])
          ->setRows($rows)
          ->render();
        break;

      default:
        // Invalid operation.
        throw new \Exception(dt('The op "@op" is invalid', ['@op' => $op]));
    }
  }

}
