<?php

namespace Tests\Feature;

use App\Services\MeilisearchService;
use Meilisearch\Client;
use Meilisearch\Endpoints\Indexes;
use Meilisearch\Search\SearchResult;
use Mockery\MockInterface;
use Tests\TenantTestCase;

class MeilisearchServicePaginationTest extends TenantTestCase
{
    /**
     * Inject a mock Meilisearch client whose index returns a search result built
     * from the given hit count and exact total, while capturing the search params.
     *
     * @param array<int, array<string, mixed>> $hits
     */
    private function serviceWithCapturedSearch(array $hits, int $totalHits, array &$captured): MeilisearchService
    {
        $searchResult = $this->createMock(SearchResult::class);
        $searchResult->method('getHits')->willReturn($hits);
        $searchResult->method('getTotalHits')->willReturn($totalHits);
        $searchResult->method('getProcessingTimeMs')->willReturn(1);
        // Estimated count is unavailable in numbered-pagination mode; guard against regressions.
        $searchResult->method('getEstimatedTotalHits')->willReturn(null);

        $index = \Mockery::mock(Indexes::class);
        $index->shouldReceive('search')
            ->once()
            ->andReturnUsing(function (string $query, array $params) use (&$captured, $searchResult) {
                $captured = $params;
                return $searchResult;
            });

        $client = \Mockery::mock(Client::class);
        $client->shouldReceive('index')->andReturn($index);

        $service = new MeilisearchService();
        $reflection = new \ReflectionClass($service);
        $clientProp = $reflection->getProperty('client');
        $clientProp->setAccessible(true);
        $clientProp->setValue($service, $client);

        return $service;
    }

    public function testSearchVehiclesUsesNumberedPaginationAndReturnsExactTotal(): void
    {
        $captured = [];
        $hits = array_map(fn ($id) => ['id' => $id], range(1, 5));
        $service = $this->serviceWithCapturedSearch($hits, 5, $captured);

        $result = $service->searchVehicles(['is_published' => true], [], 24, 0);

        // Exact total comes from totalHits, not the estimate.
        $this->assertEquals(5, $result['total']);
        $this->assertCount(5, $result['hits']);

        // Numbered pagination params are used; the estimated-count params are gone.
        $this->assertSame(24, $captured['hitsPerPage']);
        $this->assertSame(1, $captured['page']);
        $this->assertArrayNotHasKey('limit', $captured);
        $this->assertArrayNotHasKey('offset', $captured);
    }

    public function testSearchVehiclesMapsOffsetToPageNumber(): void
    {
        $captured = [];
        $service = $this->serviceWithCapturedSearch([], 100, $captured);

        // offset 48 with a page size of 24 is the third page.
        $service->searchVehicles([], [], 24, 48);

        $this->assertSame(24, $captured['hitsPerPage']);
        $this->assertSame(3, $captured['page']);
    }

    public function testGetVehicleIdsReturnsExactTotalAndStaysOnFirstPageForCountOnly(): void
    {
        $captured = [];
        $hits = array_map(fn ($id) => ['id' => $id], range(1, 3));
        $service = $this->serviceWithCapturedSearch($hits, 3, $captured);

        // The count-only call passes limit 0; it must still produce a valid request.
        $result = $service->getVehicleIds(['is_published' => true], [], 0, 0);

        $this->assertEquals(3, $result['total']);
        $this->assertEquals([1, 2, 3], $result['ids']);
        $this->assertSame(1, $captured['hitsPerPage']);
        $this->assertSame(1, $captured['page']);
        $this->assertArrayNotHasKey('limit', $captured);
        $this->assertArrayNotHasKey('offset', $captured);
        $this->assertSame(['id'], $captured['attributesToRetrieve']);
    }
}
