<?php 
/**
* Copyright (c) Microsoft Corporation.  All Rights Reserved.  
* Licensed under the MIT License.  See License in the project root 
* for license information.
* 
* GraphRequest File
* PHP version 7
*
* @category  Library
* @package   Microsoft.Graph
* @copyright 2016 Microsoft Corporation
* @license   https://opensource.org/licenses/MIT MIT License
* @version   GIT: 0.1.0
* @link      https://graph.microsoft.io/
*/

namespace BooklyOutlookCalendar\Lib\Microsoft\Vendor\Http;

use BooklyOutlookCalendar\Lib\Microsoft\Vendor\Core\GraphConstants;
use BooklyOutlookCalendar\Lib\Microsoft\Vendor\Exception\GraphException;

/**
 * Class GraphRequest
 *
 * @category Library
 * @package  Microsoft.Graph
 * @license  https://opensource.org/licenses/MIT MIT License
 * @link     https://graph.microsoft.io/
 */
class GraphRequest
{
    /**
    * A valid access token
    *
    * @var string
    */
    protected $accessToken;
    /**
    * The API version to use ("v1.0", "beta")
    *
    * @var string
    */
    protected $apiVersion;
    /**
    * The base url to call
    *
    * @var string
    */
    protected $baseUrl;
    /**
    * The endpoint to call
    *
    * @var string
    */
    protected $endpoint;
    /**
    * An array of headers to send with the request
    *
    * @var array(string => string)
    */
    protected $headers;
    /**
    * The body of the request (optional)
    *
    * @var string
    */
    protected $requestBody;
    /**
    * The type of request to make ("GET", "POST", etc.)
    *
    * @var object
    */
    protected $requestType;
    /**
    * The return type to cast the response as
    *
    * @var object
    */
    protected $returnType;
    /**
    * The timeout, in seconds
    *
    * @var string
    */
    protected $timeout;
    /**
    * The proxy port to use. Null to disable
    *
    * @var string
    */
    protected $proxyPort;

    /**
    * Constructs a new Graph Request object
    *
    * @param string $requestType The HTTP method to use, e.g. "GET" or "POST"
    * @param string $endpoint    The Graph endpoint to call
    * @param string $accessToken A valid access token to validate the Graph call
    * @param string $baseUrl     The base URL to call
    * @param string $apiVersion  The API version to use
    * @param string $proxyPort   The url where to proxy through
    * @throws GraphException when no access token is provided
    */ 
    public function __construct($requestType, $endpoint, $accessToken, $baseUrl, $apiVersion, $proxyPort = null)
    {
        $this->requestType = $requestType;
        $this->endpoint = $endpoint;
        $this->accessToken = $accessToken;

        if (!$this->accessToken) {
            throw new GraphException(GraphConstants::NO_ACCESS_TOKEN);
        }

        $this->baseUrl = $baseUrl;
        $this->apiVersion = $apiVersion;
        $this->timeout = 30;
        $this->headers = $this->_getDefaultHeaders();
        $this->proxyPort = $proxyPort;
    }

    /**
    * Sets a new accessToken
    *
    * @param string $accessToken A valid access token to validate the Graph call
    *
    * @return static object
    */
    public function setAccessToken($accessToken)
    {
        $this->accessToken = $accessToken;
        $this->headers['Authorization'] = 'Bearer ' . $this->accessToken;
        return $this;
    }

    /**
    * Sets the return type of the response object
    *
    * @param mixed $returnClass The object class to use
    *
    * @return static object
    */
    public function setReturnType($returnClass)
    {
        $this->returnType = $returnClass;

        return $this;
    }

    /**
    * Adds custom headers to the request
    *
    * @param array $headers An array of custom headers
    *
    * @return static object
    */
    public function addHeaders($headers)
    {
        $this->headers = array_merge($this->headers, $headers);
        return $this;
    }

    /**
    * Get the request headers
    *
    * @return array of headers
    */
    public function getHeaders()
    {
        return $this->headers;
    }

    /**
    * Attach a body to the request. Will JSON encode 
    * any BooklyOutlookCalendar\Lib\Microsoft\Vendor\Model objects as well as arrays
    *
    * @param mixed $obj The object to include in the request
    *
    * @return static object
    */
    public function attachBody($obj)
    {
        // Attach streams & JSON automatically
        if (is_string($obj)) {
            $this->requestBody = $obj;
        } 
        // By default, JSON-encode
        else {
            $this->requestBody = json_encode($obj);
        }
        return $this;
    }

    /**
    * Get the body of the request
    *
    * @return mixed request body of any type
    */
    public function getBody()
    {
        return $this->requestBody;
    }

    /**
    * Sets the timeout limit of the cURL request
    *
    * @param string $timeout The timeout in ms
    * 
    * @return static object
    */
    public function setTimeout($timeout)
    {
        $this->timeout = $timeout;
        return $this;
    }

    /**
    * Executes the HTTP request using Guzzle
    *
    * @param mixed $client The client to use in the request
    *
    * @throws GraphException if response is invalid
    *
    * @return mixed object or array of objects
    *         of class $returnType
    */
    public function execute($client = null)
    {
        $result = wp_remote_request( $this->_getRequestUrl(), array(
            'method'  => $this->requestType,
            'headers' => $this->headers,
            'body'    => $this->requestBody,
            'timeout' => $this->timeout,
        ) );

        if ( $result['response']['code'] >= 300 ) {
            $error = 'API Error';
            $body  = json_decode( $result['body'], true );
            if ( $body != null && $body['error'] ) {
                $error = $body['error']['code'] . ': ' . $body['error']['message'];
            }

            throw new GraphException( $error );
        }

        // Wrap response in GraphResponse layer
        $response = new GraphResponse(
            $this, 
            $result['body'],
            $result['response']['code'],
            $result['headers']['data']
        );

        // If no return type is specified, return GraphResponse
        $returnObj = $response;

        if ($this->returnType) {
            $returnObj = $response->getResponseAsObject($this->returnType);
        }
        return $returnObj; 
    }

    /**
    * Get a list of headers for the request
    *
    * @return array The headers for the request
    */
    private function _getDefaultHeaders()
    {
        $headers = array(
            'Content-Type' => 'application/json',
            'SdkVersion' => 'Graph-php-' . GraphConstants::SDK_VERSION,
            'Authorization' => 'Bearer ' . $this->accessToken
        );
        return $headers;
    }

    /**
    * Get the concatenated request URL
    *
    * @return string request URL
    */
    private function _getRequestUrl()
    {
        //Send request with opaque URL
        if (stripos($this->endpoint, "http") === 0) {
            return $this->endpoint;
        }

        return $this->baseUrl . $this->apiVersion . $this->endpoint;
    }

    /**
    * Checks whether the endpoint currently contains query
    * parameters and returns the relevant concatenator for 
    * the new query string
    *
    * @return string "?" or "&"
    */
    protected function getConcatenator()
    {
        if (stripos($this->endpoint, "?") === false) {
            return "?";
        }
        return "&";
    }
}
