<?php
/**
 * WooCommerce REST API HTTP Client
 */

namespace Webkul\WooCommerceBundle\Classes;

use Webkul\WooCommerceBundle\Classes\Oauth;

/**
 * REST APIClient class.
 */
class ApiClient
{

    /**
     * cURL handle.
     *
     * @var resource
     */
    protected $ch;

    /**
     * Store API URL.
     *
     * @var string
     */
    protected $url;

    /**
     * Consumer key.
     *
     * @var string
     */
    protected $consumerKey;

    /**
     * Consumer secret.
     *
     * @var string
     */
    protected $consumerSecret;

    /**
     * Client options.
     *
     * @var Options
     */
    protected $options;

    /**
     * Request.
     *
     * @var Request
     */
    private $request;

    /**
     * Response.
     *
     * @var Response
     */
    private $response;

    /**
     * Response headers.
     *
     * @var string
     */
    private $responseHeaders;

    /**
     * Initialize HTTP client.
     *
     * @param string $url            Store URL.
     * @param string $consumerKey    Consumer key.
     * @param string $consumerSecret Consumer Secret.
     * @param array  $options        Client options.
     */
    public function __construct($url, $consumerKey, $consumerSecret, $options = [])
    {
        if (!\function_exists('curl_version')) {
            throw new HttpClientException('cURL is NOT installed on this server', -1, new Request(), new Response());
        }

        $this->options        = $options;
        $this->consumerKey    = $consumerKey;
        $this->consumerSecret = $consumerSecret;
        $this->url            = $this->buildApiUrl($url);
    }

    /**
     * Check if is under SSL.
     *
     * @return bool
     */
    protected function isSsl()
    {
        return 'https://' === \substr($this->url, 0, 8);
    }

    /**
     * Build API URL.
     *
     * @param string $url Store URL.
     *
     * @return string
     */
    protected function buildApiUrl($url, $replaceUrl = null)
    {

        // $api = '/wc-api/';
        $api = '/wp-json/wc/';
        $url = str_replace(['/wp-admin', 'shop/'], ['',''], $url);
        $updatedUrl = \rtrim($url, '/') . $api . ($this->options['version'] ?? 'v2') . '/';
        
        if (!empty($this->options['type'])) {
            $updatedUrl = str_replace('/wp-json/wc/', '/wp-json/' . $this->options['type'] . '/', $updatedUrl);
        }
        $this->url = $updatedUrl;
        
        $response = $this->request('settings', [], []);
        
        if (!empty($response['data']['status']) && $response['data']['status'] == 404) {
            $updatedUrl = str_replace('v2', 'v1', $updatedUrl);
        }
        
        return $updatedUrl;
    }

    /**
     * Build URL.
     *
     * @param string $url        URL.
     * @param array  $parameters Query string parameters.
     *
     * @return string
     */
    protected function buildUrlQuery($url, $parameters = [])
    {
        if (!empty($parameters)) {
            $url .= '?' . \http_build_query($parameters);
        }

        return $url;
    }

    /**
     * Authenticate.
     *
     * @param string $url        Request URL.
     * @param string $method     Request method.
     * @param array  $parameters Request parameters.
     *
     * @return array
     */
    protected function authenticate($url, $method, $parameters)
    {
        // Setup authentication.
        if ($this->isSsl()) {
            $basicAuth  = new BasicAuth($this->consumerKey, $this->consumerSecret);
            $basicAuth->addAuthentication($this->ch, $url, $parameters);
        } else {
            // $parameters = ["page" => 1];
            $oAuth = new Oauth($this->consumerKey, $this->consumerSecret);
            $oAuth->addAuthentication($this->ch, $url, $method, $parameters);

        }
    }

    /**
     * Setup method.
     *
     * @param string $method Request method.
     */
    protected function setupMethod($method)
    {
        if ('POST' == $method) {
            \curl_setopt($this->ch, CURLOPT_POST, true);
        } elseif (\in_array($method, ['PUT', 'DELETE', 'OPTIONS'])) {
            \curl_setopt($this->ch, CURLOPT_CUSTOMREQUEST, $method);
        }
    }

    /**
     * Get request headers.
     *
     * @param  bool $sendData If request send data or not.
     *
     * @return array
     */
    protected function getRequestHeaders($sendData = false)
    {
        $headers = [
            'Accept'     => 'application/json',
        ];

        if ($sendData) {
            $headers['Content-Type'] = 'application/json;charset=utf-8';
        }

        return $headers;
    }

