<?php

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

use NoahVet\Reef\Domain\Security\Model\ResourceType;
use NoahVet\Reef\Domain\Security\Policy\PolicyResult;
use NoahVet\Reef\Exception\IAMException;
use NoahVet\Reef\Iam\Client;
use NoahVet\Reef\Infrastructure\Security\Api\JanePolicyResultPublisher;
use NoahVet\Reef\Infrastructure\Security\IAM\Transformer\PolicyResultTransformer;
use NoahVet\Reef\Infrastructure\Security\IAM\Transformer\PolicyResultTransformerInterface;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Symfony\Component\HttpFoundation\Response;

class JanePolicyResultPublisherTest extends TestCase
{
    private Client $client;

    private PolicyResult $policyResult;

    private PolicyResultTransformer $transformer;

    protected function setUp(): void
    {
        $this->client = $this->getMockBuilder(Client::class)
            ->disableOriginalConstructor()
            ->getMock()
        ;

        $this->transformer = new PolicyResultTransformer();

        $this->policyResult = new PolicyResult(
            'test-policy',
            'subject-1',
            ResourceType::fromString('reef:phpunit:resource'),
        );
        $this->policyResult->etag = 'etag-abc';
    }

    public function testHttp200DoesNotThrow(): void
    {
        $response = $this->buildResponse(Response::HTTP_OK);

        $this->client
            ->method('executeRawEndpoint')
            ->willReturn($response)
        ;

        $publisher = new JanePolicyResultPublisher($this->client, $this->transformer);

        // Expect no exception
        $publisher->publish($this->policyResult);
        $this->addToAssertionCount(1);
    }

    public function testHttp201DoesNotThrow(): void
    {
        $response = $this->buildResponse(Response::HTTP_CREATED);

        $this->client->method('executeRawEndpoint')->willReturn($response);

        $publisher = new JanePolicyResultPublisher($this->client, $this->transformer);
        $publisher->publish($this->policyResult);
        $this->addToAssertionCount(1);
    }

    public function testHttp500ThrowsIAMException(): void
    {
        $response = $this->buildResponse(Response::HTTP_INTERNAL_SERVER_ERROR, 'Server error');

        $this->client->method('executeRawEndpoint')->willReturn($response);

        $publisher = new JanePolicyResultPublisher($this->client, $this->transformer);

        self::expectException(IAMException::class);
        $publisher->publish($this->policyResult);
    }

    public function testHttp400ThrowsIAMException(): void
    {
        $response = $this->buildResponse(Response::HTTP_BAD_REQUEST, 'Bad request');

        $this->client->method('executeRawEndpoint')->willReturn($response);

        $publisher = new JanePolicyResultPublisher($this->client, $this->transformer);

        self::expectException(IAMException::class);
        $publisher->publish($this->policyResult);
    }

    public function testTransformerIsCalledBeforeEndpointCall(): void
    {
        $transformer = $this->createMock(PolicyResultTransformerInterface::class);
        $transformer
            ->expects(self::once())
            ->method('toApiDto')
            ->with($this->policyResult)
            ->willReturn((new \NoahVet\Reef\Iam\Model\PolicyResultPost())->setName('test'))
        ;

        $response = $this->buildResponse(Response::HTTP_OK);
        $this->client->method('executeRawEndpoint')->willReturn($response);

        $publisher = new JanePolicyResultPublisher($this->client, $transformer);
        $publisher->publish($this->policyResult);
    }

    private function buildResponse(int $statusCode, string $body = ''): ResponseInterface
    {
        $stream = $this->createMock(StreamInterface::class);
        $stream->method('getContents')->willReturn($body);

        $response = $this->createMock(ResponseInterface::class);
        $response->method('getStatusCode')->willReturn($statusCode);
        $response->method('getBody')->willReturn($stream);

        return $response;
    }
}
