<?php

namespace Drupal\acquia_contenthub\Client;

use Acquia\ContentHubClient\MetaData\ClientMetaData;
use Drupal\acquia_contenthub\Libs\Common\EnsureContentHubClientTrait;
use Drupal\Core\Cache\CacheBackendInterface;
use Psr\Log\LoggerInterface;

/**
 * Client metadata manager.
 *
 * Handles updating the client metadata in service.
 */
class ClientMetaDataManager implements ClientMetaDataManagerInterface {

  use EnsureContentHubClientTrait;

  /**
   * Client metadata builder.
   *
   * @var \Drupal\acquia_contenthub\Client\ClientMetaDataBuilder
   */
  protected ClientMetaDataBuilder $clientMetaDataBuilder;

  /**
   * Content Hub logger.
   *
   * @var \Psr\Log\LoggerInterface
   */
  protected LoggerInterface $chLogger;

  /**
   * Acquia Content Hub cache bin.
   *
   * @var \Drupal\Core\Cache\CacheBackendInterface
   */
  protected CacheBackendInterface $cache;

  /**
   * ClientMetadataManager constructor.
   *
   * @param \Drupal\acquia_contenthub\Client\ClientMetaDataBuilder $client_metadata_builder
   *   Client metadata builder.
   * @param \Drupal\acquia_contenthub\Client\ClientFactory $client_factory
   *   Client factory.
   * @param \Psr\Log\LoggerInterface $ch_logger
   *   Content Hub logger.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
   *   Acquia Content Hub cache bin.
   *
   * @throws \Exception
   */
  public function __construct(ClientMetaDataBuilder $client_metadata_builder, ClientFactory $client_factory, LoggerInterface $ch_logger, CacheBackendInterface $cache) {
    $this->clientMetaDataBuilder = $client_metadata_builder;
    $this->clientFactory = $client_factory;
    $this->chLogger = $ch_logger;
    $this->cache = $cache;
  }

  /**
   * {@inheritDoc}
   *
   * @throws \Drupal\acquia_contenthub\Exception\ContentHubClientException
   * @throws \RuntimeException
   * @throws \Exception
   */
  public function updateClientMetadata(): bool {
    $client = $this->getClient();
    $client_uuid = $client->getSettings()->getUuid();
    $remote_metadata_array = $this->getRemoteMetaData($client_uuid);
    $remote_client_metadata = ClientMetaData::fromArray($remote_metadata_array);
    $local_client_metadata = $this->clientMetaDataBuilder->buildClientMetadata();
    // Explicitly not using strict comparison
    // as we only want to see if properties are equal.
    if ($remote_client_metadata == $local_client_metadata) {
      return FALSE;
    }
    $client->updateClient($client_uuid, NULL, $local_client_metadata);
    $local_metadata_array = $local_client_metadata->toArray();
    // Set the metadata in cache to avoid call to remote.
    $this->setMetaDataCache($client_uuid, $local_metadata_array);
    $this->chLogger->info('Client metadata has been updated. Metadata: {client_metadata}', [
      'client_metadata' => json_encode($local_metadata_array),
    ]);
    return TRUE;
  }

  /**
   * {@inheritDoc}
   *
   * @throws \RuntimeException
   * @throws \Exception
   */
  public function getRemoteMetaData(string $uuid): array {
    $client = $this->getClient();
    $remote_metadata = $this->cache->get("$uuid:remote_metadata");
    if ($remote_metadata) {
      return $remote_metadata->data;
    }
    $remote_client = $client->getClientByUuid($uuid);
    if (empty($remote_client)) {
      throw new \RuntimeException(sprintf('Client uuid: %s not available on remote.', $uuid));
    }
    $remote_metadata = $remote_client['metadata'] ?? [];
    $this->setMetaDataCache($uuid, $remote_metadata);
    return $remote_metadata;
  }

  /**
   * Sets metadata into cache.
   *
   * @param string $uuid
   *   Client uuid.
   * @param array $metadata
   *   Metadata array.
   */
  protected function setMetaDataCache(string $uuid, array $metadata): void {
    $this->cache->set("$uuid:remote_metadata", $metadata, time() + 3600);
  }

}