    /**
     * Create request.
     *
     * @param string $endpoint   Request endpoint.
     * @param string $method     Request method.
     * @param array  $data       Request data.
     * @param array  $parameters Request parameters.
     *
     * @return Request
     */
    protected function createRequest($endpoint, $parameters = [], $data = [])
    {
        if (array_key_exists($endpoint, $this->endpoints)) {
            $method = $this->endpoints[$endpoint]['method'];
            $endpoint = $this->endpoints[$endpoint]['url'];
            foreach ($parameters as $key => $val) {
                $needle = '{_' . $key . '}';

                if (strpos($endpoint, $needle) !== false) {
                    $endpoint = str_replace($needle, $val, $endpoint);
                    unset($parameters[$key]);
                }
            }
        } else {
            return;
        }

        $body    = '';
        $url     = $this->url . $endpoint;
        $hasData = !empty($data);

        // Setup authentication.
        $this->authenticate($url, $method, $parameters);

        // Setup method.
        $this->setupMethod($method);
        // Include post fields.
        if ($hasData) {
            $body = json_encode($data);
            curl_setopt($this->ch, CURLOPT_POSTFIELDS, $body);
        }
        
        return;
    }

    /**
     * Get response headers.
     *
     * @return array
     */
    protected function getResponseHeaders()
    {
        $headers = [];
        $lines   = \explode("\n", $this->responseHeaders);
        $lines   = \array_filter($lines, 'trim');

        foreach ($lines as $index => $line) {
            // Remove HTTP/xxx params.
            if (strpos($line, ': ') === false) {
                continue;
            }

            list($key, $value) = \explode(': ', $line);

            $headers[$key] = isset($headers[$key]) ? $headers[$key] . ', ' . trim($value) : trim($value);
        }

        return $headers;
    }

    /**
     * Create response.
     *
     * @return Response
     */
    protected function createResponse()
    {
        // Get response data.
        $rawBody    = \curl_exec($this->ch);

        $code    = \curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
        
        try {
            $body = json_decode($rawBody, true);
            
            $results = @json_decode($rawBody, true);
            
            // if ((json_last_error() !== JSON_ERROR_NONE)) {
            //     $pattern = '/\{(?:[^{}] |(?R))*\}/x';
            //     preg_match_all($pattern, $rawBody, $matches);

            //     $body = $matches[0][0] ? @json_decode($matches[0][0], true) : '';
            // }
        } catch (\Exception $e) {
            $body = [];
        }
        
        if (!empty($body) && gettype($body) != 'integer' && gettype($body) != 'boolean') {
            $response = array_merge(['code' => $code], $body);
        } else {
            $response = [ 'code' => $code ];
        }

        // Register response.
        return $response;
    }

    /**
     * Set default cURL settings.
     */
    protected function setDefaultCurlSettings()
    {
        $verifySsl       = $this->options['verifySsl'] ?? false;
        $timeout         = $this->options['timeout'] ?? 300 ;
        $followRedirects = $this->options['followRedirects'] ?? true;

        \curl_setopt($this->ch, CURLOPT_SSL_VERIFYPEER, $verifySsl);
        if (!$verifySsl) {
            \curl_setopt($this->ch, CURLOPT_SSL_VERIFYHOST, $verifySsl);
        }
        if ($followRedirects) {
            \curl_setopt($this->ch, CURLOPT_FOLLOWLOCATION, $followRedirects);
        }

        \curl_setopt($this->ch, CURLOPT_MAXREDIRS, 10);
        \curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        \curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        \curl_setopt($this->ch, CURLOPT_TIMEOUT, $timeout);
        \curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
    }


    /**
     * Make requests.
     *
     * @param string $endpoint   Request endpoint.
     * @param array  $parameters Request parameters.
     * @param array  $data       Request data.
     *
     * @return array
     */
    public function request($endpoint, $parameters = [], $payload = [])
    {
        // Initialize cURL.
        $this->ch = \curl_init();
        // Set request args.
        //dump($endpoint, $parameters, $payload);
        // die;
        $request = $this->createRequest($endpoint, $parameters, $payload);
        // Default cURL settings.
        $this->setDefaultCurlSettings();

        // Get response.
        $response = $this->createResponse();
        
        // Check for cURL errors.
        if (\curl_errno($this->ch)) {
            $response['error'] = \curl_error($this->ch);
            $response['code'] = 0;
        }

        \curl_close($this->ch);

        return $response;
    }

    /**
     * Get request data.
     *
     * @return Request
     */
    public function getRequest()
    {
        return $this->request;
    }

