<?php

declare(strict_types=1);

namespace NoahVet\Reef\Test\A_Unit\Iterator;

use NoahVet\Reef\Factory\Jane\EndpointFactoryInterface;
use NoahVet\Reef\Iterator\ApiClientSimpleReefIterator;
use NoahVet\Reef\Jane\Client;
use NoahVet\Reef\Jane\Runtime\Client\Endpoint;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;

class ApiClientSimpleReefIteratorTest extends TestCase
{
    /**
     * @var Client&MockObject
     */
    private Client $client;

    /**
     * @var EndpointFactoryInterface&MockObject
     */
    private EndpointFactoryInterface $endpointFactory;

    protected function setUp(): void
    {
        $this->client = $this->createMock(Client::class);
        $this->endpointFactory = $this->createMock(EndpointFactoryInterface::class);
    }

    public function testSinglePageResult(): void
    {
        $enumerator = new ApiClientSimpleReefIterator(
            $this->client,
            Endpoint::class,
            $this->endpointFactory,
            ['filter1' => 'filterValue'],
            40,
        );

        $endpoint = $this->createMock(Endpoint::class);
        $page0Response = $this->createMock(ResponseInterface::class);
        $page0Response->method('getStatusCode')
            ->willReturn(200)
        ;

        $this->client->expects($this->once())
            ->method('executeRawEndpoint')
            ->with($endpoint)
            ->willReturn($page0Response)
        ;

        $endpoint->expects($this->once())
            ->method('parseResponse')
            ->with($page0Response)
            ->willReturn([
                new \stdClass(),
            ])
        ;

        $invokedCount = self::exactly(2);
        $this->endpointFactory->expects($invokedCount)
            ->method('create')
            ->willReturnCallback(function ($parameters) use ($endpoint, $invokedCount) {
                if (1 === $invokedCount->getInvocationCount()) {
                    $this->assertSame(['page' => 0, 'limit' => 40, 'filter1' => 'filterValue'], $parameters);
                }

                if (2 === $invokedCount->getInvocationCount()) {
                    $this->assertSame(['filter1' => 'filterValue'], $parameters);
                }

                return $endpoint;
            })
            ->willReturn($endpoint)
        ;

        $results = \iterator_to_array($enumerator);

        $this->assertCount(1, $results);
    }

    public function testMultiplePageResult(): void
    {
        $enumerator = new ApiClientSimpleReefIterator(
            $this->client,
            Endpoint::class,
            $this->endpointFactory,
            ['filter1' => 'filterValue'],
            1,
        );

        $endpoint = $this->createMock(Endpoint::class);
        $page0Response = $this->createMock(ResponseInterface::class);
        $page0Response->method('getStatusCode')
            ->willReturn(206)
        ;
        $page0Response->method('getHeader')
            ->with('Content-Range')
            ->willReturn(['unit 0-0/2'])
        ;

        $page1Response = $this->createMock(ResponseInterface::class);
        $page1Response->method('getStatusCode')
            ->willReturn(206)
        ;
        $page1Response->method('getHeader')
            ->with('Content-Range')
            ->willReturn(['unit 1-1/2'])
        ;

        $this->client->expects(self::exactly(2))
            ->method('executeRawEndpoint')
            ->with($endpoint)
            ->willReturnOnConsecutiveCalls($page0Response, $page1Response)
        ;

        $endpoint->expects($this->exactly(2))
            ->method('parseResponse')
            ->willReturn([
                new \stdClass(),
            ])
        ;

        $endPointInvokedCount = self::exactly(4);
        $this->endpointFactory->expects($endPointInvokedCount)
            ->method('create')
            ->willReturnCallback(function ($parameters) use ($endpoint, $endPointInvokedCount) {
                if (1 === $endPointInvokedCount->getInvocationCount() % 2) {
                    $this->assertSame(['page' => ($endPointInvokedCount->getInvocationCount() % 2) - 1, 'limit' => 1, 'filter1' => 'filterValue'], $parameters);
                }

                if (0 === $endPointInvokedCount->getInvocationCount() % 2) {
                    $this->assertSame(['filter1' => 'filterValue'], $parameters);
                }

                return $endpoint;
            })
            ->willReturn($endpoint)
        ;

        $results = \iterator_to_array($enumerator);

        $this->assertCount(2, $results);
    }
}
