<?php

// Code generated by OpenAPI Generator (https://openapi-generator.tech), manual changes will be lost - read more on https://github.com/algolia/api-clients-automation. DO NOT EDIT.

namespace Algolia\AlgoliaSearch\Api;

use Algolia\AlgoliaSearch\Algolia;
use Algolia\AlgoliaSearch\Configuration\SearchConfig;
use Algolia\AlgoliaSearch\Exceptions\ExceededRetriesException;
use Algolia\AlgoliaSearch\Exceptions\NotFoundException;
use Algolia\AlgoliaSearch\Exceptions\ValidUntilNotFoundException;
use Algolia\AlgoliaSearch\Iterators\ObjectIterator;
use Algolia\AlgoliaSearch\Iterators\RuleIterator;
use Algolia\AlgoliaSearch\Iterators\SynonymIterator;
use Algolia\AlgoliaSearch\Model\Search\AddApiKeyResponse;
use Algolia\AlgoliaSearch\Model\Search\ApiKey;
use Algolia\AlgoliaSearch\Model\Search\AssignUserIdParams;
use Algolia\AlgoliaSearch\Model\Search\BatchAssignUserIdsParams;
use Algolia\AlgoliaSearch\Model\Search\BatchDictionaryEntriesParams;
use Algolia\AlgoliaSearch\Model\Search\BatchParams;
use Algolia\AlgoliaSearch\Model\Search\BatchResponse;
use Algolia\AlgoliaSearch\Model\Search\BatchWriteParams;
use Algolia\AlgoliaSearch\Model\Search\BrowseParams;
use Algolia\AlgoliaSearch\Model\Search\BrowseResponse;
use Algolia\AlgoliaSearch\Model\Search\CreatedAtResponse;
use Algolia\AlgoliaSearch\Model\Search\DeleteApiKeyResponse;
use Algolia\AlgoliaSearch\Model\Search\DeleteByParams;
use Algolia\AlgoliaSearch\Model\Search\DeletedAtResponse;
use Algolia\AlgoliaSearch\Model\Search\DeleteSourceResponse;
use Algolia\AlgoliaSearch\Model\Search\DictionarySettingsParams;
use Algolia\AlgoliaSearch\Model\Search\GetApiKeyResponse;
use Algolia\AlgoliaSearch\Model\Search\GetDictionarySettingsResponse;
use Algolia\AlgoliaSearch\Model\Search\GetLogsResponse;
use Algolia\AlgoliaSearch\Model\Search\GetObjectsParams;
use Algolia\AlgoliaSearch\Model\Search\GetObjectsResponse;
use Algolia\AlgoliaSearch\Model\Search\GetTaskResponse;
use Algolia\AlgoliaSearch\Model\Search\GetTopUserIdsResponse;
use Algolia\AlgoliaSearch\Model\Search\HasPendingMappingsResponse;
use Algolia\AlgoliaSearch\Model\Search\IndexSettings;
use Algolia\AlgoliaSearch\Model\Search\Languages;
use Algolia\AlgoliaSearch\Model\Search\ListApiKeysResponse;
use Algolia\AlgoliaSearch\Model\Search\ListClustersResponse;
use Algolia\AlgoliaSearch\Model\Search\ListIndicesResponse;
use Algolia\AlgoliaSearch\Model\Search\ListUserIdsResponse;
use Algolia\AlgoliaSearch\Model\Search\MultipleBatchResponse;
use Algolia\AlgoliaSearch\Model\Search\OperationIndexParams;
use Algolia\AlgoliaSearch\Model\Search\RemoveUserIdResponse;
use Algolia\AlgoliaSearch\Model\Search\ReplaceSourceResponse;
use Algolia\AlgoliaSearch\Model\Search\Rule;
use Algolia\AlgoliaSearch\Model\Search\SaveObjectResponse;
use Algolia\AlgoliaSearch\Model\Search\SaveSynonymResponse;
use Algolia\AlgoliaSearch\Model\Search\SearchDictionaryEntriesParams;
use Algolia\AlgoliaSearch\Model\Search\SearchDictionaryEntriesResponse;
use Algolia\AlgoliaSearch\Model\Search\SearchForFacetValuesRequest;
use Algolia\AlgoliaSearch\Model\Search\SearchForFacetValuesResponse;
use Algolia\AlgoliaSearch\Model\Search\SearchMethodParams;
use Algolia\AlgoliaSearch\Model\Search\SearchParams;
use Algolia\AlgoliaSearch\Model\Search\SearchResponse;
use Algolia\AlgoliaSearch\Model\Search\SearchResponses;
use Algolia\AlgoliaSearch\Model\Search\SearchRulesParams;
use Algolia\AlgoliaSearch\Model\Search\SearchRulesResponse;
use Algolia\AlgoliaSearch\Model\Search\SearchSynonymsParams;
use Algolia\AlgoliaSearch\Model\Search\SearchSynonymsResponse;
use Algolia\AlgoliaSearch\Model\Search\SearchUserIdsParams;
use Algolia\AlgoliaSearch\Model\Search\SearchUserIdsResponse;
use Algolia\AlgoliaSearch\Model\Search\SettingsResponse;
use Algolia\AlgoliaSearch\Model\Search\Source;
use Algolia\AlgoliaSearch\Model\Search\SynonymHit;
use Algolia\AlgoliaSearch\Model\Search\UpdateApiKeyResponse;
use Algolia\AlgoliaSearch\Model\Search\UpdatedAtResponse;
use Algolia\AlgoliaSearch\Model\Search\UpdatedAtWithObjectIdResponse;
use Algolia\AlgoliaSearch\Model\Search\UserId;
use Algolia\AlgoliaSearch\ObjectSerializer;
use Algolia\AlgoliaSearch\RetryStrategy\ApiWrapper;
use Algolia\AlgoliaSearch\RetryStrategy\ApiWrapperInterface;
use Algolia\AlgoliaSearch\RetryStrategy\ClusterHosts;
use Algolia\AlgoliaSearch\Support\Helpers;
use GuzzleHttp\Psr7\Query;

/**
 * SearchClient Class Doc Comment.
 *
 * @category Class
 */
class SearchClient
{
    public const VERSION = '4.21.0';

    /**
     * @var ApiWrapperInterface
     */
    protected $api;

    /**
     * @var SearchConfig
     */
    protected $config;

    public function __construct(ApiWrapperInterface $apiWrapper, SearchConfig $config)
    {
        $this->config = $config;
        $this->api = $apiWrapper;
    }

    /**
     * Instantiate the client with basic credentials.
     *
     * @param string $appId  Application ID
     * @param string $apiKey Algolia API Key
     */
    public static function create($appId = null, $apiKey = null)
    {
        return static::createWithConfig(SearchConfig::create($appId, $apiKey));
    }

    /**
     * Instantiate the client with configuration.
     *
     * @param SearchConfig $config Configuration
     */
    public static function createWithConfig(SearchConfig $config)
    {
        $config = clone $config;

        $apiWrapper = new ApiWrapper(
            Algolia::getHttpClient(),
            $config,
            self::getClusterHosts($config)
        );

        return new static($apiWrapper, $config);
    }

    /**
     * Gets the cluster hosts depending on the config.
     *
     * @return ClusterHosts
     */
    public static function getClusterHosts(SearchConfig $config)
    {
        $cacheKey = sprintf('%s-clusterHosts-%s', __CLASS__, $config->getAppId());

        if ($hosts = $config->getHosts()) {
            // If a list of hosts was passed, we ignore the cache
            $clusterHosts = ClusterHosts::create($hosts);
        } elseif (false === ($clusterHosts = ClusterHosts::createFromCache($cacheKey))) {
            // We'll try to restore the ClusterHost from cache, if we cannot
            // we create a new instance and set the cache key
            $clusterHosts = ClusterHosts::createFromAppId($config->getAppId())
                ->setCacheKey($cacheKey)
            ;
        }

        return $clusterHosts;
    }

    /**
     * @return SearchConfig
     */
    public function getClientConfig()
    {
        return $this->config;
    }

    /**
     * Stub method setting a new API key to authenticate requests.
     *
     * @param string $apiKey
     */
    public function setClientApiKey($apiKey)
    {
        $this->config->setClientApiKey($apiKey);
    }