    /**
     * Get response data.
     *
     * @return Response
     */
    public function getResponse()
    {
        return $this->response;
    }

    protected $endpoints = [
        'products' => [
            'url'    => 'settings',
            'method' => 'GET',
        ],
        'addProduct' => [
            'url'    => 'products',
            'method' => 'POST',
        ],
        'addProductImages' => [
            'url'    => 'products/uploads/',
            'method' => 'POST',
        ],
        'getProduct' => [
            'url'    => 'products/{_id}',
            'method' => 'GET',
        ],
        'getWpmlProduct'=>[
            'url'    => 'products',
            'method' => 'GET',
        ],
        'getAllProduct' =>[
            'url'    => 'products',
            'method' => 'GET'
        ],
        'getAllProductByPage' => [
            'url'    => 'products?page={_page}',
            'method' => 'GET'
        ],
        'getAllVariableProductByPage' => [
            'url'    => 'products?page={_page}&type={_type}',
            'method' => 'GET'
        ],
        'updateProduct' => [
            'url'    => 'products/{_id}',
            'method' => 'PUT',
        ],
        'addVariation' => [
            'url'    => 'products/{_product}/variations',
            'method' => 'POST',
        ],
        'getVariation' => [
            'url'    => 'products/{_product}/variations',
            'method' => 'GET',
        ],
        'getVariationById' => [
            'url'    => 'products/{_product}/variations/{_variationid}',
            'method' => 'GET',
        ],
        'updateVariation' => [
            'url' => 'products/{_product}/variations/{_id}',
            'method' => 'PUT',
        ],
        'addAttribute' => [
            'url'    => 'products/attributes',
            'method' => 'POST',
        ],
        'addProductAttribute' => [
            'url'    => 'products/attribute',
            'method' => 'POST'
        ],
        'getAttributes' => [
            'url'    => 'products/attributes',
            'method' => 'GET',
        ],
        'getAttribute' => [
            'url'    => 'products/attributes/{_id}',
            'method' => 'GET',
        ],
        'updateAttribute' => [
            'url'    => 'products/attributes/{_id}',
            'method' => 'PUT',
        ],
        'getOptions' => [
            'url'    => 'products/attributes',
            'method' => 'GET',
        ],
        'addOption' => [
            'url'    => 'products/attributes/{_attribute}/terms',
            'method' => 'POST',
        ],
        'getOption' => [
            'url'   => 'products/attributes/{_attribute}/terms/{_id}',
            'method' => 'GET',
        ],
        'getAttributeOption' => [
            'url'   => 'products/attributes/{_attribute}/terms/',
            'method' => 'GET',
        ],
        'updateOption' => [
            'url'   => 'products/attributes/{_attribute}/terms/{_id}',
            'method' => 'PUT',
        ],
        'getAllOption' => [
            'url'   => 'products/attributes/{_attribute}/terms?page={_page}',
            'method' => 'GET',
        ],
        'addCategory' => [
            'url'    => 'products/categories',
            'method' => 'POST',
        ],
        'getCategory' => [
            'url'    => 'products/categories',
            'method' => 'GET',
        ],
        'updateCategory' => [
            'url'    => 'products/categories/{_id}',
            'method' => 'POST',
        ],
        'getCategories' => [
            'url'   => 'products/categories?per_page={_per_page}&page={_page}&orderby={_orderby}',
            'method' => 'GET',
        ],
        // 'addVariation' => [
        //     'url' => 'products/{_product}/variations/batch',
        //     'method' => 'POST',
        // ],
        'settings' => [
            'url' => 'products',
            'method' => 'GET',
        ],
        'addMedia' => [
            'url' => 'media',
            'method' => 'POST',
            'type' => 'wp',
        ],
        'getSystemStatus' => [
            'url' => 'system_status',
            'method' => 'GET',
        ],
        'deleteProduct' => [
            'url' => 'products/{_id}',
            'method' => 'DELETE'
        ],
        'deleteCategory' => [
            'url' => 'products/categories/{_id}',
            'method' => 'DELETE'
        ],
        'deleteAttribute' => [
            'url' => 'products/attributes/{_id}',
            'method' => 'DELETE'
        ],
        'deleteAttributeOptions' => [
            'url' => 'products/attributes/{_attribute}/terms/{_id}',
            'method' => 'DELETE'
        ],
        /** summary **/
        'catalogSummary' => [
            'url' => 'catalog/summary',
            'method' => 'GET',
        ],


    ];
}

