<?php

namespace App\Tests\Service;

use App\Entity\Event;
use App\Entity\User;
use App\Service\EventIndexService;
use App\Service\MeilisearchService;
use Doctrine\ORM\EntityManagerInterface;
use PHPUnit\Framework\TestCase;

class EventIndexServiceTest extends TestCase
{
    private MeilisearchService $meilisearch;
    private EventIndexService $service;

    protected function setUp(): void
    {
        $this->meilisearch = $this->createMock(MeilisearchService::class);
        $em = $this->createMock(EntityManagerInterface::class);
        $this->service = new EventIndexService($this->meilisearch, $em);
    }

    private function createEvent(bool $online = true, bool $secret = false): Event
    {
        $user = $this->createMock(User::class);
        $user->method('getId')->willReturn(42);
        $user->method('getCompanyName')->willReturn('Asso Test');
        $user->method('getFirstName')->willReturn('Test');
        $user->method('getLastName')->willReturn('User');

        $event = $this->createMock(Event::class);
        $event->method('getId')->willReturn(1);
        $event->method('getTitle')->willReturn('Brocante');
        $event->method('getDescription')->willReturn('Description');
        $event->method('getAddress')->willReturn('12 rue');
        $event->method('getZipcode')->willReturn('75001');
        $event->method('getCity')->willReturn('Paris');
        $event->method('getStartAt')->willReturn(new \DateTimeImmutable('2026-07-01 10:00'));
        $event->method('getEndAt')->willReturn(new \DateTimeImmutable('2026-07-01 18:00'));
        $event->method('isOnline')->willReturn($online);
        $event->method('isSecret')->willReturn($secret);
        $event->method('getAccount')->willReturn($user);

        return $event;
    }

    public function testIndexEventOnlineNotSecret(): void
    {
        $event = $this->createEvent(true, false);

        $this->meilisearch->expects(self::exactly(3))
            ->method('createIndexIfNotExists');

        $this->meilisearch->expects(self::exactly(3))
            ->method('addDocuments');

        $this->service->indexEvent($event);
    }

    public function testIndexEventOfflineRemovesFromGlobal(): void
    {
        $event = $this->createEvent(false, false);

        $this->meilisearch->expects(self::exactly(2))
            ->method('createIndexIfNotExists');

        $this->meilisearch->expects(self::exactly(2))
            ->method('addDocuments');

        $this->meilisearch->expects(self::once())
            ->method('indexExists')
            ->with('event_global')
            ->willReturn(true);

        $this->meilisearch->expects(self::once())
            ->method('deleteDocument')
            ->with('event_global', 1);

        $this->service->indexEvent($event);
    }

    public function testIndexEventSecretRemovesFromGlobal(): void
    {
        $event = $this->createEvent(true, true);

        $this->meilisearch->expects(self::once())
            ->method('indexExists')
            ->with('event_global')
            ->willReturn(false);

        $this->meilisearch->expects(self::never())
            ->method('deleteDocument');

        $this->service->indexEvent($event);
    }

    public function testRemoveEvent(): void
    {
        $event = $this->createEvent();

        $this->meilisearch->expects(self::exactly(3))
            ->method('deleteDocument');

        $this->meilisearch->expects(self::once())
            ->method('indexExists')
            ->with('event_global')
            ->willReturn(true);

        $this->service->removeEvent($event);
    }

    public function testIndexEventCatchesException(): void
    {
        $event = $this->createEvent();

        $this->meilisearch->method('createIndexIfNotExists')
            ->willThrowException(new \RuntimeException('Meilisearch down'));

        $this->service->indexEvent($event);
        self::assertTrue(true);
    }

    public function testRemoveEventCatchesException(): void
    {
        $event = $this->createEvent();

        $this->meilisearch->method('deleteDocument')
            ->willThrowException(new \RuntimeException('Meilisearch down'));

        $this->service->removeEvent($event);
        self::assertTrue(true);
    }

    public function testRemoveEventGlobalIndexNotExists(): void
    {
        $event = $this->createEvent();

        $this->meilisearch->expects(self::once())
            ->method('indexExists')
            ->with('event_global')
            ->willReturn(false);

        $this->meilisearch->expects(self::exactly(2))
            ->method('deleteDocument');

        $this->service->removeEvent($event);
    }

    public function testSearchEventsFallsBackOnException(): void
    {
        $this->meilisearch->method('search')
            ->willThrowException(new \RuntimeException('Meilisearch down'));

        $repo = $this->createMock(\Doctrine\ORM\EntityRepository::class);
        $repo->method('findBy')->willReturn([]);

        $em = $this->createMock(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($repo);

        $service = new EventIndexService($this->meilisearch, $em);
        $result = $service->searchEvents('event_global', 'test', ['isOnline' => true]);

        self::assertIsArray($result);
    }

    public function testSearchEventsEmptyQueryReturnsFallback(): void
    {
        $repo = $this->createMock(\Doctrine\ORM\EntityRepository::class);
        $repo->method('findBy')->willReturn([]);

        $em = $this->createMock(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($repo);

        $service = new EventIndexService($this->meilisearch, $em);
        $result = $service->searchEvents('event_global', '', ['isOnline' => true]);

        self::assertIsArray($result);
    }

    public function testSearchEventsWithResults(): void
    {
        $this->meilisearch->method('search')
            ->willReturn(['hits' => [['id' => 1], ['id' => 2]]]);

        $repo = $this->createMock(\Doctrine\ORM\EntityRepository::class);
        $repo->method('findBy')->willReturn([]);

        $em = $this->createMock(EntityManagerInterface::class);
        $em->method('getRepository')->willReturn($repo);

        $service = new EventIndexService($this->meilisearch, $em);
        $result = $service->searchEvents('event_global', 'brocante');

        self::assertIsArray($result);
    }

    public function testSearchEventsNoHits(): void
    {
        $this->meilisearch->method('search')
            ->willReturn(['hits' => []]);

        $em = $this->createMock(EntityManagerInterface::class);

        $service = new EventIndexService($this->meilisearch, $em);
        $result = $service->searchEvents('event_global', 'zzzzz');

        self::assertEmpty($result);
    }
}
