<?php

namespace App\Service;

use App\Entity\Event;
use Doctrine\ORM\EntityManagerInterface;

class EventIndexService
{
    private const INDEX_GLOBAL = 'event_global';
    private const INDEX_ADMIN = 'event_admin';
    private const INDEX_PREFIX = 'event_';

    public function __construct(
        private readonly MeilisearchService $meilisearch,
        private readonly EntityManagerInterface $em,
    ) {
    }

    /**
     * @param array<string, mixed>  $fallbackCriteria
     * @param array<string, string> $fallbackOrder
     *
     * @return list<Event>|array<Event>
     */
    public function searchEvents(string $index, string $query, array $fallbackCriteria = [], array $fallbackOrder = ['startAt' => 'ASC']): array
    {
        if ('' === $query) {
            return $this->em->getRepository(Event::class)->findBy($fallbackCriteria, $fallbackOrder);
        }

        try {
            $results = $this->meilisearch->search($index, $query);
            $ids = array_map(fn (array $hit) => $hit['id'], $results['hits'] ?? []);

            return $ids ? $this->em->getRepository(Event::class)->findBy(['id' => $ids]) : [];
        } catch (\Throwable) {
            return $this->em->getRepository(Event::class)->findBy($fallbackCriteria, $fallbackOrder);
        }
    }

    public function indexEvent(Event $event): void
    {
        try {
            $document = $this->toDocument($event);
            $accountIndex = self::INDEX_PREFIX.$event->getAccount()->getId();

            $this->meilisearch->createIndexIfNotExists(self::INDEX_ADMIN);
            $this->meilisearch->addDocuments(self::INDEX_ADMIN, [$document]);

            $this->meilisearch->createIndexIfNotExists($accountIndex);
            $this->meilisearch->addDocuments($accountIndex, [$document]);

            if ($event->isOnline() && !$event->isSecret()) {
                $this->meilisearch->createIndexIfNotExists(self::INDEX_GLOBAL);
                $this->meilisearch->addDocuments(self::INDEX_GLOBAL, [$document]);
            } else {
                $this->removeFromGlobal($event);
            }
        } catch (\Throwable) {
            // Meilisearch unavailable, skip indexing
        }
    }

    public function removeEvent(Event $event): void
    {
        try {
            $accountIndex = self::INDEX_PREFIX.$event->getAccount()->getId();

            $this->meilisearch->deleteDocument(self::INDEX_ADMIN, $event->getId());
            $this->meilisearch->deleteDocument($accountIndex, $event->getId());
            $this->removeFromGlobal($event);
        } catch (\Throwable) {
            // Meilisearch unavailable, skip removal
        }
    }

    private function removeFromGlobal(Event $event): void
    {
        if ($this->meilisearch->indexExists(self::INDEX_GLOBAL)) {
            $this->meilisearch->deleteDocument(self::INDEX_GLOBAL, $event->getId());
        }
    }

    /**
     * @return array<string, mixed>
     */
    private function toDocument(Event $event): array
    {
        return [
            'id' => $event->getId(),
            'title' => $event->getTitle(),
            'description' => $event->getDescription(),
            'address' => $event->getAddress(),
            'zipcode' => $event->getZipcode(),
            'city' => $event->getCity(),
            'startAt' => $event->getStartAt()?->format('Y-m-d H:i'),
            'endAt' => $event->getEndAt()?->format('Y-m-d H:i'),
            'isOnline' => $event->isOnline(),
            'isSecret' => $event->isSecret(),
            'accountId' => $event->getAccount()->getId(),
            'accountName' => $event->getAccount()->getCompanyName() ?? $event->getAccount()->getFirstName().' '.$event->getAccount()->getLastName(),
        ];
    }
}
