<?php

declare(strict_types=1);

namespace NoahVet\Reef\Test\A_Unit\Security\IAM\TokenExchange;

use NoahVet\Reef\Exception\OAuthRequestException;
use NoahVet\Reef\Factory\HttpClientFactoryInterface;
use NoahVet\Reef\Security\IAM\TokenExchange\DirectTokenExchanger;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\StreamInterface;

class DirectTokenExchangerTest extends TestCase
{
    /**
     * @var HttpClientFactoryInterface&MockObject
     */
    private HttpClientFactoryInterface $httpClientFactory;

    /**
     * @var ClientInterface&MockObject
     */
    private ClientInterface $client;

    /**
     * @var RequestFactoryInterface&MockObject
     */
    private RequestFactoryInterface $requestFactory;

    /**
     * @var StreamFactoryInterface&MockObject
     */
    private StreamFactoryInterface $streamFactory;

    private DirectTokenExchanger $tokenExchanger;

    protected function setUp(): void
    {
        $this->cache = $this->createMock(CacheItemPoolInterface::class);
        $this->httpClientFactory = $this->createMock(HttpClientFactoryInterface::class);

        $this->client = $this->createMock(ClientInterface::class);
        $this->streamFactory = $this->createMock(StreamFactoryInterface::class);
        $this->requestFactory = $this->createMock(RequestFactoryInterface::class);

        $this->httpClientFactory->method('create')->willReturn($this->client);
        $this->httpClientFactory->method('getRequestFactory')->willReturn($this->requestFactory);
        $this->httpClientFactory->method('getStreamFactory')->willReturn($this->streamFactory);

        $this->tokenExchanger = new DirectTokenExchanger(
            $this->httpClientFactory,
            'testClientId',
            'testClientSecret',
            'https://test-oauth-base-url.com',
        );
    }

    public function testExchangeFailsWhenResponseNotSuccessful(): void
    {
        $bearerToken = 'testBearerToken';

        $stream = $this->createMock(StreamInterface::class);
        $request = $this->createMock(RequestInterface::class);
        $request
            ->method('withHeader')
            ->willReturn($request)
        ;
        $request
            ->method('withBody')
            ->willReturn($request)
        ;

        $this->requestFactory
            ->method('createRequest')
            ->willReturn($request)
        ;

        $response = $this->createMock(ResponseInterface::class);
        $response->method('getStatusCode')->willReturn(400);
        $this->client->method('sendRequest')->willReturn($response);
        $this->streamFactory->method('createStream')->willReturn($stream);

        $this->expectException(OAuthRequestException::class);

        $this->tokenExchanger->exchangeTokenForCurrentClient($bearerToken);
    }
}
