<?php

namespace Fifa\ConnectServiceBus\Sdk\Authentication;

use Fifa\ConnectServiceBus\Sdk\Api\ApiException;
use Fifa\ConnectServiceBus\Sdk\Authentication\Model\AuthenticationInformation;
use Fifa\ConnectServiceBus\Sdk\Authentication\Model\Token;
use Fifa\ConnectServiceBus\Sdk\Exception\AuthenticationException;

/**
 * Class TokenProvider
 * @package Fifa\ConnectServiceBus\Sdk\Authentication
 */
class TokenProvider implements TokenProviderInterface
{
    /**
     * @var AuthenticationInformation
     */
    private $authenticationInformation;

    /**
     * @var TokenCacheInterface
     */
    private $tokenCache;

    /**
     * Constructor
     * @param AuthenticationInformation $authenticationInformation
     * @param CacheStorageInterface $cacheStorage
     * @param int $expirationTimeSpanInSeconds
     */
    public function __construct(AuthenticationInformation $authenticationInformation, CacheStorageInterface $cacheStorage, int $expirationTimeSpanInSeconds = 300)
    {
        $this->authenticationInformation = $authenticationInformation;
        $this->tokenCache = new TokenCache($authenticationInformation, $cacheStorage, $expirationTimeSpanInSeconds);
    }

    /**
     * Returns the token for API authorization
     *
     * @return Token
     * @throws AuthenticationException
     */
    public function getToken(): Token
    {
        $token = $this->tokenCache->getTokenFromCache();
        if(is_null($token)) {
            $token = $this->getAuthenticationHeader();
            $this->tokenCache->storeCache($token);
        }

        return $token;
    }

    /**
     * Post the token generated from the symmetric key and other information to STS URL and construct the authentication header
     *
     * @return Token
     * @throws AuthenticationException
     */
    private function getAuthenticationHeader(): Token
    {
        $clientSecret = urlencode($this->authenticationInformation->getSecretKey());
        // This is a built-in scope for every application that refers to the static list of permissions configured on the application registration
        $scope = urlencode($this->authenticationInformation->getResourceUri() . '/.default');
        // Information about the app
        $clientId = urlencode($this->authenticationInformation->getClientId());

        // Construct the body for the STS request
        $authenticationRequestBody = 'grant_type=client_credentials&client_secret=' . $clientSecret . '&scope=' . $scope . '&client_id=' . $clientId;

        //Using curl to post the information to STS and get back the authentication response
        $ch = curl_init();
        // set url
        $stsUrl = 'https://login.microsoftonline.com/' . $this->authenticationInformation->getTenant() . '/oauth2/v2.0/token';
        curl_setopt($ch, CURLOPT_URL, $stsUrl);
        // Get the response back as a string
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        // Mark as Post request
        curl_setopt($ch, CURLOPT_POST, 1);
        // Set the parameters for the request
        curl_setopt($ch, CURLOPT_POSTFIELDS, $authenticationRequestBody);

        // read the output from the post request
        $output = curl_exec($ch);
        // close curl resource to free up system resources
        curl_close($ch);
        // decode the response from sts using json decoder
        $tokenOutput = json_decode($output);

        if (!isset($tokenOutput->access_token)) {
            $description = $tokenOutput->error_description ?? '';
            $apiException = new ApiException(
                'Failed getting the access token. ' . $description
            );
            throw new AuthenticationException($apiException, $description);
        }

        return new Token($tokenOutput);
    }
}