<?php

declare(strict_types=1);

namespace MonsieurBiz\SyliusSearchPlugin\Model\Document\Index;

use Elastica\Document;
use Elastica\Exception\ResponseException;
use Exception;
use JoliCode\Elastically\Client;
use MonsieurBiz\SyliusSearchPlugin\Exception\ReadOnlyIndexException;
use MonsieurBiz\SyliusSearchPlugin\Model\Document\ResultInterface;
use MonsieurBiz\SyliusSearchPlugin\Model\Documentable\DocumentableInterface;
use MonsieurBiz\SyliusSearchPlugin\Provider\DocumentRepositoryProviderInterface;
use Sylius\Component\Locale\Model\LocaleInterface;
use Sylius\Component\Resource\Repository\RepositoryInterface;
use Webmozart\Assert\Assert;

class Indexer extends AbstractIndex implements IndexerInterface
{
    protected DocumentRepositoryProviderInterface $documentRepositoryProvider;

    private RepositoryInterface $localeRepository;

    private array $locales = [];

    /**
     * PopulateCommand constructor.
     */
    public function __construct(
        Client $client,
        DocumentRepositoryProviderInterface $documentRepositoryProvider,
        RepositoryInterface $localeRepository,
    ) {
        parent::__construct($client);
        $this->documentRepositoryProvider = $documentRepositoryProvider;
        $this->localeRepository = $localeRepository;
    }

    public function getLocales(): array
    {
        if (count($this->locales) === 0) {
            $locales = $this->localeRepository->findAll();
            $this->locales = array_map(
                static fn (LocaleInterface $locale) => $locale->getCode(),
                $locales,
            );
        }

        return $this->locales;
    }

    public function indexAll(): void
    {
        foreach ($this->getLocales() as $locale) {
            $this->indexAllByLocale($locale);
        }
    }

    /**
     * Index all document for a locale.
     *
     *
     * @throws Exception
     */
    public function indexAllByLocale(string $locale): void
    {
        $indexName = $this->getIndexName($locale);
        $newIndex = $this->getIndexBuilder()->createIndex($indexName);

        $repositories = $this->documentRepositoryProvider->getRepositories();
        foreach ($repositories as $repository) {
            $documents = $repository->findAll();
            foreach ($documents as $document) {
                Assert::isInstanceOf($document, DocumentableInterface::class);
                $convertToDocument = $document->convertToDocument($locale);
                $this->getIndexer()->scheduleIndex($newIndex, new Document($convertToDocument->getUniqId(), $convertToDocument));
            }
        }

        $this->getIndexBuilder()->markAsLive(
            $newIndex,
            $indexName,
        );

        $this->getIndexer()->flush();

        $this->getIndexer()->refresh($indexName);

        try {
            $this->getIndexBuilder()->purgeOldIndices($indexName);
        } catch (ResponseException $exception) {
            throw new ReadOnlyIndexException($exception->getMessage());
        }
    }

    public function indexOne(DocumentableInterface $subject): void
    {
        foreach ($this->getLocales() as $locale) {
            $this->indexOneByLocale($subject->convertToDocument($locale), $locale);
            $this->getIndexer()->flush();
        }
    }

    public function indexOneByLocale(ResultInterface $document, string $locale): void
    {
        $this->getIndexer()->scheduleIndex(
            $this->getClient()->getIndex($this->getIndexName($locale)),
            new Document($document->getUniqId(), $document),
        );
    }

    public function removeOne(DocumentableInterface $subject): void
    {
        foreach ($this->getLocales() as $locale) {
            $this->removeOneByLocale($subject->convertToDocument($locale), $locale);
            $this->getIndexer()->flush();
        }
    }

    public function removeOneByLocale(ResultInterface $document, string $locale): void
    {
        $this->getIndexer()->scheduleDelete(
            $this->getClient()->getIndex($this->getIndexName($locale)),
            $document->getUniqId(),
        );
    }
}
