<?php

/**
 * Created by PhpStorm.
 * User: alt
 * Date: 17-Oct-18
 * Time: 9:41 PM
 */
namespace XCloner\As247\CloudStorages\Support;

if (!defined('ABSPATH') && \PHP_SAPI !== 'cli') {
    die;
}
use XCloner\As247\CloudStorages\Cache\TempCache;
use XCloner\As247\CloudStorages\Contracts\Cache\Store;
use XCloner\GuzzleHttp\Client;
use XCloner\GuzzleHttp\Exception\GuzzleException;
class OneDriveOauth
{
    protected $clientId;
    protected $clientSecret;
    protected $accessToken;
    protected $refreshToken;
    protected $tenantId = 'common';
    protected $httpClient;
    /**
     * @var Store
     */
    protected $cache;
    protected $tokenEndpoint = 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token';
    protected $oauthUrl = 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize';
    protected $oauthParams = ['access_type' => 'offline', 'redirect_uri' => 'http://localhost:53682', 'response_type' => 'code', 'scope' => 'files.readwrite.all offline_access'];
    public function __construct(Store $cache = null)
    {
        $this->cache = $cache;
    }
    public function createAuthUrl()
    {
        $this->oauthParams['client_id'] = $this->clientId;
        $this->oauthParams['redirect_uri'] = $this->getCurrentUrl();
        $this->oauthParams['state'] = time();
        return str_replace('{tenant}', $this->tenantId, $this->oauthUrl) . '?' . http_build_query($this->oauthParams);
    }
    protected function getCurrentUrl()
    {
        return 'http://' . $_SERVER['HTTP_HOST'];
    }
    /**
     * @param $code
     * @return mixed
     * @throws GuzzleException
     */
    public function fetchAccessTokenWithAuthCode($code)
    {
        $postKey = 'form_params';
        $response = $this->getHttpClient()->post(str_replace('{tenant}', $this->tenantId, $this->tokenEndpoint), ['headers' => ['Accept' => 'application/json'], $postKey => ['client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'grant_type' => 'authorization_code', 'redirect_uri' => $this->getCurrentUrl(), 'code' => $code]]);
        return json_decode($response->getBody(), \true);
    }
    /**
     * Fetch new access token or return existing one
     * @param int $minLifetime If token life time smaller this value then it will fetch new token
     *            eg. current token expires in 50 sec, but we need 60, then it fetch new token
     * @return mixed
     * @throws GuzzleException
     */
    function getAccessToken($minLifetime = 600)
    {
        $key = $this->clientId . $this->clientSecret . $this->refreshToken;
        $token = $this->getCache()->get($key);
        if (!$token) {
            $token = [];
            $token['refresh_token'] = $this->refreshToken;
        }
        $token_created_at = isset($token['created_at']) ? $token['created_at'] : 0;
        $token_expired_in = isset($token['expires_in']) ? $token['expires_in'] : 0;
        $willBeExpireIn = $token_expired_in + $token_created_at - time();
        if (empty($token['access_token']) || $willBeExpireIn <= $minLifetime) {
            $token = $this->fetchAccessTokenWithRefreshToken($this->refreshToken);
            $token['created_at'] = time();
            if (!empty($token['access_token'])) {
                $this->getCache()->put($key, $token, 0);
            }
        }
        return $token['access_token'];
    }
    /**
     * @param string $refresh_token
     * @return mixed
     * @throws GuzzleException
     */
    function fetchAccessTokenWithRefreshToken($refresh_token = '')
    {
        $refresh_token = $refresh_token ?: $this->refreshToken;
        $postKey = 'form_params';
        $response = $this->getHttpClient()->post(str_replace('{tenant}', $this->tenantId, $this->tokenEndpoint), ['headers' => ['Accept' => 'application/json'], $postKey => ['client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'grant_type' => 'refresh_token', 'refresh_token' => $refresh_token]]);
        return json_decode($response->getBody(), \true);
    }
    /**
     * Get a instance of the Guzzle HTTP client.
     *
     * @return Client
     */
    protected function getHttpClient()
    {
        if (is_null($this->httpClient)) {
            $this->httpClient = new Client();
        }
        return $this->httpClient;
    }
    /**
     * @return TempCache|Store
     */
    public function getCache()
    {
        if (!$this->cache) {
            $this->cache = new TempCache(static::class);
        }
        return $this->cache;
    }
    /**
     * @param string $clientId
     * @return OneDriveOauth
     */
    public function setClientId(string $clientId): OneDriveOauth
    {
        $this->clientId = $clientId;
        return $this;
    }
    /**
     * @param string $clientSecret
     * @return OneDriveOauth
     */
    public function setClientSecret(string $clientSecret): OneDriveOauth
    {
        $this->clientSecret = $clientSecret;
        return $this;
    }
    /**
     * @param mixed $accessToken
     * @return OneDriveOauth
     */
    public function setAccessToken($accessToken)
    {
        $this->accessToken = $accessToken;
        return $this;
    }
    /**
     * @param string $refreshToken
     * @return OneDriveOauth
     */
    public function setRefreshToken(string $refreshToken): OneDriveOauth
    {
        $this->refreshToken = $refreshToken;
        return $this;
    }
    /**
     * @param mixed $httpClient
     * @return OneDriveOauth
     */
    public function setHttpClient($httpClient)
    {
        $this->httpClient = $httpClient;
        return $this;
    }
    /**
     * @param $cache
     * @return OneDriveOauth
     */
    public function setCache(Store $cache): OneDriveOauth
    {
        $this->cache = $cache;
        return $this;
    }
    /**
     * @param string $tokenEndpoint
     * @return OneDriveOauth
     */
    public function setTokenEndpoint(string $tokenEndpoint): OneDriveOauth
    {
        $this->tokenEndpoint = $tokenEndpoint;
        return $this;
    }
    /**
     * @return mixed
     */
    public function getTenantId()
    {
        return $this->tenantId;
    }
    /**
     * @param mixed $tenantId
     * @return OneDriveOauth
     */
    public function setTenantId($tenantId)
    {
        $this->tenantId = $tenantId;
        return $this;
    }
    /**
     * @return string
     */
    public function getOauthUrl(): string
    {
        return $this->oauthUrl;
    }
    /**
     * @param string $oauthUrl
     * @return OneDriveOauth
     */
    public function setOauthUrl(string $oauthUrl): OneDriveOauth
    {
        $this->oauthUrl = $oauthUrl;
        return $this;
    }
    /**
     * @return string
     */
    public function getClientId(): string
    {
        return $this->clientId;
    }
    /**
     * @return string
     */
    public function getClientSecret(): string
    {
        return $this->clientSecret;
    }
    /**
     * @return string
     */
    public function getRefreshToken(): string
    {
        return $this->refreshToken;
    }
    /**
     * @return string
     */
    public function getTokenEndpoint(): string
    {
        return $this->tokenEndpoint;
    }
    /**
     * @return string[]
     */
    public function getOauthParams(): array
    {
        return $this->oauthParams;
    }
    /**
     * @param string[] $oauthParams
     * @return OneDriveOauth
     */
    public function setOauthParams(array $oauthParams): OneDriveOauth
    {
        $this->oauthParams = $oauthParams;
        return $this;
    }
}
