<?php

declare(strict_types=1);

namespace NoahVet\Reef\Security\IAM\TokenExchange;

use NoahVet\Reef\Exception\OAuthRequestException;
use NoahVet\Reef\Factory\HttpClientFactoryInterface;

class DirectTokenExchanger implements TokenExchangerInterface
{
    public function __construct(
        private readonly HttpClientFactoryInterface $httpClientFactory,
        private readonly string $reefClientId,
        private readonly string $reefClientSecret,
        private readonly string $reefOAuthBaseUrl,
    ) {
    }

    public function exchangeTokenForCurrentClient(string $bearerToken): string
    {
        $client = $this->httpClientFactory->create();

        $stream = $this->httpClientFactory->getStreamFactory()->createStream();
        $stream->write(
            \http_build_query(
                [
                    'grant_type' => 'urn:ietf:params:oauth:grant-type:token-exchange',
                    'client_id' => $this->reefClientId,
                    'client_secret' => $this->reefClientSecret,
                    'subject_token' => $bearerToken,
                    'subject_token_type' => 'urn:ietf:params:oauth:token-type:access_token',
                    'audience' => $this->reefClientId,
                ],
            ),
        );

        $tokenExchangeResponse = $client->sendRequest(
            $this->httpClientFactory->getRequestFactory()->createRequest(
                'POST',
                $this->reefOAuthBaseUrl.'/oauth/v2/token',
            )
                ->withHeader('Content-Type', 'application/x-www-form-urlencoded')
                ->withBody($stream),
        );

        if (200 !== $tokenExchangeResponse->getStatusCode()) {
            throw new OAuthRequestException("Can't exchange token for current client");
        }

        $tokenExchangeResponseBody = \json_decode($tokenExchangeResponse->getBody()->getContents(), true);
        if (!\is_array($tokenExchangeResponseBody)) {
            throw new OAuthRequestException("Can't parse token exchange response");
        }
        $newAccessToken = $tokenExchangeResponseBody['access_token'] ?? null;

        if (null === $newAccessToken) {
            throw new OAuthRequestException("Can't find access token in token exchange response");
        }

        return $newAccessToken;
    }
}
