<?php

namespace Drupal\acquia_contenthub\Libs\InterestList;

use Acquia\ContentHubClient\ContentHubClient;
use Drupal\acquia_contenthub\Client\ClientFactory;
use Drupal\acquia_contenthub\Exception\ContentHubClientException;
use Drupal\acquia_contenthub\Exception\ContentHubException;
use Drupal\Component\Utility\Crypt;

/**
 * Interest list storage.
 *
 * Fetches and caches entities in interest list.
 */
class InterestListStorage implements InterestListStorageInterface {

  /**
   * Cached interest list.
   *
   * @var array
   */
  protected array $cache = [];

  /**
   * Content Hub client.
   *
   * @var \Acquia\ContentHubClient\ContentHubClient|bool
   */
  protected $client;

  /**
   * InterestListStorage constructor.
   *
   * @param \Drupal\acquia_contenthub\Client\ClientFactory $client_factory
   *   Client factory.
   *
   * @throws \Exception
   */
  public function __construct(ClientFactory $client_factory) {
    $this->client = $client_factory->getClient();
  }

  /**
   * {@inheritDoc}
   */
  public function getInterestList(string $webhook_uuid, string $site_role, array $query = []): array {
    if (!empty($query['uuids'])) {
      $query['uuids'] = implode(',', $query['uuids']);
    }
    $cache_key = $this->getCacheKey($webhook_uuid, $site_role, $query);
    if (!empty($this->cache[$cache_key])) {
      return $this->cache[$cache_key];
    }

    $this->cache[$cache_key] = $this->fetchInterestList($webhook_uuid, $site_role, $query);
    return $this->cache[$cache_key];
  }

  /**
   * Calculates a base-64 encoded, URL-safe sha-256 hash.
   *
   * @param string $webhook_uuid
   *   Webhook uuid.
   * @param string $site_role
   *   Site role.
   * @param array $query
   *   Query params of interest list.
   *
   * @return string
   *   A base-64 encoded sha-256 hash.
   */
  public function getCacheKey(string $webhook_uuid, string $site_role, array $query): string {
    return Crypt::hashBase64($webhook_uuid . $site_role . json_encode($query));
  }

  /**
   * Fetches interest list from CH service.
   *
   * If query is empty, it will fetch all entities in the interest list using
   * pagination.
   *
   * @param string $webhook_uuid
   *   Webhook uuid.
   * @param string $site_role
   *   Site role.
   * @param array $query
   *   Query params of interest list.
   *
   * @return array
   *   An associate array keyed by the entity uuid.
   *
   * @throws \Drupal\acquia_contenthub\Exception\ContentHubClientException
   * @throws \Drupal\acquia_contenthub\Exception\ContentHubException
   * @throws \Exception
   */
  protected function fetchInterestList(string $webhook_uuid, string $site_role, array $query): array {
    $this->validateClient();
    if (!empty($query)) {
      return $this->client->getInterestList($webhook_uuid, $site_role, $query);
    }

    $interest_list = [];
    $query = [
      'from' => 0,
      'size' => self::INTEREST_LIST_PAGINATION_SIZE,
    ];
    do {
      $data = $this->client->getInterestList($webhook_uuid, $site_role, $query);
      $interest_list = array_merge($interest_list, $data);
      $query['from'] += self::INTEREST_LIST_PAGINATION_SIZE;
    } while (count($data) === self::INTEREST_LIST_PAGINATION_SIZE);

    return $interest_list;
  }

  /**
   * Validates client.
   *
   * @throws \Drupal\acquia_contenthub\Exception\ContentHubClientException
   * @throws \Drupal\acquia_contenthub\Exception\ContentHubException
   */
  protected function validateClient(): void {
    if (!$this->client instanceof ContentHubClient) {
      throw new ContentHubClientException('Error trying to connect to the Content Hub. Make sure this site is registered to Content hub.');
    }
    $settings = $this->client->getSettings();
    $webhook_uuid = $settings ? $settings->getWebhook('uuid') : '';
    if (!$webhook_uuid) {
      throw new ContentHubException('Webhook not available, unable to fetch interest list.');
    }
  }

  /**
   * {@inheritDoc}
   */
  public function resetCache(): void {
    $this->cache = [];
  }

  /**
   * {@inheritDoc}
   */
  public function isEntityInInterestList(string $webhook_uuid, string $entity_uuid, string $site_role): bool {
    try {
      $interests = $this->getInterestList($webhook_uuid, $site_role, ['uuids' => [$entity_uuid]]);
      return !empty($interests) && isset($interests[$entity_uuid]);
    }
    catch (\Exception $exception) {
      return FALSE;
    }
  }

  /**
   * {@inheritDoc}
   */
  public function isEntityDisabled(string $webhook_uuid, string $entity_uuid, string $site_role): bool {
    $disabled_entities = $this->getInterestList($webhook_uuid, $site_role, [
      'uuids' => [$entity_uuid],
      'disable_syndication' => TRUE,
    ]);
    return !empty($disabled_entities) && isset($disabled_entities[$entity_uuid]);
  }

  /**
   * {@inheritDoc}
   */
  public function filterDisabledEntities(string $webhook_uuid, array $uuids, string $site_role): array {
    $disabled_entities = $this->getInterestList($webhook_uuid, $site_role, [
      'uuids' => $uuids,
      'disable_syndication' => TRUE,
    ]);
    if (empty($disabled_entities)) {
      return $uuids;
    }
    $disabled_entities = array_keys($disabled_entities);
    return array_values(array_diff($uuids, $disabled_entities));
  }

}
