<?php

namespace Drupal\acquia_contenthub_subscriber;

use Acquia\ContentHubClient\Syndication\SyndicationEvents;
use Acquia\ContentHubClient\Syndication\SyndicationStatus;
use Drupal\acquia_contenthub\Client\CdfMetricsManager;
use Drupal\acquia_contenthub\Client\ClientFactory;
use Drupal\acquia_contenthub\Libs\Common\EnsureContentHubClientTrait;
use Drupal\acquia_contenthub\Libs\InterestList\InterestListStorageInterface;
use Drupal\acquia_contenthub\Libs\InterestList\InterestListTrait;
use Drupal\acquia_contenthub\Libs\Logging\ContentHubLoggerInterface;
use Drupal\acquia_contenthub\Settings\ContentHubConfigurationInterface;
use Drupal\acquia_contenthub_subscriber\Exception\ContentHubImportException;
use Drupal\depcalc\DependencyStack;

/**
 * Service for importing entities.
 *
 * @package Drupal\acquia_contenthub_subscriber
 */
class EntityImportService {

  use EnsureContentHubClientTrait;
  use InterestListTrait;

  /**
   * The role of the site.
   */
  protected const SITE_ROLE = 'subscriber';

  /**
   * The CDF importer object.
   *
   * @var \Drupal\acquia_contenthub_subscriber\CdfImporter
   */
  protected CdfImporter $importer;

  /**
   * The subscription tracker.
   *
   * @var \Drupal\acquia_contenthub_subscriber\SubscriberTracker
   */
  protected SubscriberTracker $tracker;

  /**
   * CH configurations.
   *
   * @var \Drupal\acquia_contenthub\Settings\ContentHubConfigurationInterface
   */
  protected ContentHubConfigurationInterface $achConfigurations;

  /**
   * Content Hub logger.
   *
   * @var \Drupal\acquia_contenthub\Libs\Logging\ContentHubLoggerInterface
   */
  protected ContentHubLoggerInterface $chLogger;

  /**
   * Cdf Metrics Manager.
   *
   * @var \Drupal\acquia_contenthub\Client\CdfMetricsManager
   */
  protected CdfMetricsManager $cdfMetricsManager;

  /**
   * Interest list storage.
   *
   * @var \Drupal\acquia_contenthub\Libs\InterestList\InterestListStorageInterface
   */
  protected InterestListStorageInterface $interestListStorage;

  /**
   * Constructs an EntityImportService object.
   *
   * @param \Drupal\acquia_contenthub_subscriber\CdfImporter $cdf_importer
   *   The CDF importer.
   * @param \Drupal\acquia_contenthub_subscriber\SubscriberTracker $tracker
   *   The subscription tracker.
   * @param \Drupal\acquia_contenthub\Settings\ContentHubConfigurationInterface $ch_configuration
   *   CH configuration.
   * @param \Drupal\acquia_contenthub\Libs\Logging\ContentHubLoggerInterface $ch_logger
   *   Content Hub logger service.
   * @param \Drupal\acquia_contenthub\Client\CdfMetricsManager $cdf_metrics_manager
   *   Cdf metrics manager.
   * @param \Drupal\acquia_contenthub\Libs\InterestList\InterestListStorageInterface $interest_list_storage
   *   Interest list storage.
   * @param \Drupal\acquia_contenthub\Client\ClientFactory $client_factory
   *   The Content Hub Client factory service.
   */
  public function __construct(CdfImporter $cdf_importer, SubscriberTracker $tracker, ContentHubConfigurationInterface $ch_configuration, ContentHubLoggerInterface $ch_logger, CdfMetricsManager $cdf_metrics_manager, InterestListStorageInterface $interest_list_storage, ClientFactory $client_factory) {
    $this->importer = $cdf_importer;
    $this->tracker = $tracker;
    $this->achConfigurations = $ch_configuration;
    $this->chLogger = $ch_logger;
    $this->cdfMetricsManager = $cdf_metrics_manager;
    $this->interestListStorage = $interest_list_storage;
    $this->clientFactory = $client_factory;
  }