    /**
     * Creates a new API key with specific permissions and restrictions.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param ApiKey|array $apiKey apiKey (required)
     *                             - $apiKey['acl'] => (array) Permissions that determine the type of API requests this key can make. The required ACL is listed in each endpoint's reference. For more information, see [access control list](https://www.algolia.com/doc/guides/security/api-keys/#access-control-list-acl). (required)
     *                             - $apiKey['description'] => (string) Description of an API key to help you identify this API key.
     *                             - $apiKey['indexes'] => (array) Index names or patterns that this API key can access. By default, an API key can access all indices in the same application.  You can use leading and trailing wildcard characters (`*`):  - `dev_*` matches all indices starting with \"dev_\". - `*_dev` matches all indices ending with \"_dev\". - `*_products_*` matches all indices containing \"_products_\".
     *                             - $apiKey['maxHitsPerQuery'] => (int) Maximum number of results this API key can retrieve in one query. By default, there's no limit.
     *                             - $apiKey['maxQueriesPerIPPerHour'] => (int) Maximum number of API requests allowed per IP address or [user token](https://www.algolia.com/doc/guides/sending-events/concepts/usertoken/) per hour.  If this limit is reached, the API returns an error with status code `429`. By default, there's no limit.
     *                             - $apiKey['queryParameters'] => (string) Query parameters to add when making API requests with this API key.  To restrict this API key to specific IP addresses, add the `restrictSources` parameter. You can only add a single source, but you can provide a range of IP addresses.  Creating an API key fails if the request is made from an IP address outside the restricted range.
     *                             - $apiKey['referers'] => (array) Allowed HTTP referrers for this API key.  By default, all referrers are allowed. You can use leading and trailing wildcard characters (`*`):  - `https://algolia.com/_*` allows all referrers starting with \"https://algolia.com/\" - `*.algolia.com` allows all referrers ending with \".algolia.com\" - `*algolia.com*` allows all referrers in the domain \"algolia.com\".  Like all HTTP headers, referrers can be spoofed. Don't rely on them to secure your data. For more information, see [HTTP referrer restrictions](https://www.algolia.com/doc/guides/security/security-best-practices/#http-referrers-restrictions).
     *                             - $apiKey['validity'] => (int) Duration (in seconds) after which the API key expires. By default, API keys don't expire.
     *
     * @see ApiKey
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return AddApiKeyResponse|array<string, mixed>
     */
    public function addApiKey($apiKey, $requestOptions = [])
    {
        // verify the required parameter 'apiKey' is set
        if (!isset($apiKey)) {
            throw new \InvalidArgumentException(
                'Parameter `apiKey` is required when calling `addApiKey`.'
            );
        }

        $resourcePath = '/1/keys';
        $queryParameters = [];
        $headers = [];
        $httpBody = $apiKey;

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * If a record with the specified object ID exists, the existing record is replaced. Otherwise, a new record is added to the index.  If you want to use auto-generated object IDs, use the [`saveObject` operation](#tag/Records/operation/saveObject). To update _some_ attributes of an existing record, use the [`partial` operation](#tag/Records/operation/partialUpdateObject) instead. To add, update, or replace multiple records, use the [`batch` operation](#tag/Records/operation/batch).
     *
     * Required API Key ACLs:
     *  - addObject
     *
     * @param string $indexName      Name of the index on which to perform the operation. (required)
     * @param string $objectID       Unique record identifier. (required)
     * @param array  $body           The record. A schemaless object with attributes that are useful in the context of search and discovery. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtWithObjectIdResponse
     */
    public function addOrUpdateObject($indexName, $objectID, $body, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `addOrUpdateObject`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `addOrUpdateObject`.'
            );
        }
        // verify the required parameter 'body' is set
        if (!isset($body)) {
            throw new \InvalidArgumentException(
                'Parameter `body` is required when calling `addOrUpdateObject`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/{objectID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = $body;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('PUT', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Adds a source to the list of allowed sources.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param array|Source $source Source to add. (required)
     *                             - $source['source'] => (string) IP address range of the source. (required)
     *                             - $source['description'] => (string) Source description.
     *
     * @see Source
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|CreatedAtResponse
     */
    public function appendSource($source, $requestOptions = [])
    {
        // verify the required parameter 'source' is set
        if (!isset($source)) {
            throw new \InvalidArgumentException(
                'Parameter `source` is required when calling `appendSource`.'
            );
        }

        $resourcePath = '/1/security/sources/append';
        $queryParameters = [];
        $headers = [];
        $httpBody = $source;

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Assigns or moves a user ID to a cluster.  The time it takes to move a user is proportional to the amount of data linked to the user ID.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param string                   $xAlgoliaUserID     Unique identifier of the user who makes the search request. (required)
     * @param array|AssignUserIdParams $assignUserIdParams assignUserIdParams (required)
     *                                                     - $assignUserIdParams['cluster'] => (string) Cluster name. (required)
     *
     * @see AssignUserIdParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|CreatedAtResponse
     *
     * @deprecated
     */
    public function assignUserId($xAlgoliaUserID, $assignUserIdParams, $requestOptions = [])
    {
        // verify the required parameter 'xAlgoliaUserID' is set
        if (!isset($xAlgoliaUserID)) {
            throw new \InvalidArgumentException(
                'Parameter `xAlgoliaUserID` is required when calling `assignUserId`.'
            );
        }
        // verify the required parameter 'assignUserIdParams' is set
        if (!isset($assignUserIdParams)) {
            throw new \InvalidArgumentException(
                'Parameter `assignUserIdParams` is required when calling `assignUserId`.'
            );
        }

        $resourcePath = '/1/clusters/mapping';
        $queryParameters = [];
        $headers = [];
        $httpBody = $assignUserIdParams;

        $headers['X-Algolia-User-ID'] = $xAlgoliaUserID;

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Adds, updates, or deletes records in one index with a single API request.  Batching index updates reduces latency and increases data integrity.  - Actions are applied in the order they're specified. - Actions are equivalent to the individual API requests of the same name.  This operation is subject to [indexing rate limits](https://support.algolia.com/hc/en-us/articles/4406975251089-Is-there-a-rate-limit-for-indexing-on-Algolia).
     *
     * @param string                 $indexName        Name of the index on which to perform the operation. (required)
     * @param array|BatchWriteParams $batchWriteParams batchWriteParams (required)
     *                                                 - $batchWriteParams['requests'] => (array)  (required)
     *
     * @see BatchWriteParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|BatchResponse
     */
    public function batch($indexName, $batchWriteParams, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `batch`.'
            );
        }
        // verify the required parameter 'batchWriteParams' is set
        if (!isset($batchWriteParams)) {
            throw new \InvalidArgumentException(
                'Parameter `batchWriteParams` is required when calling `batch`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/batch';
        $queryParameters = [];
        $headers = [];
        $httpBody = $batchWriteParams;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Assigns multiple user IDs to a cluster.  **You can't move users with this operation**.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param string                         $xAlgoliaUserID           Unique identifier of the user who makes the search request. (required)
     * @param array|BatchAssignUserIdsParams $batchAssignUserIdsParams batchAssignUserIdsParams (required)
     *                                                                 - $batchAssignUserIdsParams['cluster'] => (string) Cluster name. (required)
     *                                                                 - $batchAssignUserIdsParams['users'] => (array) User IDs to assign. (required)
     *
     * @see BatchAssignUserIdsParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|CreatedAtResponse
     *
     * @deprecated
     */
    public function batchAssignUserIds($xAlgoliaUserID, $batchAssignUserIdsParams, $requestOptions = [])
    {
        // verify the required parameter 'xAlgoliaUserID' is set
        if (!isset($xAlgoliaUserID)) {
            throw new \InvalidArgumentException(
                'Parameter `xAlgoliaUserID` is required when calling `batchAssignUserIds`.'
            );
        }
        // verify the required parameter 'batchAssignUserIdsParams' is set
        if (!isset($batchAssignUserIdsParams)) {
            throw new \InvalidArgumentException(
                'Parameter `batchAssignUserIdsParams` is required when calling `batchAssignUserIds`.'
            );
        }

        $resourcePath = '/1/clusters/mapping/batch';
        $queryParameters = [];
        $headers = [];
        $httpBody = $batchAssignUserIdsParams;

        $headers['X-Algolia-User-ID'] = $xAlgoliaUserID;

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Adds or deletes multiple entries from your plurals, segmentation, or stop word dictionaries.
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param array                              $dictionaryName               Dictionary type in which to search. (required)
     * @param array|BatchDictionaryEntriesParams $batchDictionaryEntriesParams batchDictionaryEntriesParams (required)
     *                                                                         - $batchDictionaryEntriesParams['clearExistingDictionaryEntries'] => (bool) Whether to replace all custom entries in the dictionary with the ones sent with this request.
     *                                                                         - $batchDictionaryEntriesParams['requests'] => (array) List of additions and deletions to your dictionaries. (required)
     *
     * @see BatchDictionaryEntriesParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function batchDictionaryEntries($dictionaryName, $batchDictionaryEntriesParams, $requestOptions = [])
    {
        // verify the required parameter 'dictionaryName' is set
        if (!isset($dictionaryName)) {
            throw new \InvalidArgumentException(
                'Parameter `dictionaryName` is required when calling `batchDictionaryEntries`.'
            );
        }
        // verify the required parameter 'batchDictionaryEntriesParams' is set
        if (!isset($batchDictionaryEntriesParams)) {
            throw new \InvalidArgumentException(
                'Parameter `batchDictionaryEntriesParams` is required when calling `batchDictionaryEntries`.'
            );
        }

        $resourcePath = '/1/dictionaries/{dictionaryName}/batch';
        $queryParameters = [];
        $headers = [];
        $httpBody = $batchDictionaryEntriesParams;

        // path params
        if (null !== $dictionaryName) {
            $resourcePath = str_replace(
                '{dictionaryName}',
                ObjectSerializer::toPathValue($dictionaryName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Retrieves records from an index, up to 1,000 per request.  While searching retrieves _hits_ (records augmented with attributes for highlighting and ranking details), browsing _just_ returns matching records. This can be useful if you want to export your indices.  - The Analytics API doesn't collect data when using `browse`. - Records are ranked by attributes and custom ranking. - There's no ranking for: typo-tolerance, number of matched words, proximity, geo distance.  Browse requests automatically apply these settings:  - `advancedSyntax`: `false` - `attributesToHighlight`: `[]` - `attributesToSnippet`: `[]` - `distinct`: `false` - `enablePersonalization`: `false` - `enableRules`: `false` - `facets`: `[]` - `getRankingInfo`: `false` - `ignorePlurals`: `false` - `optionalFilters`: `[]` - `typoTolerance`: `true` or `false` (`min` and `strict` evaluate to `true`)  If you send these parameters with your browse requests, they'll be ignored.
     *
     * Required API Key ACLs:
     *  - browse
     *
     * @param string             $indexName    Name of the index on which to perform the operation. (required)
     * @param array|BrowseParams $browseParams browseParams (optional)
     *
     * @see BrowseParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|BrowseResponse
     */
    public function browse($indexName, $browseParams = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `browse`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/browse';
        $queryParameters = [];
        $headers = [];
        $httpBody = isset($browseParams) ? $browseParams : [];

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, true);
    }

    /**
     * Deletes only the records from an index while keeping settings, synonyms, and rules. This operation is resource-intensive and subject to [indexing rate limits](https://support.algolia.com/hc/en-us/articles/4406975251089-Is-there-a-rate-limit-for-indexing-on-Algolia).
     *
     * Required API Key ACLs:
     *  - deleteIndex
     *
     * @param string $indexName      Name of the index on which to perform the operation. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function clearObjects($indexName, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `clearObjects`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/clear';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Deletes all rules from the index.
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param string $indexName         Name of the index on which to perform the operation. (required)
     * @param bool   $forwardToReplicas Whether changes are applied to replica indices. (optional)
     * @param array  $requestOptions    the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function clearRules($indexName, $forwardToReplicas = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `clearRules`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/rules/clear';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $forwardToReplicas) {
            $queryParameters['forwardToReplicas'] = $forwardToReplicas;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Deletes all synonyms from the index.
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param string $indexName         Name of the index on which to perform the operation. (required)
     * @param bool   $forwardToReplicas Whether changes are applied to replica indices. (optional)
     * @param array  $requestOptions    the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function clearSynonyms($indexName, $forwardToReplicas = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `clearSynonyms`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/synonyms/clear';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $forwardToReplicas) {
            $queryParameters['forwardToReplicas'] = $forwardToReplicas;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * This method lets you send requests to the Algolia REST API.
     *
     * @param string $path           Path of the endpoint, anything after \"/1\" must be specified. (required)
     * @param array  $parameters     Query parameters to apply to the current query. (optional)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|object
     */
    public function customDelete($path, $parameters = null, $requestOptions = [])
    {
        // verify the required parameter 'path' is set
        if (!isset($path)) {
            throw new \InvalidArgumentException(
                'Parameter `path` is required when calling `customDelete`.'
            );
        }

        $resourcePath = '/{path}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $parameters) {
            $queryParameters = $parameters;
        }

        // path params
        if (null !== $path) {
            $resourcePath = str_replace(
                '{path}',
                $path,
                $resourcePath
            );
        }

        return $this->sendRequest('DELETE', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * This method lets you send requests to the Algolia REST API.
     *
     * @param string $path           Path of the endpoint, anything after \"/1\" must be specified. (required)
     * @param array  $parameters     Query parameters to apply to the current query. (optional)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|object
     */
    public function customGet($path, $parameters = null, $requestOptions = [])
    {
        // verify the required parameter 'path' is set
        if (!isset($path)) {
            throw new \InvalidArgumentException(
                'Parameter `path` is required when calling `customGet`.'
            );
        }

        $resourcePath = '/{path}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $parameters) {
            $queryParameters = $parameters;
        }

        // path params
        if (null !== $path) {
            $resourcePath = str_replace(
                '{path}',
                $path,
                $resourcePath
            );
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * This method lets you send requests to the Algolia REST API.
     *
     * @param string $path           Path of the endpoint, anything after \"/1\" must be specified. (required)
     * @param array  $parameters     Query parameters to apply to the current query. (optional)
     * @param array  $body           Parameters to send with the custom request. (optional)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|object
     */
    public function customPost($path, $parameters = null, $body = null, $requestOptions = [])
    {
        // verify the required parameter 'path' is set
        if (!isset($path)) {
            throw new \InvalidArgumentException(
                'Parameter `path` is required when calling `customPost`.'
            );
        }

        $resourcePath = '/{path}';
        $queryParameters = [];
        $headers = [];
        $httpBody = isset($body) ? $body : [];

        if (null !== $parameters) {
            $queryParameters = $parameters;
        }

        // path params
        if (null !== $path) {
            $resourcePath = str_replace(
                '{path}',
                $path,
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * This method lets you send requests to the Algolia REST API.
     *
     * @param string $path           Path of the endpoint, anything after \"/1\" must be specified. (required)
     * @param array  $parameters     Query parameters to apply to the current query. (optional)
     * @param array  $body           Parameters to send with the custom request. (optional)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|object
     */
    public function customPut($path, $parameters = null, $body = null, $requestOptions = [])
    {
        // verify the required parameter 'path' is set
        if (!isset($path)) {
            throw new \InvalidArgumentException(
                'Parameter `path` is required when calling `customPut`.'
            );
        }

        $resourcePath = '/{path}';
        $queryParameters = [];
        $headers = [];
        $httpBody = isset($body) ? $body : [];

        if (null !== $parameters) {
            $queryParameters = $parameters;
        }

        // path params
        if (null !== $path) {
            $resourcePath = str_replace(
                '{path}',
                $path,
                $resourcePath
            );
        }

        return $this->sendRequest('PUT', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Deletes the API key.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param string $key            API key. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|DeleteApiKeyResponse
     */
    public function deleteApiKey($key, $requestOptions = [])
    {
        // verify the required parameter 'key' is set
        if (!isset($key)) {
            throw new \InvalidArgumentException(
                'Parameter `key` is required when calling `deleteApiKey`.'
            );
        }

        $resourcePath = '/1/keys/{key}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $key) {
            $resourcePath = str_replace(
                '{key}',
                ObjectSerializer::toPathValue($key),
                $resourcePath
            );
        }

        return $this->sendRequest('DELETE', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * This operation doesn't accept empty filters.  This operation is resource-intensive. You should only use it if you can't get the object IDs of the records you want to delete. It's more efficient to get a list of object IDs with the [`browse` operation](#tag/Search/operation/browse), and then delete the records using the [`batch` operation](#tag/Records/operation/batch).  This operation is subject to [indexing rate limits](https://support.algolia.com/hc/en-us/articles/4406975251089-Is-there-a-rate-limit-for-indexing-on-Algolia).
     *
     * Required API Key ACLs:
     *  - deleteIndex
     *
     * @param string               $indexName      Name of the index on which to perform the operation. (required)
     * @param array|DeleteByParams $deleteByParams deleteByParams (required)
     *                                             - $deleteByParams['facetFilters'] => (array)
     *                                             - $deleteByParams['filters'] => (string) Filter expression to only include items that match the filter criteria in the response.  You can use these filter expressions:  - **Numeric filters.** `<facet> <op> <number>`, where `<op>` is one of `<`, `<=`, `=`, `!=`, `>`, `>=`. - **Ranges.** `<facet>:<lower> TO <upper>` where `<lower>` and `<upper>` are the lower and upper limits of the range (inclusive). - **Facet filters.** `<facet>:<value>` where `<facet>` is a facet attribute (case-sensitive) and `<value>` a facet value. - **Tag filters.** `_tags:<value>` or just `<value>` (case-sensitive). - **Boolean filters.** `<facet>: true | false`.  You can combine filters with `AND`, `OR`, and `NOT` operators with the following restrictions:  - You can only combine filters of the same type with `OR`.   **Not supported:** `facet:value OR num > 3`. - You can't use `NOT` with combinations of filters.   **Not supported:** `NOT(facet:value OR facet:value)` - You can't combine conjunctions (`AND`) with `OR`.   **Not supported:** `facet:value OR (facet:value AND facet:value)`  Use quotes around your filters, if the facet attribute name or facet value has spaces, keywords (`OR`, `AND`, `NOT`), or quotes. If a facet attribute is an array, the filter matches if it matches at least one element of the array.  For more information, see [Filters](https://www.algolia.com/doc/guides/managing-results/refine-results/filtering/).
     *                                             - $deleteByParams['numericFilters'] => (array)
     *                                             - $deleteByParams['tagFilters'] => (array)
     *                                             - $deleteByParams['aroundLatLng'] => (string) Coordinates for the center of a circle, expressed as a comma-separated string of latitude and longitude.  Only records included within a circle around this central location are included in the results. The radius of the circle is determined by the `aroundRadius` and `minimumAroundRadius` settings. This parameter is ignored if you also specify `insidePolygon` or `insideBoundingBox`.
     *                                             - $deleteByParams['aroundRadius'] => (array)
     *                                             - $deleteByParams['insideBoundingBox'] => (array)
     *                                             - $deleteByParams['insidePolygon'] => (array) Coordinates of a polygon in which to search.  Polygons are defined by 3 to 10,000 points. Each point is represented by its latitude and longitude. Provide multiple polygons as nested arrays. For more information, see [filtering inside polygons](https://www.algolia.com/doc/guides/managing-results/refine-results/geolocation/#filtering-inside-rectangular-or-polygonal-areas). This parameter is ignored if you also specify `insideBoundingBox`.
     *
     * @see DeleteByParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function deleteBy($indexName, $deleteByParams, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `deleteBy`.'
            );
        }
        // verify the required parameter 'deleteByParams' is set
        if (!isset($deleteByParams)) {
            throw new \InvalidArgumentException(
                'Parameter `deleteByParams` is required when calling `deleteBy`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/deleteByQuery';
        $queryParameters = [];
        $headers = [];
        $httpBody = $deleteByParams;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Deletes an index and all its settings.  - Deleting an index doesn't delete its analytics data. - If you try to delete a non-existing index, the operation is ignored without warning. - If the index you want to delete has replica indices, the replicas become independent indices. - If the index you want to delete is a replica index, you must first unlink it from its primary index before you can delete it.   For more information, see [Delete replica indices](https://www.algolia.com/doc/guides/managing-results/refine-results/sorting/how-to/deleting-replicas/).
     *
     * Required API Key ACLs:
     *  - deleteIndex
     *
     * @param string $indexName      Name of the index on which to perform the operation. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|DeletedAtResponse
     */
    public function deleteIndex($indexName, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `deleteIndex`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('DELETE', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Deletes a record by its object ID.  To delete more than one record, use the [`batch` operation](#tag/Records/operation/batch). To delete records matching a query, use the [`deleteBy` operation](#tag/Records/operation/deleteBy).
     *
     * Required API Key ACLs:
     *  - deleteObject
     *
     * @param string $indexName      Name of the index on which to perform the operation. (required)
     * @param string $objectID       Unique record identifier. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|DeletedAtResponse
     */
    public function deleteObject($indexName, $objectID, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `deleteObject`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `deleteObject`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/{objectID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('DELETE', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Deletes a rule by its ID. To find the object ID for rules, use the [`search` operation](#tag/Rules/operation/searchRules).
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param string $indexName         Name of the index on which to perform the operation. (required)
     * @param string $objectID          Unique identifier of a rule object. (required)
     * @param bool   $forwardToReplicas Whether changes are applied to replica indices. (optional)
     * @param array  $requestOptions    the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function deleteRule($indexName, $objectID, $forwardToReplicas = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `deleteRule`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `deleteRule`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/rules/{objectID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $forwardToReplicas) {
            $queryParameters['forwardToReplicas'] = $forwardToReplicas;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('DELETE', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Deletes a source from the list of allowed sources.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param string $source         IP address range of the source. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|DeleteSourceResponse
     */
    public function deleteSource($source, $requestOptions = [])
    {
        // verify the required parameter 'source' is set
        if (!isset($source)) {
            throw new \InvalidArgumentException(
                'Parameter `source` is required when calling `deleteSource`.'
            );
        }

        $resourcePath = '/1/security/sources/{source}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $source) {
            $resourcePath = str_replace(
                '{source}',
                ObjectSerializer::toPathValue($source),
                $resourcePath
            );
        }

        return $this->sendRequest('DELETE', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Deletes a synonym by its ID. To find the object IDs of your synonyms, use the [`search` operation](#tag/Synonyms/operation/searchSynonyms).
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param string $indexName         Name of the index on which to perform the operation. (required)
     * @param string $objectID          Unique identifier of a synonym object. (required)
     * @param bool   $forwardToReplicas Whether changes are applied to replica indices. (optional)
     * @param array  $requestOptions    the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|DeletedAtResponse
     */
    public function deleteSynonym($indexName, $objectID, $forwardToReplicas = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `deleteSynonym`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `deleteSynonym`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/synonyms/{objectID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $forwardToReplicas) {
            $queryParameters['forwardToReplicas'] = $forwardToReplicas;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('DELETE', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Gets the permissions and restrictions of an API key.  When authenticating with the admin API key, you can request information for any of your application's keys. When authenticating with other API keys, you can only retrieve information for that key, with the description replaced by `<redacted>`.
     *
     * @param string $key            API key. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|GetApiKeyResponse
     */
    public function getApiKey($key, $requestOptions = [])
    {
        // verify the required parameter 'key' is set
        if (!isset($key)) {
            throw new \InvalidArgumentException(
                'Parameter `key` is required when calling `getApiKey`.'
            );
        }

        $resourcePath = '/1/keys/{key}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $key) {
            $resourcePath = str_replace(
                '{key}',
                ObjectSerializer::toPathValue($key),
                $resourcePath
            );
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Checks the status of a given application task.
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param int   $taskID         Unique task identifier. (required)
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|GetTaskResponse
     */
    public function getAppTask($taskID, $requestOptions = [])
    {
        // verify the required parameter 'taskID' is set
        if (!isset($taskID)) {
            throw new \InvalidArgumentException(
                'Parameter `taskID` is required when calling `getAppTask`.'
            );
        }

        $resourcePath = '/1/task/{taskID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $taskID) {
            $resourcePath = str_replace(
                '{taskID}',
                ObjectSerializer::toPathValue($taskID),
                $resourcePath
            );
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Lists supported languages with their supported dictionary types and number of custom entries.
     *
     * Required API Key ACLs:
     *  - settings
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|array<string,Languages>
     */
    public function getDictionaryLanguages($requestOptions = [])
    {
        $resourcePath = '/1/dictionaries/*/languages';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Retrieves the languages for which standard dictionary entries are turned off.
     *
     * Required API Key ACLs:
     *  - settings
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|GetDictionarySettingsResponse
     */
    public function getDictionarySettings($requestOptions = [])
    {
        $resourcePath = '/1/dictionaries/*/settings';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * The request must be authenticated by an API key with the [`logs` ACL](https://www.algolia.com/doc/guides/security/api-keys/#access-control-list-acl).  - Logs are held for the last seven days. - Up to 1,000 API requests per server are logged. - This request counts towards your [operations quota](https://support.algolia.com/hc/en-us/articles/4406981829777-How-does-Algolia-count-records-and-operations-) but doesn't appear in the logs itself.
     *
     * Required API Key ACLs:
     *  - logs
     *
     * @param int    $offset         First log entry to retrieve. The most recent entries are listed first. (optional, default to 0)
     * @param int    $length         Maximum number of entries to retrieve. (optional, default to 10)
     * @param string $indexName      Index for which to retrieve log entries. By default, log entries are retrieved for all indices. (optional)
     * @param array  $type           Type of log entries to retrieve. By default, all log entries are retrieved. (optional)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|GetLogsResponse
     */
    public function getLogs($offset = null, $length = null, $indexName = null, $type = null, $requestOptions = [])
    {
        $resourcePath = '/1/logs';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $offset) {
            $queryParameters['offset'] = $offset;
        }

        if (null !== $length) {
            $queryParameters['length'] = $length;
        }

        if (null !== $indexName) {
            $queryParameters['indexName'] = $indexName;
        }

        if (null !== $type) {
            $queryParameters['type'] = $type;
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Retrieves one record by its object ID.  To retrieve more than one record, use the [`objects` operation](#tag/Records/operation/getObjects).
     *
     * Required API Key ACLs:
     *  - search
     *
     * @param string $indexName            Name of the index on which to perform the operation. (required)
     * @param string $objectID             Unique record identifier. (required)
     * @param array  $attributesToRetrieve Attributes to include with the records in the response. This is useful to reduce the size of the API response. By default, all retrievable attributes are returned.  `objectID` is always retrieved.  Attributes included in `unretrievableAttributes` won't be retrieved unless the request is authenticated with the admin API key. (optional)
     * @param array  $requestOptions       the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|object
     */
    public function getObject($indexName, $objectID, $attributesToRetrieve = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `getObject`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `getObject`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/{objectID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $attributesToRetrieve) {
            $queryParameters['attributesToRetrieve'] = $attributesToRetrieve;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Retrieves one or more records, potentially from different indices.  Records are returned in the same order as the requests.
     *
     * Required API Key ACLs:
     *  - search
     *
     * @param array|GetObjectsParams $getObjectsParams Request object. (required)
     *                                                 - $getObjectsParams['requests'] => (array)  (required)
     *
     * @see GetObjectsParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|GetObjectsResponse
     */
    public function getObjects($getObjectsParams, $requestOptions = [])
    {
        // verify the required parameter 'getObjectsParams' is set
        if (!isset($getObjectsParams)) {
            throw new \InvalidArgumentException(
                'Parameter `getObjectsParams` is required when calling `getObjects`.'
            );
        }

        $resourcePath = '/1/indexes/*/objects';
        $queryParameters = [];
        $headers = [];
        $httpBody = $getObjectsParams;

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, true);
    }

    /**
     * Retrieves a rule by its ID. To find the object ID of rules, use the [`search` operation](#tag/Rules/operation/searchRules).
     *
     * Required API Key ACLs:
     *  - settings
     *
     * @param string $indexName      Name of the index on which to perform the operation. (required)
     * @param string $objectID       Unique identifier of a rule object. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|Rule
     */
    public function getRule($indexName, $objectID, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `getRule`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `getRule`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/rules/{objectID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Retrieves an object with non-null index settings.
     *
     * Required API Key ACLs:
     *  - settings
     *
     * @param string $indexName      Name of the index on which to perform the operation. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SettingsResponse
     */
    public function getSettings($indexName, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `getSettings`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/settings';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Retrieves all allowed IP addresses with access to your application.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|Source[]
     */
    public function getSources($requestOptions = [])
    {
        $resourcePath = '/1/security/sources';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Retrieves a synonym by its ID. To find the object IDs for your synonyms, use the [`search` operation](#tag/Synonyms/operation/searchSynonyms).
     *
     * Required API Key ACLs:
     *  - settings
     *
     * @param string $indexName      Name of the index on which to perform the operation. (required)
     * @param string $objectID       Unique identifier of a synonym object. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SynonymHit
     */
    public function getSynonym($indexName, $objectID, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `getSynonym`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `getSynonym`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/synonyms/{objectID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Checks the status of a given task.  Indexing tasks are asynchronous. When you add, update, or delete records or indices, a task is created on a queue and completed depending on the load on the server.  The indexing tasks' responses include a task ID that you can use to check the status.
     *
     * Required API Key ACLs:
     *  - addObject
     *
     * @param string $indexName      Name of the index on which to perform the operation. (required)
     * @param int    $taskID         Unique task identifier. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|GetTaskResponse
     */
    public function getTask($indexName, $taskID, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `getTask`.'
            );
        }
        // verify the required parameter 'taskID' is set
        if (!isset($taskID)) {
            throw new \InvalidArgumentException(
                'Parameter `taskID` is required when calling `getTask`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/task/{taskID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $taskID) {
            $resourcePath = str_replace(
                '{taskID}',
                ObjectSerializer::toPathValue($taskID),
                $resourcePath
            );
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Get the IDs of the 10 users with the highest number of records per cluster.  Since it can take a few seconds to get the data from the different clusters, the response isn't real-time.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|GetTopUserIdsResponse
     *
     * @deprecated
     */
    public function getTopUserIds($requestOptions = [])
    {
        $resourcePath = '/1/clusters/mapping/top';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Returns the user ID data stored in the mapping.  Since it can take a few seconds to get the data from the different clusters, the response isn't real-time.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param string $userID         Unique identifier of the user who makes the search request. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UserId
     *
     * @deprecated
     */
    public function getUserId($userID, $requestOptions = [])
    {
        // verify the required parameter 'userID' is set
        if (!isset($userID)) {
            throw new \InvalidArgumentException(
                'Parameter `userID` is required when calling `getUserId`.'
            );
        }

        $resourcePath = '/1/clusters/mapping/{userID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $userID) {
            $resourcePath = str_replace(
                '{userID}',
                ObjectSerializer::toPathValue($userID),
                $resourcePath
            );
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * To determine when the time-consuming process of creating a large batch of users or migrating users from one cluster to another is complete, this operation retrieves the status of the process.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param bool  $getClusters    Whether to include the cluster's pending mapping state in the response. (optional)
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|HasPendingMappingsResponse
     *
     * @deprecated
     */
    public function hasPendingMappings($getClusters = null, $requestOptions = [])
    {
        $resourcePath = '/1/clusters/mapping/pending';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $getClusters) {
            $queryParameters['getClusters'] = $getClusters;
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Lists all API keys associated with your Algolia application, including their permissions and restrictions.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|ListApiKeysResponse
     */
    public function listApiKeys($requestOptions = [])
    {
        $resourcePath = '/1/keys';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Lists the available clusters in a multi-cluster setup.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|ListClustersResponse
     *
     * @deprecated
     */
    public function listClusters($requestOptions = [])
    {
        $resourcePath = '/1/clusters';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Lists all indices in the current Algolia application.  The request follows any index restrictions of the API key you use to make the request.
     *
     * Required API Key ACLs:
     *  - listIndexes
     *
     * @param int   $page           Requested page of the API response. If `null`, the API response is not paginated. (optional)
     * @param int   $hitsPerPage    Number of hits per page. (optional, default to 100)
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|ListIndicesResponse
     */
    public function listIndices($page = null, $hitsPerPage = null, $requestOptions = [])
    {
        $resourcePath = '/1/indexes';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $page) {
            $queryParameters['page'] = $page;
        }

        if (null !== $hitsPerPage) {
            $queryParameters['hitsPerPage'] = $hitsPerPage;
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Lists the userIDs assigned to a multi-cluster application.  Since it can take a few seconds to get the data from the different clusters, the response isn't real-time.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param int   $page           Requested page of the API response. If `null`, the API response is not paginated. (optional)
     * @param int   $hitsPerPage    Number of hits per page. (optional, default to 100)
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|ListUserIdsResponse
     *
     * @deprecated
     */
    public function listUserIds($page = null, $hitsPerPage = null, $requestOptions = [])
    {
        $resourcePath = '/1/clusters/mapping';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        if (null !== $page) {
            $queryParameters['page'] = $page;
        }

        if (null !== $hitsPerPage) {
            $queryParameters['hitsPerPage'] = $hitsPerPage;
        }

        return $this->sendRequest('GET', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Adds, updates, or deletes records in multiple indices with a single API request.  - Actions are applied in the order they are specified. - Actions are equivalent to the individual API requests of the same name.  This operation is subject to [indexing rate limits](https://support.algolia.com/hc/en-us/articles/4406975251089-Is-there-a-rate-limit-for-indexing-on-Algolia).
     *
     * @param array|BatchParams $batchParams batchParams (required)
     *                                       - $batchParams['requests'] => (array)  (required)
     *
     * @see BatchParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|MultipleBatchResponse
     */
    public function multipleBatch($batchParams, $requestOptions = [])
    {
        // verify the required parameter 'batchParams' is set
        if (!isset($batchParams)) {
            throw new \InvalidArgumentException(
                'Parameter `batchParams` is required when calling `multipleBatch`.'
            );
        }

        $resourcePath = '/1/indexes/*/batch';
        $queryParameters = [];
        $headers = [];
        $httpBody = $batchParams;

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Copies or moves (renames) an index within the same Algolia application.  - Existing destination indices are overwritten, except for their analytics data. - If the destination index doesn't exist yet, it'll be created. - This operation is resource-intensive.  **Copy**  - Copying a source index that doesn't exist creates a new index with 0 records and default settings. - The API keys of the source index are merged with the existing keys in the destination index. - You can't copy the `enableReRanking`, `mode`, and `replicas` settings. - You can't copy to a destination index that already has replicas. - Be aware of the [size limits](https://www.algolia.com/doc/guides/scaling/algolia-service-limits/#application-record-and-index-limits). - Related guide: [Copy indices](https://www.algolia.com/doc/guides/sending-and-managing-data/manage-indices-and-apps/manage-indices/how-to/copy-indices/)  **Move**  - Moving a source index that doesn't exist is ignored without returning an error. - When moving an index, the analytics data keeps its original name, and a new set of analytics data is started for the new name.   To access the original analytics in the dashboard, create an index with the original name. - If the destination index has replicas, moving will overwrite the existing index and copy the data to the replica indices. - Related guide: [Move indices](https://www.algolia.com/doc/guides/sending-and-managing-data/manage-indices-and-apps/manage-indices/how-to/move-indices/).  This operation is subject to [indexing rate limits](https://support.algolia.com/hc/en-us/articles/4406975251089-Is-there-a-rate-limit-for-indexing-on-Algolia).
     *
     * Required API Key ACLs:
     *  - addObject
     *
     * @param string                     $indexName            Name of the index on which to perform the operation. (required)
     * @param array|OperationIndexParams $operationIndexParams operationIndexParams (required)
     *                                                         - $operationIndexParams['operation'] => (array)  (required)
     *                                                         - $operationIndexParams['destination'] => (string) Index name (case-sensitive). (required)
     *                                                         - $operationIndexParams['scope'] => (array) **Only for copying.**  If you specify a scope, only the selected scopes are copied. Records and the other scopes are left unchanged. If you omit the `scope` parameter, everything is copied: records, settings, synonyms, and rules.
     *
     * @see OperationIndexParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function operationIndex($indexName, $operationIndexParams, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `operationIndex`.'
            );
        }
        // verify the required parameter 'operationIndexParams' is set
        if (!isset($operationIndexParams)) {
            throw new \InvalidArgumentException(
                'Parameter `operationIndexParams` is required when calling `operationIndex`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/operation';
        $queryParameters = [];
        $headers = [];
        $httpBody = $operationIndexParams;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Adds new attributes to a record, or updates existing ones.  - If a record with the specified object ID doesn't exist,   a new record is added to the index **if** `createIfNotExists` is true. - If the index doesn't exist yet, this method creates a new index. - You can use any first-level attribute but not nested attributes.   If you specify a nested attribute, this operation replaces its first-level ancestor.  To update an attribute without pushing the entire record, you can use these built-in operations. These operations can be helpful if you don't have access to your initial data.  - Increment: increment a numeric attribute - Decrement: decrement a numeric attribute - Add: append a number or string element to an array attribute - Remove: remove all matching number or string elements from an array attribute made of numbers or strings - AddUnique: add a number or string element to an array attribute made of numbers or strings only if it's not already present - IncrementFrom: increment a numeric integer attribute only if the provided value matches the current value, and otherwise ignore the whole object update. For example, if you pass an IncrementFrom value of 2 for the version attribute, but the current value of the attribute is 1, the engine ignores the update. If the object doesn't exist, the engine only creates it if you pass an IncrementFrom value of 0. - IncrementSet: increment a numeric integer attribute only if the provided value is greater than the current value, and otherwise ignore the whole object update. For example, if you pass an IncrementSet value of 2 for the version attribute, and the current value of the attribute is 1, the engine updates the object. If the object doesn't exist yet, the engine only creates it if you pass an IncrementSet value greater than 0.  You can specify an operation by providing an object with the attribute to update as the key and its value being an object with the following properties:  - _operation: the operation to apply on the attribute - value: the right-hand side argument to the operation, for example, increment or decrement step, value to add or remove.  When updating multiple attributes or using multiple operations targeting the same record, you should use a single partial update for faster processing.  This operation is subject to [indexing rate limits](https://support.algolia.com/hc/en-us/articles/4406975251089-Is-there-a-rate-limit-for-indexing-on-Algolia).
     *
     * Required API Key ACLs:
     *  - addObject
     *
     * @param string $indexName          Name of the index on which to perform the operation. (required)
     * @param string $objectID           Unique record identifier. (required)
     * @param array  $attributesToUpdate Attributes with their values. (required)
     * @param bool   $createIfNotExists  Whether to create a new record if it doesn't exist. (optional, default to true)
     * @param array  $requestOptions     the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtWithObjectIdResponse
     */
    public function partialUpdateObject($indexName, $objectID, $attributesToUpdate, $createIfNotExists = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `partialUpdateObject`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `partialUpdateObject`.'
            );
        }
        // verify the required parameter 'attributesToUpdate' is set
        if (!isset($attributesToUpdate)) {
            throw new \InvalidArgumentException(
                'Parameter `attributesToUpdate` is required when calling `partialUpdateObject`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/{objectID}/partial';
        $queryParameters = [];
        $headers = [];
        $httpBody = $attributesToUpdate;

        if (null !== $createIfNotExists) {
            $queryParameters['createIfNotExists'] = $createIfNotExists;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Deletes a user ID and its associated data from the clusters.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param string $userID         Unique identifier of the user who makes the search request. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|RemoveUserIdResponse
     *
     * @deprecated
     */
    public function removeUserId($userID, $requestOptions = [])
    {
        // verify the required parameter 'userID' is set
        if (!isset($userID)) {
            throw new \InvalidArgumentException(
                'Parameter `userID` is required when calling `removeUserId`.'
            );
        }

        $resourcePath = '/1/clusters/mapping/{userID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $userID) {
            $resourcePath = str_replace(
                '{userID}',
                ObjectSerializer::toPathValue($userID),
                $resourcePath
            );
        }

        return $this->sendRequest('DELETE', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Replaces the list of allowed sources.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param array $source         Allowed sources. (required)
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|ReplaceSourceResponse
     */
    public function replaceSources($source, $requestOptions = [])
    {
        // verify the required parameter 'source' is set
        if (!isset($source)) {
            throw new \InvalidArgumentException(
                'Parameter `source` is required when calling `replaceSources`.'
            );
        }

        $resourcePath = '/1/security/sources';
        $queryParameters = [];
        $headers = [];
        $httpBody = $source;

        return $this->sendRequest('PUT', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Restores a deleted API key.  Restoring resets the `validity` attribute to `0`.  Algolia stores up to 1,000 API keys per application. If you create more, the oldest API keys are deleted and can't be restored.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param string $key            API key. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return AddApiKeyResponse|array<string, mixed>
     */
    public function restoreApiKey($key, $requestOptions = [])
    {
        // verify the required parameter 'key' is set
        if (!isset($key)) {
            throw new \InvalidArgumentException(
                'Parameter `key` is required when calling `restoreApiKey`.'
            );
        }

        $resourcePath = '/1/keys/{key}/restore';
        $queryParameters = [];
        $headers = [];
        $httpBody = null;

        // path params
        if (null !== $key) {
            $resourcePath = str_replace(
                '{key}',
                ObjectSerializer::toPathValue($key),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Adds a record to an index or replaces it.  - If the record doesn't have an object ID, a new record with an auto-generated object ID is added to your index. - If a record with the specified object ID exists, the existing record is replaced. - If a record with the specified object ID doesn't exist, a new record is added to your index. - If you add a record to an index that doesn't exist yet, a new index is created.  To update _some_ attributes of a record, use the [`partial` operation](#tag/Records/operation/partialUpdateObject). To add, update, or replace multiple records, use the [`batch` operation](#tag/Records/operation/batch).  This operation is subject to [indexing rate limits](https://support.algolia.com/hc/en-us/articles/4406975251089-Is-there-a-rate-limit-for-indexing-on-Algolia).
     *
     * Required API Key ACLs:
     *  - addObject
     *
     * @param string $indexName      Name of the index on which to perform the operation. (required)
     * @param array  $body           The record. A schemaless object with attributes that are useful in the context of search and discovery. (required)
     * @param array  $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SaveObjectResponse
     */
    public function saveObject($indexName, $body, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `saveObject`.'
            );
        }
        // verify the required parameter 'body' is set
        if (!isset($body)) {
            throw new \InvalidArgumentException(
                'Parameter `body` is required when calling `saveObject`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}';
        $queryParameters = [];
        $headers = [];
        $httpBody = $body;

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * If a rule with the specified object ID doesn't exist, it's created. Otherwise, the existing rule is replaced.  To create or update more than one rule, use the [`batch` operation](#tag/Rules/operation/saveRules).
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param string     $indexName Name of the index on which to perform the operation. (required)
     * @param string     $objectID  Unique identifier of a rule object. (required)
     * @param array|Rule $rule      rule (required)
     *                              - $rule['objectID'] => (string) Unique identifier of a rule object. (required)
     *                              - $rule['conditions'] => (array) Conditions that trigger a rule.  Some consequences require specific conditions or don't require any condition. For more information, see [Conditions](https://www.algolia.com/doc/guides/managing-results/rules/rules-overview/#conditions).
     *                              - $rule['consequence'] => (array)  (required)
     *                              - $rule['description'] => (string) Description of the rule's purpose to help you distinguish between different rules.
     *                              - $rule['enabled'] => (bool) Whether the rule is active.
     *                              - $rule['validity'] => (array) Time periods when the rule is active.
     *
     * @see Rule
     *
     * @param bool  $forwardToReplicas Whether changes are applied to replica indices. (optional)
     * @param array $requestOptions    the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function saveRule($indexName, $objectID, $rule, $forwardToReplicas = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `saveRule`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `saveRule`.'
            );
        }
        // verify the required parameter 'rule' is set
        if (!isset($rule)) {
            throw new \InvalidArgumentException(
                'Parameter `rule` is required when calling `saveRule`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/rules/{objectID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = $rule;

        if (null !== $forwardToReplicas) {
            $queryParameters['forwardToReplicas'] = $forwardToReplicas;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('PUT', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Create or update multiple rules.  If a rule with the specified object ID doesn't exist, Algolia creates a new one. Otherwise, existing rules are replaced.  This operation is subject to [indexing rate limits](https://support.algolia.com/hc/en-us/articles/4406975251089-Is-there-a-rate-limit-for-indexing-on-Algolia).
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param string $indexName          Name of the index on which to perform the operation. (required)
     * @param array  $rules              rules (required)
     * @param bool   $forwardToReplicas  Whether changes are applied to replica indices. (optional)
     * @param bool   $clearExistingRules Whether existing rules should be deleted before adding this batch. (optional)
     * @param array  $requestOptions     the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function saveRules($indexName, $rules, $forwardToReplicas = null, $clearExistingRules = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `saveRules`.'
            );
        }
        // verify the required parameter 'rules' is set
        if (!isset($rules)) {
            throw new \InvalidArgumentException(
                'Parameter `rules` is required when calling `saveRules`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/rules/batch';
        $queryParameters = [];
        $headers = [];
        $httpBody = $rules;

        if (null !== $forwardToReplicas) {
            $queryParameters['forwardToReplicas'] = $forwardToReplicas;
        }

        if (null !== $clearExistingRules) {
            $queryParameters['clearExistingRules'] = $clearExistingRules;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * If a synonym with the specified object ID doesn't exist, Algolia adds a new one. Otherwise, the existing synonym is replaced. To add multiple synonyms in a single API request, use the [`batch` operation](#tag/Synonyms/operation/saveSynonyms).
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param string           $indexName  Name of the index on which to perform the operation. (required)
     * @param string           $objectID   Unique identifier of a synonym object. (required)
     * @param array|SynonymHit $synonymHit synonymHit (required)
     *                                     - $synonymHit['objectID'] => (string) Unique identifier of a synonym object. (required)
     *                                     - $synonymHit['type'] => (array)  (required)
     *                                     - $synonymHit['synonyms'] => (array) Words or phrases considered equivalent.
     *                                     - $synonymHit['input'] => (string) Word or phrase to appear in query strings (for [`onewaysynonym`s](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/adding-synonyms/in-depth/one-way-synonyms/)).
     *                                     - $synonymHit['word'] => (string) Word or phrase to appear in query strings (for [`altcorrection1` and `altcorrection2`](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/adding-synonyms/in-depth/synonyms-alternative-corrections/)).
     *                                     - $synonymHit['corrections'] => (array) Words to be matched in records.
     *                                     - $synonymHit['placeholder'] => (string) [Placeholder token](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/adding-synonyms/in-depth/synonyms-placeholders/) to be put inside records.
     *                                     - $synonymHit['replacements'] => (array) Query words that will match the [placeholder token](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/adding-synonyms/in-depth/synonyms-placeholders/).
     *
     * @see SynonymHit
     *
     * @param bool  $forwardToReplicas Whether changes are applied to replica indices. (optional)
     * @param array $requestOptions    the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SaveSynonymResponse
     */
    public function saveSynonym($indexName, $objectID, $synonymHit, $forwardToReplicas = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `saveSynonym`.'
            );
        }
        // verify the required parameter 'objectID' is set
        if (!isset($objectID)) {
            throw new \InvalidArgumentException(
                'Parameter `objectID` is required when calling `saveSynonym`.'
            );
        }
        // verify the required parameter 'synonymHit' is set
        if (!isset($synonymHit)) {
            throw new \InvalidArgumentException(
                'Parameter `synonymHit` is required when calling `saveSynonym`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/synonyms/{objectID}';
        $queryParameters = [];
        $headers = [];
        $httpBody = $synonymHit;

        if (null !== $forwardToReplicas) {
            $queryParameters['forwardToReplicas'] = $forwardToReplicas;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $objectID) {
            $resourcePath = str_replace(
                '{objectID}',
                ObjectSerializer::toPathValue($objectID),
                $resourcePath
            );
        }

        return $this->sendRequest('PUT', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * If a synonym with the `objectID` doesn't exist, Algolia adds a new one. Otherwise, existing synonyms are replaced.  This operation is subject to [indexing rate limits](https://support.algolia.com/hc/en-us/articles/4406975251089-Is-there-a-rate-limit-for-indexing-on-Algolia).
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param string $indexName               Name of the index on which to perform the operation. (required)
     * @param array  $synonymHit              synonymHit (required)
     * @param bool   $forwardToReplicas       Whether changes are applied to replica indices. (optional)
     * @param bool   $replaceExistingSynonyms Whether to replace all synonyms in the index with the ones sent with this request. (optional)
     * @param array  $requestOptions          the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function saveSynonyms($indexName, $synonymHit, $forwardToReplicas = null, $replaceExistingSynonyms = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `saveSynonyms`.'
            );
        }
        // verify the required parameter 'synonymHit' is set
        if (!isset($synonymHit)) {
            throw new \InvalidArgumentException(
                'Parameter `synonymHit` is required when calling `saveSynonyms`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/synonyms/batch';
        $queryParameters = [];
        $headers = [];
        $httpBody = $synonymHit;

        if (null !== $forwardToReplicas) {
            $queryParameters['forwardToReplicas'] = $forwardToReplicas;
        }

        if (null !== $replaceExistingSynonyms) {
            $queryParameters['replaceExistingSynonyms'] = $replaceExistingSynonyms;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Sends multiple search requests to one or more indices.  This can be useful in these cases:  - Different indices for different purposes, such as, one index for products, another one for marketing content. - Multiple searches to the same index—for example, with different filters.  Use the helper `searchForHits` or `searchForFacets` to get the results in a more convenient format, if you already know the return type you want.
     *
     * Required API Key ACLs:
     *  - search
     *
     * @param array|SearchMethodParams $searchMethodParams Muli-search request body. Results are returned in the same order as the requests. (required)
     *                                                     - $searchMethodParams['requests'] => (array)  (required)
     *                                                     - $searchMethodParams['strategy'] => (array)
     *
     * @see SearchMethodParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SearchResponses
     */
    public function search($searchMethodParams, $requestOptions = [])
    {
        // verify the required parameter 'searchMethodParams' is set
        if (!isset($searchMethodParams)) {
            throw new \InvalidArgumentException(
                'Parameter `searchMethodParams` is required when calling `search`.'
            );
        }

        $resourcePath = '/1/indexes/*/queries';
        $queryParameters = [];
        $headers = [];
        $httpBody = $searchMethodParams;

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, true);
    }

    /**
     * Searches for standard and custom dictionary entries.
     *
     * Required API Key ACLs:
     *  - settings
     *
     * @param array                               $dictionaryName                Dictionary type in which to search. (required)
     * @param array|SearchDictionaryEntriesParams $searchDictionaryEntriesParams searchDictionaryEntriesParams (required)
     *                                                                           - $searchDictionaryEntriesParams['query'] => (string) Search query. (required)
     *                                                                           - $searchDictionaryEntriesParams['page'] => (int) Page of search results to retrieve.
     *                                                                           - $searchDictionaryEntriesParams['hitsPerPage'] => (int) Number of hits per page.
     *                                                                           - $searchDictionaryEntriesParams['language'] => (array)
     *
     * @see SearchDictionaryEntriesParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SearchDictionaryEntriesResponse
     */
    public function searchDictionaryEntries($dictionaryName, $searchDictionaryEntriesParams, $requestOptions = [])
    {
        // verify the required parameter 'dictionaryName' is set
        if (!isset($dictionaryName)) {
            throw new \InvalidArgumentException(
                'Parameter `dictionaryName` is required when calling `searchDictionaryEntries`.'
            );
        }
        // verify the required parameter 'searchDictionaryEntriesParams' is set
        if (!isset($searchDictionaryEntriesParams)) {
            throw new \InvalidArgumentException(
                'Parameter `searchDictionaryEntriesParams` is required when calling `searchDictionaryEntries`.'
            );
        }

        $resourcePath = '/1/dictionaries/{dictionaryName}/search';
        $queryParameters = [];
        $headers = [];
        $httpBody = $searchDictionaryEntriesParams;

        // path params
        if (null !== $dictionaryName) {
            $resourcePath = str_replace(
                '{dictionaryName}',
                ObjectSerializer::toPathValue($dictionaryName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, true);
    }

    /**
     * Searches for values of a specified facet attribute.  - By default, facet values are sorted by decreasing count.   You can adjust this with the `sortFacetValueBy` parameter. - Searching for facet values doesn't work if you have **more than 65 searchable facets and searchable attributes combined**.
     *
     * Required API Key ACLs:
     *  - search
     *
     * @param string                            $indexName                   Name of the index on which to perform the operation. (required)
     * @param string                            $facetName                   Facet attribute in which to search for values.  This attribute must be included in the `attributesForFaceting` index setting with the `searchable()` modifier. (required)
     * @param array|SearchForFacetValuesRequest $searchForFacetValuesRequest searchForFacetValuesRequest (optional)
     *                                                                       - $searchForFacetValuesRequest['params'] => (string) Search parameters as a URL-encoded query string.
     *                                                                       - $searchForFacetValuesRequest['facetQuery'] => (string) Text to search inside the facet's values.
     *                                                                       - $searchForFacetValuesRequest['maxFacetHits'] => (int) Maximum number of facet values to return when [searching for facet values](https://www.algolia.com/doc/guides/managing-results/refine-results/faceting/#search-for-facet-values).
     *
     * @see SearchForFacetValuesRequest
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SearchForFacetValuesResponse
     */
    public function searchForFacetValues($indexName, $facetName, $searchForFacetValuesRequest = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `searchForFacetValues`.'
            );
        }
        // verify the required parameter 'facetName' is set
        if (!isset($facetName)) {
            throw new \InvalidArgumentException(
                'Parameter `facetName` is required when calling `searchForFacetValues`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/facets/{facetName}/query';
        $queryParameters = [];
        $headers = [];
        $httpBody = isset($searchForFacetValuesRequest) ? $searchForFacetValuesRequest : [];

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        // path params
        if (null !== $facetName) {
            $resourcePath = str_replace(
                '{facetName}',
                ObjectSerializer::toPathValue($facetName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, true);
    }

    /**
     * Searches for rules in your index.
     *
     * Required API Key ACLs:
     *  - settings
     *
     * @param string                  $indexName         Name of the index on which to perform the operation. (required)
     * @param array|SearchRulesParams $searchRulesParams searchRulesParams (optional)
     *                                                   - $searchRulesParams['query'] => (string) Search query for rules.
     *                                                   - $searchRulesParams['anchoring'] => (array)
     *                                                   - $searchRulesParams['context'] => (string) Only return rules that match the context (exact match).
     *                                                   - $searchRulesParams['page'] => (int) Requested page of the API response.  Algolia uses `page` and `hitsPerPage` to control how search results are displayed ([paginated](https://www.algolia.com/doc/guides/building-search-ui/ui-and-ux-patterns/pagination/js/)).  - `hitsPerPage`: sets the number of search results (_hits_) displayed per page. - `page`: specifies the page number of the search results you want to retrieve. Page numbering starts at 0, so the first page is `page=0`, the second is `page=1`, and so on.  For example, to display 10 results per page starting from the third page, set `hitsPerPage` to 10 and `page` to 2.
     *                                                   - $searchRulesParams['hitsPerPage'] => (int) Maximum number of hits per page.  Algolia uses `page` and `hitsPerPage` to control how search results are displayed ([paginated](https://www.algolia.com/doc/guides/building-search-ui/ui-and-ux-patterns/pagination/js/)).  - `hitsPerPage`: sets the number of search results (_hits_) displayed per page. - `page`: specifies the page number of the search results you want to retrieve. Page numbering starts at 0, so the first page is `page=0`, the second is `page=1`, and so on.  For example, to display 10 results per page starting from the third page, set `hitsPerPage` to 10 and `page` to 2.
     *                                                   - $searchRulesParams['enabled'] => (bool) If `true`, return only enabled rules. If `false`, return only inactive rules. By default, _all_ rules are returned.
     *
     * @see SearchRulesParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SearchRulesResponse
     */
    public function searchRules($indexName, $searchRulesParams = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `searchRules`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/rules/search';
        $queryParameters = [];
        $headers = [];
        $httpBody = isset($searchRulesParams) ? $searchRulesParams : [];

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, true);
    }

    /**
     * Searches a single index and returns matching search results (_hits_).  This method lets you retrieve up to 1,000 hits. If you need more, use the [`browse` operation](#tag/Search/operation/browse) or increase the `paginatedLimitedTo` index setting.
     *
     * Required API Key ACLs:
     *  - search
     *
     * @param string             $indexName    Name of the index on which to perform the operation. (required)
     * @param array|SearchParams $searchParams searchParams (optional)
     *
     * @see SearchParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SearchResponse
     */
    public function searchSingleIndex($indexName, $searchParams = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `searchSingleIndex`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/query';
        $queryParameters = [];
        $headers = [];
        $httpBody = isset($searchParams) ? $searchParams : [];

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, true);
    }

    /**
     * Searches for synonyms in your index.
     *
     * Required API Key ACLs:
     *  - settings
     *
     * @param string                     $indexName            Name of the index on which to perform the operation. (required)
     * @param array|SearchSynonymsParams $searchSynonymsParams Body of the `searchSynonyms` operation. (optional)
     *                                                         - $searchSynonymsParams['query'] => (string) Search query.
     *                                                         - $searchSynonymsParams['type'] => (array)
     *                                                         - $searchSynonymsParams['page'] => (int) Page of search results to retrieve.
     *                                                         - $searchSynonymsParams['hitsPerPage'] => (int) Number of hits per page.
     *
     * @see SearchSynonymsParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SearchSynonymsResponse
     */
    public function searchSynonyms($indexName, $searchSynonymsParams = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `searchSynonyms`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/synonyms/search';
        $queryParameters = [];
        $headers = [];
        $httpBody = isset($searchSynonymsParams) ? $searchSynonymsParams : [];

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, true);
    }

    /**
     * Since it can take a few seconds to get the data from the different clusters, the response isn't real-time.  To ensure rapid updates, the user IDs index isn't built at the same time as the mapping. Instead, it's built every 12 hours, at the same time as the update of user ID usage. For example, if you add or move a user ID, the search will show an old value until the next time the mapping is rebuilt (every 12 hours).
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param array|SearchUserIdsParams $searchUserIdsParams searchUserIdsParams (required)
     *                                                       - $searchUserIdsParams['query'] => (string) Query to search. The search is a prefix search with [typo tolerance](https://www.algolia.com/doc/guides/managing-results/optimize-search-results/typo-tolerance/) enabled. An empty query will retrieve all users. (required)
     *                                                       - $searchUserIdsParams['clusterName'] => (string) Cluster name.
     *                                                       - $searchUserIdsParams['page'] => (int) Page of search results to retrieve.
     *                                                       - $searchUserIdsParams['hitsPerPage'] => (int) Number of hits per page.
     *
     * @see SearchUserIdsParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|SearchUserIdsResponse
     *
     * @deprecated
     */
    public function searchUserIds($searchUserIdsParams, $requestOptions = [])
    {
        // verify the required parameter 'searchUserIdsParams' is set
        if (!isset($searchUserIdsParams)) {
            throw new \InvalidArgumentException(
                'Parameter `searchUserIdsParams` is required when calling `searchUserIds`.'
            );
        }

        $resourcePath = '/1/clusters/mapping/search';
        $queryParameters = [];
        $headers = [];
        $httpBody = $searchUserIdsParams;

        return $this->sendRequest('POST', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, true);
    }

    /**
     * Turns standard stop word dictionary entries on or off for a given language.
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param array|DictionarySettingsParams $dictionarySettingsParams dictionarySettingsParams (required)
     *                                                                 - $dictionarySettingsParams['disableStandardEntries'] => (array)  (required)
     *
     * @see DictionarySettingsParams
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function setDictionarySettings($dictionarySettingsParams, $requestOptions = [])
    {
        // verify the required parameter 'dictionarySettingsParams' is set
        if (!isset($dictionarySettingsParams)) {
            throw new \InvalidArgumentException(
                'Parameter `dictionarySettingsParams` is required when calling `setDictionarySettings`.'
            );
        }

        $resourcePath = '/1/dictionaries/*/settings';
        $queryParameters = [];
        $headers = [];
        $httpBody = $dictionarySettingsParams;

        return $this->sendRequest('PUT', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Update the specified index settings.  Index settings that you don't specify are left unchanged. Specify `null` to reset a setting to its default value.  For best performance, update the index settings before you add new records to your index.
     *
     * Required API Key ACLs:
     *  - editSettings
     *
     * @param string              $indexName     Name of the index on which to perform the operation. (required)
     * @param array|IndexSettings $indexSettings indexSettings (required)
     *
     * @see IndexSettings
     *
     * @param bool  $forwardToReplicas Whether changes are applied to replica indices. (optional)
     * @param array $requestOptions    the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdatedAtResponse
     */
    public function setSettings($indexName, $indexSettings, $forwardToReplicas = null, $requestOptions = [])
    {
        // verify the required parameter 'indexName' is set
        if (!isset($indexName)) {
            throw new \InvalidArgumentException(
                'Parameter `indexName` is required when calling `setSettings`.'
            );
        }
        // verify the required parameter 'indexSettings' is set
        if (!isset($indexSettings)) {
            throw new \InvalidArgumentException(
                'Parameter `indexSettings` is required when calling `setSettings`.'
            );
        }

        $resourcePath = '/1/indexes/{indexName}/settings';
        $queryParameters = [];
        $headers = [];
        $httpBody = $indexSettings;

        if (null !== $forwardToReplicas) {
            $queryParameters['forwardToReplicas'] = $forwardToReplicas;
        }

        // path params
        if (null !== $indexName) {
            $resourcePath = str_replace(
                '{indexName}',
                ObjectSerializer::toPathValue($indexName),
                $resourcePath
            );
        }

        return $this->sendRequest('PUT', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Replaces the permissions of an existing API key.  Any unspecified attribute resets that attribute to its default value.
     *
     * Required API Key ACLs:
     *  - admin
     *
     * @param string       $key    API key. (required)
     * @param ApiKey|array $apiKey apiKey (required)
     *                             - $apiKey['acl'] => (array) Permissions that determine the type of API requests this key can make. The required ACL is listed in each endpoint's reference. For more information, see [access control list](https://www.algolia.com/doc/guides/security/api-keys/#access-control-list-acl). (required)
     *                             - $apiKey['description'] => (string) Description of an API key to help you identify this API key.
     *                             - $apiKey['indexes'] => (array) Index names or patterns that this API key can access. By default, an API key can access all indices in the same application.  You can use leading and trailing wildcard characters (`*`):  - `dev_*` matches all indices starting with \"dev_\". - `*_dev` matches all indices ending with \"_dev\". - `*_products_*` matches all indices containing \"_products_\".
     *                             - $apiKey['maxHitsPerQuery'] => (int) Maximum number of results this API key can retrieve in one query. By default, there's no limit.
     *                             - $apiKey['maxQueriesPerIPPerHour'] => (int) Maximum number of API requests allowed per IP address or [user token](https://www.algolia.com/doc/guides/sending-events/concepts/usertoken/) per hour.  If this limit is reached, the API returns an error with status code `429`. By default, there's no limit.
     *                             - $apiKey['queryParameters'] => (string) Query parameters to add when making API requests with this API key.  To restrict this API key to specific IP addresses, add the `restrictSources` parameter. You can only add a single source, but you can provide a range of IP addresses.  Creating an API key fails if the request is made from an IP address outside the restricted range.
     *                             - $apiKey['referers'] => (array) Allowed HTTP referrers for this API key.  By default, all referrers are allowed. You can use leading and trailing wildcard characters (`*`):  - `https://algolia.com/_*` allows all referrers starting with \"https://algolia.com/\" - `*.algolia.com` allows all referrers ending with \".algolia.com\" - `*algolia.com*` allows all referrers in the domain \"algolia.com\".  Like all HTTP headers, referrers can be spoofed. Don't rely on them to secure your data. For more information, see [HTTP referrer restrictions](https://www.algolia.com/doc/guides/security/security-best-practices/#http-referrers-restrictions).
     *                             - $apiKey['validity'] => (int) Duration (in seconds) after which the API key expires. By default, API keys don't expire.
     *
     * @see ApiKey
     *
     * @param array $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @return array<string, mixed>|UpdateApiKeyResponse
     */
    public function updateApiKey($key, $apiKey, $requestOptions = [])
    {
        // verify the required parameter 'key' is set
        if (!isset($key)) {
            throw new \InvalidArgumentException(
                'Parameter `key` is required when calling `updateApiKey`.'
            );
        }
        // verify the required parameter 'apiKey' is set
        if (!isset($apiKey)) {
            throw new \InvalidArgumentException(
                'Parameter `apiKey` is required when calling `updateApiKey`.'
            );
        }

        $resourcePath = '/1/keys/{key}';
        $queryParameters = [];
        $headers = [];
        $httpBody = $apiKey;

        // path params
        if (null !== $key) {
            $resourcePath = str_replace(
                '{key}',
                ObjectSerializer::toPathValue($key),
                $resourcePath
            );
        }

        return $this->sendRequest('PUT', $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions);
    }

    /**
     * Wait for a task to complete with `indexName` and `taskID`.
     *
     * @param string   $indexName      Index name
     * @param int      $taskId         Task Id
     * @param array    $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     * @param null|int $maxRetries     Maximum number of retries
     * @param null|int $timeout        Timeout
     *
     * @return GetTaskResponse
     *
     * @throws ExceededRetriesException
     */
    public function waitForTask($indexName, $taskId, $requestOptions = [], $maxRetries = null, $timeout = null)
    {
        if (null === $timeout) {
            $timeout = $this->config->getWaitTaskTimeBeforeRetry();
        }

        if (null === $maxRetries) {
            $maxRetries = $this->config->getDefaultMaxRetries();
        }

        return Helpers::retryUntil(
            $this,
            'getTask',
            [$indexName, $taskId, $requestOptions],
            function ($res) {return 'published' === $res['status']; },
            $maxRetries,
            $timeout
        );
    }

    /**
     * Wait for an application-level task to complete with `taskID`.
     *
     * @param int      $taskId         Task Id
     * @param array    $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     * @param null|int $maxRetries     Maximum number of retries
     * @param null|int $timeout        Timeout
     *
     * @return GetTaskResponse
     *
     * @throws ExceededRetriesException
     */
    public function waitForAppTask($taskId, $requestOptions = [], $maxRetries = null, $timeout = null)
    {
        if (null === $timeout) {
            $timeout = $this->config->getWaitTaskTimeBeforeRetry();
        }

        if (null === $maxRetries) {
            $maxRetries = $this->config->getDefaultMaxRetries();
        }

        return Helpers::retryUntil(
            $this,
            'getAppTask',
            [$taskId, $requestOptions],
            function ($res) {return 'published' === $res['status']; },
            $maxRetries,
            $timeout
        );
    }

    /**
     * Wait for an API key to be added, updated or deleted based on a given `operation`.
     *
     * @param string   $key            the `key` that has been added, deleted or updated
     * @param string   $operation      the `operation` that was done on a `key`
     * @param array    $apiKey         necessary to know if an `update` operation has been processed, compare fields of the response with it
     * @param null|int $maxRetries     Maximum number of retries
     * @param null|int $timeout        Timeout
     * @param array    $requestOptions the requestOptions to send along with the query, they will be merged with the transporter requestOptions
     *
     * @throws ExceededRetriesException
     */
    public function waitForApiKey(
        $key,
        $operation,
        $apiKey = null,
        $maxRetries = null,
        $timeout = null,
        $requestOptions = []
    ) {
        if (null === $timeout) {
            $timeout = $this->config->getWaitTaskTimeBeforeRetry();
        }

        if (null === $maxRetries) {
            $maxRetries = $this->config->getDefaultMaxRetries();
        }

        return Helpers::retryForApiKeyUntil(
            $operation,
            $this,
            $key,
            $apiKey,
            $maxRetries,
            $timeout,
            'Algolia\AlgoliaSearch\Support\Helpers::linearTimeout',
            $requestOptions
        );
    }

    /**
     * Helper: Iterate on the `browse` method of the client to allow aggregating objects of an index.
     *
     * @param string $indexName      Index name
     * @param array  $requestOptions Request options
     *
     * @return ObjectIterator
     */
    public function browseObjects($indexName, $requestOptions = [])
    {
        return new ObjectIterator($indexName, $this, $requestOptions);
    }

    /**
     * Helper: Iterate on the `searchRules` method of the client to allow aggregating rules of an index.
     *
     * @param string $indexName      Index name
     * @param array  $requestOptions Request options
     *
     * @return RuleIterator
     */
    public function browseRules($indexName, $requestOptions = [])
    {
        return new RuleIterator($indexName, $this, $requestOptions);
    }

    /**
     * Helper: Iterate on the `searchSynonyms` method of the client to allow aggregating synonyms of an index.
     *
     * @param string $indexName      Index name
     * @param array  $requestOptions Request options
     *
     * @return SynonymIterator
     */
    public function browseSynonyms($indexName, $requestOptions = [])
    {
        return new SynonymIterator($indexName, $this, $requestOptions);
    }

    /**
     * Helper: Replace all objects in an index using a temporary one.
     * See https://api-clients-automation.netlify.app/docs/add-new-api-client#5-helpers for implementation details.
     *
     * @param string $indexName      the `indexName` to replace `objects` in
     * @param array  $objects        the array of `objects` to store in the given Algolia `indexName`
     * @param array  $batchSize      The size of the chunk of `objects`. The number of `batch` calls will be equal to `length(objects) / batchSize`. Defaults to 1000.
     * @param array  $requestOptions Request options
     * @param mixed  $scopes
     */
    public function replaceAllObjects($indexName, $objects, $batchSize = 1000, $scopes = ['settings', 'rules', 'synonyms'], $requestOptions = [])
    {
        $tmpIndexName = $indexName.'_tmp_'.rand(10000000, 99999999);

        try {
            $copyOperationResponse = $this->operationIndex(
                $indexName,
                [
                    'operation' => 'copy',
                    'destination' => $tmpIndexName,
                    'scope' => $scopes,
                ],
                $requestOptions
            );

            $batchResponses = $this->chunkedBatch($tmpIndexName, $objects, 'addObject', true, $batchSize, $requestOptions);

            $this->waitForTask($tmpIndexName, $copyOperationResponse['taskID']);

            $copyOperationResponse = $this->operationIndex(
                $indexName,
                [
                    'operation' => 'copy',
                    'destination' => $tmpIndexName,
                    'scope' => $scopes,
                ],
                $requestOptions
            );

            $this->waitForTask($tmpIndexName, $copyOperationResponse['taskID']);

            $moveOperationResponse = $this->operationIndex(
                $tmpIndexName,
                [
                    'operation' => 'move',
                    'destination' => $indexName,
                ],
                $requestOptions
            );

            $this->waitForTask($tmpIndexName, $moveOperationResponse['taskID']);

            return [
                'copyOperationResponse' => $copyOperationResponse,
                'batchResponses' => $batchResponses,
                'moveOperationResponse' => $moveOperationResponse,
            ];
        } catch (\Throwable $e) {
            $this->deleteIndex($tmpIndexName);

            throw $e;
        }
    }

    /**
     * Helper: Saves the given array of objects in the given index. The `chunkedBatch` helper is used under the hood, which creates a `batch` requests with at most 1000 objects in it.
     *
     * @param string $indexName      the `indexName` to replace `objects` in
     * @param array  $objects        the array of `objects` to store in the given Algolia `indexName`
     * @param bool   $waitForTasks   Whether or not we should wait until every `batch` tasks has been processed, this operation may slow the total execution time of this method but is more reliable
     * @param array  $batchSize      The size of the chunk of `objects`. The number of `batch` calls will be equal to `length(objects) / batchSize`. Defaults to 1000.
     * @param array  $requestOptions Request options
     */
    public function saveObjects($indexName, $objects, $waitForTasks = false, $batchSize = 1000, $requestOptions = [])
    {
        return $this->chunkedBatch($indexName, $objects, 'addObject', $waitForTasks, $batchSize, $requestOptions);
    }

    /**
     * Helper: Deletes every records for the given objectIDs. The `chunkedBatch` helper is used under the hood, which creates a `batch` requests with at most 1000 objectIDs in it.
     *
     * @param string $indexName      the `indexName` to delete `objectIDs` from
     * @param array  $objectIDs      the `objectIDs` to delete
     * @param bool   $waitForTasks   Whether or not we should wait until every `batch` tasks has been processed, this operation may slow the total execution time of this method but is more reliable
     * @param array  $batchSize      The size of the chunk of `objects`. The number of `batch` calls will be equal to `length(objects) / batchSize`. Defaults to 1000.
     * @param array  $requestOptions Request options
     */
    public function deleteObjects($indexName, $objectIDs, $waitForTasks = false, $batchSize = 1000, $requestOptions = [])
    {
        $objects = [];

        foreach ($objectIDs as $id) {
            $objects[] = ['objectID' => $id];
        }

        return $this->chunkedBatch($indexName, $objects, 'deleteObject', $waitForTasks, $batchSize, $requestOptions);
    }

    /**
     * Helper: Replaces object content of all the given objects according to their respective `objectID` field. The `chunkedBatch` helper is used under the hood, which creates a `batch` requests with at most 1000 objects in it.
     *
     * @param string $indexName         the `indexName` to replace `objects` in
     * @param array  $objects           the array of `objects` to store in the given Algolia `indexName`
     * @param bool   $createIfNotExists To be provided if non-existing objects are passed, otherwise, the call will fail..
     * @param bool   $waitForTasks      Whether or not we should wait until every `batch` tasks has been processed, this operation may slow the total execution time of this method but is more reliable
     * @param array  $batchSize         The size of the chunk of `objects`. The number of `batch` calls will be equal to `length(objects) / batchSize`. Defaults to 1000.
     * @param array  $requestOptions    Request options
     */
    public function partialUpdateObjects($indexName, $objects, $createIfNotExists, $waitForTasks = false, $batchSize = 1000, $requestOptions = [])
    {
        return $this->chunkedBatch($indexName, $objects, (true == $createIfNotExists) ? 'partialUpdateObject' : 'partialUpdateObjectNoCreate', $waitForTasks, $batchSize, $requestOptions);
    }

    /**
     * Helper: Chunks the given `objects` list in subset of 1000 elements max in order to make it fit in `batch` requests.
     *
     * @param string $indexName      the `indexName` to replace `objects` in
     * @param array  $objects        the array of `objects` to store in the given Algolia `indexName`
     * @param array  $action         the `batch` `action` to perform on the given array of `objects`, defaults to `addObject`
     * @param bool   $waitForTasks   whether or not we should wait until every `batch` tasks has been processed, this operation may slow the total execution time of this method but is more reliable
     * @param array  $batchSize      The size of the chunk of `objects`. The number of `batch` calls will be equal to `length(objects) / batchSize`. Defaults to 1000.
     * @param array  $requestOptions Request options
     */
    public function chunkedBatch(
        $indexName,
        $objects,
        $action = 'addObject',
        $waitForTasks = true,
        $batchSize = 1000,
        $requestOptions = []
    ) {
        $responses = [];
        $requests = [];
        $count = 0;

        foreach ($objects as $object) {
            $requests[] = [
                'action' => $action,
                'body' => $object,
            ];

            if (sizeof($requests) === $batchSize || $count === sizeof($objects) - 1) {
                $responses[] = $this->batch($indexName, ['requests' => $requests], $requestOptions);
                $requests = [];
            }

            ++$count;
        }

        if (!empty($requests)) {
            $responses[] = $this->batch($indexName, ['requests' => $requests], $requestOptions);
        }

        if ($waitForTasks && !empty($responses)) {
            foreach ($responses as $response) {
                $this->waitForTask($indexName, $response['taskID']);
            }
        }

        return $responses;
    }

    /**
     * Helper: Generate a secured API Key.
     *
     * @param string $parentApiKey Parent API Key
     * @param array  $restrictions API Key's restrictions
     *
     * @return string
     */
    public static function generateSecuredApiKey($parentApiKey, $restrictions)
    {
        $formattedRestrictions = $restrictions;
        if (isset($restrictions['searchParams'])) {
            $formattedRestrictions = array_merge($restrictions, $restrictions['searchParams']);
            unset($formattedRestrictions['searchParams']);
        }

        ksort($formattedRestrictions);
        $urlEncodedRestrictions = Helpers::buildQuery($formattedRestrictions);

        $content = hash_hmac('sha256', $urlEncodedRestrictions, $parentApiKey).$urlEncodedRestrictions;

        return base64_encode($content);
    }

    /**
     * Helper: Returns the time the given securedApiKey remains valid in seconds.
     *
     * @param string $securedApiKey the key to check
     *
     * @return int remaining validity in seconds
     *
     * @throws ValidUntilNotFoundException
     */
    public static function getSecuredApiKeyRemainingValidity($securedApiKey)
    {
        $decodedKey = base64_decode($securedApiKey);
        $regex = '/validUntil=(\d+)/';
        preg_match($regex, $decodedKey, $matches);

        if (0 === count($matches)) {
            throw new ValidUntilNotFoundException('validUntil not found in given secured api key.');
        }

        $validUntil = (int) $matches[1];

        return $validUntil - time();
    }

    public function indexExists($indexName)
    {
        try {
            $this->getSettings($indexName);
        } catch (NotFoundException $e) {
            return false;
        } catch (\Throwable $e) {
            throw $e;
        }

        return true;
    }

    private function sendRequest($method, $resourcePath, $headers, $queryParameters, $httpBody, $requestOptions, $useReadTransporter = false)
    {
        if (!isset($requestOptions['headers'])) {
            $requestOptions['headers'] = [];
        }
        if (!isset($requestOptions['queryParameters'])) {
            $requestOptions['queryParameters'] = [];
        }

        $requestOptions['headers'] = array_merge($headers, $requestOptions['headers']);
        $requestOptions['queryParameters'] = array_merge($queryParameters, $requestOptions['queryParameters']);
        $query = Query::build($requestOptions['queryParameters']);

        return $this->api->sendRequest(
            $method,
            $resourcePath.($query ? "?{$query}" : ''),
            $httpBody,
            $requestOptions,
            $useReadTransporter
        );
    }
}
