<?php

declare (strict_types=1);
namespace CelerSearch\Vendor\Meilisearch\Endpoints;

use CelerSearch\Vendor\Meilisearch\Contracts\Endpoint;
use CelerSearch\Vendor\Meilisearch\Contracts\FacetSearchQuery;
use CelerSearch\Vendor\Meilisearch\Contracts\Http;
use CelerSearch\Vendor\Meilisearch\Contracts\Index\Settings;
use CelerSearch\Vendor\Meilisearch\Contracts\IndexesQuery;
use CelerSearch\Vendor\Meilisearch\Contracts\IndexesResults;
use CelerSearch\Vendor\Meilisearch\Contracts\SimilarDocumentsQuery;
use CelerSearch\Vendor\Meilisearch\Contracts\Task;
use CelerSearch\Vendor\Meilisearch\Contracts\TasksQuery;
use CelerSearch\Vendor\Meilisearch\Contracts\TasksResults;
use CelerSearch\Vendor\Meilisearch\Endpoints\Delegates\HandlesDocuments;
use CelerSearch\Vendor\Meilisearch\Endpoints\Delegates\HandlesSettings;
use CelerSearch\Vendor\Meilisearch\Endpoints\Delegates\HandlesTasks;
use CelerSearch\Vendor\Meilisearch\Exceptions\ApiException;
use CelerSearch\Vendor\Meilisearch\Search\FacetSearchResult;
use CelerSearch\Vendor\Meilisearch\Search\SearchResult;
use CelerSearch\Vendor\Meilisearch\Search\SimilarDocumentsSearchResult;
use function CelerSearch\Vendor\Meilisearch\partial;
/** @internal */
class Indexes extends Endpoint
{
    use HandlesDocuments;
    use HandlesSettings;
    use HandlesTasks;
    protected const PATH = '/indexes';
    private ?string $uid;
    private ?string $primaryKey;
    private ?\DateTimeInterface $createdAt;
    private ?\DateTimeInterface $updatedAt;
    public function __construct(Http $http, ?string $uid = null, ?string $primaryKey = null, ?\DateTimeInterface $createdAt = null, ?\DateTimeInterface $updatedAt = null)
    {
        $this->uid = $uid;
        $this->primaryKey = $primaryKey;
        $this->createdAt = $createdAt;
        $this->updatedAt = $updatedAt;
        $this->tasks = new Tasks($http);
        parent::__construct($http);
    }
    protected function newInstance(array $attributes) : self
    {
        return new self($this->http, $attributes['uid'], $attributes['primaryKey'], null !== $attributes['createdAt'] ? new \DateTimeImmutable($attributes['createdAt']) : null, null !== $attributes['updatedAt'] ? new \DateTimeImmutable($attributes['updatedAt']) : null);
    }
    /**
     * @return $this
     */
    protected function fill(array $attributes) : self
    {
        $this->uid = $attributes['uid'];
        $this->primaryKey = $attributes['primaryKey'];
        $this->createdAt = null !== $attributes['createdAt'] ? new \DateTimeImmutable($attributes['createdAt']) : null;
        $this->updatedAt = null !== $attributes['updatedAt'] ? new \DateTimeImmutable($attributes['updatedAt']) : null;
        return $this;
    }
    /**
     * @throws \Exception|ApiException
     */
    public function create(string $uid, array $options = []) : Task
    {
        $options['uid'] = $uid;
        return Task::fromArray($this->http->post(self::PATH, $options), partial(\Closure::fromCallable([Tasks::class, 'waitTask']), $this->http));
    }
    public function all(?IndexesQuery $options = null) : IndexesResults
    {
        $indexes = [];
        $query = isset($options) ? $options->toArray() : [];
        $response = $this->allRaw($query);
        foreach ($response['results'] as $index) {
            $indexes[] = $this->newInstance($index);
        }
        $response['results'] = $indexes;
        return new IndexesResults($response);
    }
    public function allRaw(array $options = []) : array
    {
        return $this->http->get(self::PATH, $options);
    }
    public function getPrimaryKey() : ?string
    {
        return $this->primaryKey;
    }
    public function fetchPrimaryKey() : ?string
    {
        return $this->fetchInfo()->getPrimaryKey();
    }
    public function getUid() : ?string
    {
        return $this->uid;
    }
    public function getCreatedAt() : ?\DateTimeInterface
    {
        return $this->createdAt;
    }
    public function getUpdatedAt() : ?\DateTimeInterface
    {
        return $this->updatedAt;
    }
    public function fetchRawInfo() : ?array
    {
        return $this->http->get(self::PATH . '/' . $this->uid);
    }
    public function fetchInfo() : self
    {
        $response = $this->fetchRawInfo();
        return $this->fill($response);
    }
    public function update(array $body) : Task
    {
        return Task::fromArray($this->http->patch(self::PATH . '/' . $this->uid, $body), partial(Tasks::waitTask(...), $this->http));
    }
    public function delete() : Task
    {
        $response = $this->http->delete(self::PATH . '/' . $this->uid);
        \assert(null !== $response);
        return Task::fromArray($response, partial(Tasks::waitTask(...), $this->http));
    }
    /**
     * @param array<array{indexes: mixed}> $indexes
     */
    public function swapIndexes(array $indexes) : Task
    {
        return Task::fromArray($this->http->post('/swap-indexes', $indexes), partial(Tasks::waitTask(...), $this->http));
    }
    // Tasks
    public function getTask(int $uid) : Task
    {
        return Task::fromArray($this->http->get('/tasks/' . $uid), partial(Tasks::waitTask(...), $this->http));
    }
    public function getTasks(?TasksQuery $options = null) : TasksResults
    {
        $options = $options ?? new TasksQuery();
        if ([] !== $options->getIndexUids()) {
            $options->setIndexUids([$this->uid, ...$options->getIndexUids()]);
        } else {
            $options->setIndexUids([$this->uid]);
        }
        $response = $this->http->get('/tasks', $options->toArray());
        $response['results'] = \array_map(fn(array $task) => Task::fromArray($task, partial(Tasks::waitTask(...), $this->http)), $response['results']);
        return new TasksResults($response);
    }
    // Search
    /**
     * @phpstan-return ($options is array{raw: true|non-falsy-string|positive-int} ? array : SearchResult)
     */
    public function search(?string $query, array $searchParams = [], array $options = []) : SearchResult|array
    {
        $result = $this->rawSearch($query, $searchParams);
        if (\array_key_exists('raw', $options) && $options['raw']) {
            return $result;
        }
        $searchResult = new SearchResult($result);
        $searchResult->applyOptions($options);
        return $searchResult;
    }
    public function rawSearch(?string $query, array $searchParams = []) : array
    {
        $parameters = \array_merge(['q' => $query], $searchParams);
        $result = $this->http->post(self::PATH . '/' . $this->uid . '/search', $parameters);
        // patch to prevent breaking in laravel/scout getTotalCount method,
        // affects only Meilisearch >= v0.28.0.
        if (isset($result['estimatedTotalHits'])) {
            $result['nbHits'] = $result['estimatedTotalHits'];
        }
        return $result;
    }
    public function searchSimilarDocuments(SimilarDocumentsQuery $parameters) : SimilarDocumentsSearchResult
    {
        $result = $this->http->post(self::PATH . '/' . $this->uid . '/similar', $parameters->toArray());
        return new SimilarDocumentsSearchResult($result);
    }
    // Facet Search
    public function facetSearch(FacetSearchQuery $params) : FacetSearchResult
    {
        $response = $this->http->post(self::PATH . '/' . $this->uid . '/facet-search', $params->toArray());
        return new FacetSearchResult($response);
    }
    // Stats
    public function stats() : array
    {
        return $this->http->get(self::PATH . '/' . $this->uid . '/stats');
    }
    // Settings - Global
    public function getSettings() : array
    {
        return (new Settings($this->http->get(self::PATH . '/' . $this->uid . '/settings')))->getIterator()->getArrayCopy();
    }
    public function updateSettings($settings) : Task
    {
        return Task::fromArray($this->http->patch(self::PATH . '/' . $this->uid . '/settings', $settings), partial(Tasks::waitTask(...), $this->http));
    }
    public function resetSettings() : Task
    {
        return Task::fromArray($this->http->delete(self::PATH . '/' . $this->uid . '/settings'), partial(Tasks::waitTask(...), $this->http));
    }
}