  /**
   * Imports entities.
   *
   * @param array $uuids
   *   The entities to import.
   * @param string $webhook
   *   The webhook which the interest list belongs to.
   *
   * @return \Drupal\depcalc\DependencyStack|null
   *   Dependency stack if the process was successful.
   *
   * @throws \Drupal\acquia_contenthub\Exception\ContentHubClientException
   * @throws \Drupal\acquia_contenthub_subscriber\Exception\ContentHubImportException
   */
  public function importEntities(array $uuids, string $webhook): ?DependencyStack {
    try {
      $stack = $this->importer->importEntities(...$uuids);
      $this->cdfMetricsManager->sendClientCdfUpdates();
      return $stack;
    }
    catch (ContentHubImportException $e) {
      $e_uuids = $e->getUuids();
      $deletable = !array_diff($uuids, $e_uuids) && $e->isEntitiesMissing();
      $note = $e->getCode() === ContentHubImportException::MISSING_ENTITIES ?
        'Check export table, nullify hashes of entities marked as missing and try to re-export original entity/entities.' : 'N/A';
      if (!$deletable) {
        if ($this->achConfigurations->getContentHubConfig()->shouldSendContentHubUpdates()) {
          $event_ref = $this->chLogger->logError('Entity uuids: "{uuids}". Error: "{error}". Note: {note}', [
            'uuids' => implode(',', $e_uuids),
            'error' => $e->getMessage(),
            'note' => $note,
          ]);
          $uuids = $this->interestListStorage->filterDisabledEntities($webhook, $uuids, static::SITE_ROLE);
          $this->updateInterestList($webhook, $uuids, SyndicationStatus::IMPORT_FAILED, $event_ref);
        }
        else {
          // There are import problems but probably on dependent entities.
          $this->chLogger->getChannel()->error('Import failed: {error}.', ['error' => $e->getMessage()]);
        }

        $triggering_uuids = $e->getTriggeringUuids();
        array_walk($triggering_uuids, function (&$msg) use ($note) {
          $msg = sprintf('%s Note: %s', $msg, $note);
        });

        $this->chLogger->getEvent()->logMultipleEntityEvents(
          SyndicationEvents::IMPORT_FAILURE['severity'],
          $triggering_uuids,
          SyndicationEvents::IMPORT_FAILURE['name']
        );

        throw $e;
      }

      // The UUIDs can't be imported since they aren't in the Service.
      foreach ($uuids as $uuid) {
        $this->deleteFromTrackingTableAndInterestList($uuid, $webhook);
      }
    }
    catch (\Exception $default_exception) {
      $this->chLogger->logError('Error: {error}', ['error' => $default_exception->getMessage()]);
      throw $default_exception;
    }

    return NULL;
  }

  /**
   * Updates interest list.
   *
   * @param string $webhook
   *   The webhook uuid.
   * @param array $uuids
   *   Entity uuids to update.
   * @param string $status
   *   The status of the syndication.
   * @param string|null $event_ref
   *   The event reference.
   */
  public function updateInterestList(string $webhook, array $uuids, string $status, ?string $event_ref = NULL): void {
    $interest_list = $this->buildInterestList($uuids, $status, NULL, $event_ref);
    try {
      $this->getClient()->updateInterestListBySiteRole($webhook, static::SITE_ROLE, $interest_list);
    }
    catch (\Exception $e) {
      $this->chLogger->getChannel()->error('Could not update interest list. Reason: {reason}', ['reason' => $e->getMessage()]);
    }
  }

  /**
   * Adds dependencies to interest list.
   *
   * @param string $webhook
   *   The webhook to assign entities to.
   * @param array $uuids
   *   The entity uuids to add.
   * @param string|null $filter_uuid
   *   Filter uuid to include in the reason if there's one.
   */
  public function addDependenciesToInterestList(string $webhook, array $uuids, ?string $filter_uuid): void {
    $interest_list = $this->buildInterestList(
      $uuids,
      SyndicationStatus::IMPORT_SUCCESSFUL,
      $filter_uuid ?? 'manual'
    );
    try {
      $this->getClient()->addEntitiesToInterestListBySiteRole($webhook, static::SITE_ROLE, $interest_list);
      $this->chLogger->getChannel()->info(
        'The following imported entities have been added to the interest list on Content Hub for webhook {webhook}: {entity_uuids}.',
        [
          'webhook' => $webhook,
          'entity_uuids' => implode(', ', $uuids),
        ]
      );
    }
    catch (\Exception $e) {
      $this->chLogger->getChannel()->error(
        'Error adding the following entities to the interest list for webhook {webhook}: {entity_uuids}. Error message: {error}.',
        [
          'webhook' => $webhook,
          'entity_uuids' => implode(', ', $uuids),
          'error' => $e->getMessage(),
        ]
      );
    }
  }

  /**
   * Deletes entity from tracking table and interest list.
   *
   * @param string $uuid
   *   The uuid of the entity.
   * @param string $webhook
   *   The webhook the entity is related to.
   */
  protected function deleteFromTrackingTableAndInterestList(string $uuid, string $webhook): void {
    try {
      if ($this->tracker->getEntityByRemoteIdAndHash($uuid)) {
        return;
      }

      // If we cannot load, delete interest and tracking record.
      if ($this->achConfigurations->getContentHubConfig()->shouldSendContentHubUpdates()) {
        $this->getClient()->deleteInterest($uuid, $webhook);
      }
      $this->tracker->delete('entity_uuid', $uuid);
      $this->chLogger->getChannel()->info('The following entity was deleted from interest list and tracking table: {uuid}', ['uuid' => $uuid]);
    }
    catch (\Exception $ex) {
      $this->chLogger->getChannel()->error('Entity deletion from tracking table and interest list failed. Entity: {uuid}. Message: {error}', [
        'uuid' => $uuid,
        'error' => $ex->getMessage(),
      ]);
    }
  }

}
