<?php

namespace NoahVet\Reef\Test\A_Unit\Infrastructure\Security\Api;

use NoahVet\Reef\Iam\Client;
use NoahVet\Reef\Iam\Model\PolicyResult;
use NoahVet\Reef\Infrastructure\Security\Api\JanePolicyResultStore;
use PHPUnit\Framework\TestCase;

class JanePolicyResultStoreTest extends TestCase
{
    private Client $client;

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

    public function testFindEtagReturnsEtagWhenFound(): void
    {
        $stored = (new PolicyResult())
            ->setName('test-policy')
            ->setSubject('subject-1')
            ->setEtag('etag-abc')
        ;

        $this->client
            ->method('getPolicyResultCollection')
            ->with([
                'name' => 'test-policy',
                'subject' => 'subject-1',
            ], [])
            ->willReturn([$stored])
        ;

        $store = new JanePolicyResultStore($this->client, []);

        self::assertSame('etag-abc', $store->findEtag('test-policy', 'subject-1'));
    }

    public function testFindEtagReturnsNullWhenNotFound(): void
    {
        $this->client
            ->method('getPolicyResultCollection')
            ->willReturn([])
        ;

        $store = new JanePolicyResultStore($this->client, []);

        self::assertNull($store->findEtag('test-policy', 'subject-missing'));
    }

    public function testFindEtagsReturnsSubjectToEtagMap(): void
    {
        $result1 = (new PolicyResult())
            ->setSubject('subject-a')
            ->setEtag('etag-a')
        ;
        $result2 = (new PolicyResult())
            ->setSubject('subject-b')
            ->setEtag('etag-b')
        ;

        $this->client
            ->method('getPolicyResultCollection')
            ->willReturnOnConsecutiveCalls(
                [$result1, $result2],
                [],
            )
        ;

        $store = new JanePolicyResultStore($this->client, []);
        $etags = $store->findEtags('test-policy', ['subject-a', 'subject-b']);

        self::assertSame([
            'subject-a' => 'etag-a',
            'subject-b' => 'etag-b',
        ], $etags);
    }

    public function testFindEtagsAggregatesMultiplePages(): void
    {
        $page = 0;

        $page1 = (new PolicyResult())
            ->setSubject('subject-a')
            ->setEtag('etag-a')
        ;
        $page2 = (new PolicyResult())
            ->setSubject('subject-b')
            ->setEtag('etag-b')
        ;

        $this->client
            ->method('getPolicyResultCollection')
            ->willReturnCallback(static function (array $parameters) use (&$page, $page1, $page2): array {
                ++$page;

                self::assertSame([
                    'name' => 'test-policy',
                    'subject[]' => ['subject-a', 'subject-b'],
                    'page' => $page,
                ], $parameters);

                if (1 === $page) {
                    return [$page1];
                }

                if (2 === $page) {
                    return [$page2];
                }

                return [];
            })
        ;

        $store = new JanePolicyResultStore($this->client, []);
        $etags = $store->findEtags('test-policy', ['subject-a', 'subject-b']);

        self::assertSame([
            'subject-a' => 'etag-a',
            'subject-b' => 'etag-b',
        ], $etags);
    }
}
