<?php

/**
 * Trustpilot API Class
 *
 * @package  Mtc_TruspilotAPI
 * @author   Aivars Francis <aivars.francis@mtcmedia.co.uk>
 * @author   Efraim Zakarias <efraim.zakarias@mtcmedia.co.uk>
 *
 */
namespace Mtc\Plugins\TrustpilotAPI\Classes;

use Item;
use MTC\Core\Setting;
use Order;

class TrustpilotAPI
{
    /**
     * @var string Trustpilot API key
     */
    protected static $api_key = TRUSTPILOT_API_KEY;
    /**
     * @var string Trustpilot API secret key
     */
    protected static $api_secret = TRUSTPILOT_API_SECRET;
    /**
     * @var string Trustpilot API business ID
     */
    protected static $business_id = TRUSTPILOT_API_BUSINESS_ID;
    /**
     * @var bool Trustpilot API cache toggle
     */
    protected static $caching = TRUSTPILOT_API_ENABLE_CACHE;
    /**
     * @var int Trustpilot API cache time in seconds
     */
    protected static $cache_time = TRUSTPILOT_API_CACHE_TIME;
    /**
     * @var string Trustpilot API base URL
     */
    protected static $base_api_url = 'https://api.trustpilot.com/v1/';

    /**
     * get a list of product reviews based on the product epos code
     *
     * @param string $product_SKU
     * @param string $item_url
     *
     * @return json object
     *
     */
    public static function getProductReviews($product_SKU, $item_url = '')
    {
        if (empty($product_SKU) || $product_SKU === null) {
            $result = json_encode([
                'message' => 'Invalid product SKU',
            ]);
        } else {
            // declare cache file
            $cache_file = SITE_PATH.'/plugins/TrustpilotAPI/cache/'.SITE.'/trustpilot.reviews.'.$product_SKU.'.cache';

            // create the cache folder if unavailable
            if (! is_dir(SITE_PATH.'/plugins/TrustpilotAPI/cache/'.SITE)) {
                mkdir(SITE_PATH.'/plugins/TrustpilotAPI/cache/'.SITE, 0755, true);
            }

            // not a configuration setting but a flag changed later in the code
            $use_cache = false;

            // Check that the cache file is in place and that its last modified time
            // is within the cache time specified in settings. If it exceeds the time
            // then set a flag and do not run a fresh attempt at pulling the feed.
            if (self::$caching == true) {
                // check time
                if (file_exists($cache_file)) {
                    if ((date('U') - filemtime($cache_file)) < self::$cache_time) {
                        $use_cache = true;
                        $result = file_get_contents($cache_file);
                    }
                }
            }

            // if caching is disabled or
            // if cache is expired
            if (self::$caching == false || $use_cache == false) {
                $url = '?sku='.$product_SKU;

                if ($item_url != '') {
                    $url .= '&productUrl='.urlencode($item_url);
                }

                $url = self::$base_api_url.'product-reviews/business-units/'.self::$business_id.'/reviews'.$url;

                // request product reviews from trustpilot
                $result = self::initCurlRequest($url);

                // if caching is enabled write to file
                if (self::$caching) {
                    $fh = fopen($cache_file, 'w+');
                    fwrite($fh, $result);
                    fclose($fh);
                }
            }
        }

        return $result;
    }

    /**
     * get a list of product reviews summaries based on the product epos code
     *
     * @param string $product_SKU
     * @param string $item_url
     *
     * @return json object
     *
     */
    public static function getProductReviewsSummaries($product_SKU, $item_url = '')
    {
        if (empty($product_SKU) || $product_SKU === null) {
            $result = json_encode([
                'message' => 'Invalid product SKU',
            ]);
        } else {
            // declare cache file
            $cache_file =
                SITE_PATH.'/plugins/TrustpilotAPI/cache/'.SITE.'/trustpilot.reviews.summaries.'.$product_SKU.'.cache';

            // create the cache folder if unavailable
            if (! is_dir(SITE_PATH.'/plugins/TrustpilotAPI/cache/'.SITE)) {
                mkdir(SITE_PATH.'/plugins/TrustpilotAPI/cache/'.SITE, 0755, true);
            }

            // not a configuration setting but a flag changed later in the code
            $use_cache = false;

            // Check that the cache file is in place and that its last modified time
            // is within the cache time specified in settings.  If it exceeds the time
            // then set a flag and do not run a fresh attempt at pulling the feed.
            if (self::$caching == true) {
                // check time
                if (file_exists($cache_file)) {
                    if ((date('U') - filemtime($cache_file)) < self::$cache_time) {
                        $use_cache = true;
                        $result = file_get_contents($cache_file);
                    }
                }
            }

            // if caching is disabled or
            // if cache is expired
            if (self::$caching == false || $use_cache == false) {
                if ($item_url != '') {
                    $url = '?productUrl='.urlencode($item_url);
                } else {
                    $url = '?sku='.$product_SKU;
                }

                $url = self::$base_api_url.'product-reviews/business-units/'.self::$business_id.$url;

                // request product review summaries from trustpilot
                $result = self::initCurlRequest($url);

                // if caching is enabled write to file
                if (self::$caching) {
                    $fh = fopen($cache_file, 'w+');
                    fwrite($fh, $result);
                    fclose($fh);
                }
            }
        }

        return $result;
    }

    /**
     * display the product review rating
     *
     * @param string $product_SKU
     *
     * @return html image
     *
     */
    public static function printRating($product_SKU)
    {
        $product = self::getProductReviewsSummaries($product_SKU);
        $product = json_decode($product);
        $stars = floor($product->starsAverage);

        return '<img src="'.self::getStarImage($stars).'"/>';
    }

    /**
     * get star image from trustpilot
     * specifiy the required size
     *
     * @param int $stars
     * @param string $imagesize
     *
     * @return string image
     *
     */
    public static function getStarImage($stars, $size = 'image130x24')
    {
        // request product review star image from trustpilot
        $url = self::$base_api_url.'resources/images/stars/'.$stars;

        // init curl request
        $result = self::initCurlRequest($url);

        $images = json_decode($result);

        return $images->{$size}->url;
    }

    /**
     * get a list of general reviews for the business
     *
     * @return json object
     *
     */
    public static function getBusinessUnit()
    {
        // request list of general reviews for the business
        $url = self::$base_api_url.'business-units/'.self::$business_id.'/reviews';

        // init curl request
        $result = self::initCurlRequest($url);

        return $result;
    }

    public static function getBusinessId()
    {
        $url = self::$base_api_url.'business-units/find?name=domain.co.uk';

        // init curl request
        $result = json_decode(self::initCurlRequest($url));

        $setting = Setting::where('key', '=', 'TRUSTPILOT_API_BUSINESS_ID')->first();
        $setting->value = $result->id;
        $setting->save();
    }

    /**
     * create a product review invitation
     *
     * used for creating product invitation links after an order is placed.
     *
     * @param string $token
     * @param object order
     *
     * @return array $result
     *
     */
    private function createReviewInvitationLink($token, Order $order)
    {
        require SITE_PATH.'/shop/includes/image_folders.php';

        $url = self::$base_api_url.'private/product-reviews/business-units/'.self::$business_id.'/invitation-links';

        // set curl params
        $params = [
            'Authorization: Bearer '.$token,
            'Content-Type: application/json',
        ];

        $products = [];
        foreach ($order->getItems() as $item) {
            $item_info = new Item($item['item_id']);
            $brand = $item_info->brand_name === null ? '' : $item_info->brand_name;

            $products[] = [
                'productUrl' => SITE_URL.$item_info->url,
                'imageUrl' => SITE_URL.'/'.$product_folders['thumbs']['path'].'/'.$item_info->images[0]['name'],
                'name' => $item_info->name,
                'sku' => $item_info->id,
                'brand' => $brand,
            ];
        }

        $request = [
            'consumer' => [
                'email' => $order->info['email'],
                //'name' => $order->getCustomerName(),
                'name' => $order->address['billing']['firstname'],
            ],
            'referenceId' => SITE.time().rand(1, 50),
            'locale' => 'en-GB',
            'redirectUri' => SITE_URL,
            'products' => $products,
        ];

        // set curl options
        $curl_options = [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($request),
            CURLOPT_RETURNTRANSFER => true,
        ];

        // init curl request
        $result = self::initCurlRequest($url, $params, $curl_options);

        return json_decode($result);
    }

    /**
     * Create and send invite to Trustpilot
     *
     * @param Order $order
     * @return mixed
     */
    public function createReviewInvitation(Order $order)
    {
        if (! defined('TRUSTPILOT_API_ENABLED') || ! TRUSTPILOT_API_ENABLED) {
            return null;
        }
        require SITE_PATH.'/shop/includes/image_folders.php';

        $product_folders = $image_folders['product_folders'];

        self::refreshToken();

        $access_tokens = self::getAccessToken();
        $token = $access_tokens['access_token'];

        $url = 'https://invitations-api.trustpilot.com/v1/private/business-units/'.self::$business_id.'/invitations';

        // set curl params
        $params = [
            'Authorization: Bearer '.$token,
            'Content-Type: application/json',
        ];

        $products = [];
        foreach ($order->getItems() as $item) {
            $item_info = new Item($item['item_id']);
            $brand = $item_info->brand_name === null ? '' : $item_info->brand_name;

            $products[] = [
                'productUrl' => SITE_URL.$item_info->url,
                'imageUrl' => SITE_URL.'/'.$product_folders['thumbs']['path'].'/'.$item_info->images[0]['name'],
                'name' => $item_info->name,
                'sku' => $item_info->id,
                'brand' => $brand,
            ];
        }

        $request = [
            'tags' => [],
            'recipientEmail' => $order->info['email'],
            //"recipientName" => $order->getCustomerName(),
            'recipientName' => $order->address['billing']['firstname'],
            'senderEmail' => 'noreply.invitations@trustpilotmail.com',
            'senderName' => config('app.name'),
            'replyTo' => config('site.contact_email'),
            'preferredSendTime' => date('Y-m-d\TH:i:s'),
            'templateId' => TRUSTPILOT_TEMPLATE_ID,
            'referenceId' => SITE.time().rand(1, 50),
            'locale' => 'en-GB',
            'redirectUri' => $this->createReviewInvitationLink($token, $order)->reviewUrl ?? SITE_URL,
        ];

        // set curl options
        $curl_options = [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($request),
            CURLOPT_RETURNTRANSFER => true,
        ];

        // init curl request
        $result = self::initCurlRequest($url, $params, $curl_options);

        $response = json_decode($result);

        // If there is a failure and it is caused by expired token, refresh token and try again immediately
        if (
            ! isset($response->id)
            && $response->fault->detail->errorcode === 'keymanagement.service.access_token_expired'
        ) {
            self::refreshToken();
            $result = self::initCurlRequest($url, $params, $curl_options);
            $response = json_decode($result);
        }

        return $response;
    }

    /**
     * connects the user to trustpilot
     *
     * @param string $email
     * @param string $password
     *
     * @return json object $result
     *
     */
    public static function oAuth($email, $password)
    {
        if (empty($email) || empty($password)) {
            $result = json_encode([
                'status' => 'error',
                'message' => 'Email and/or password is empty',
            ]);
        } else {
            $url = self::$base_api_url.'oauth/oauth-business-users-for-applications/accesstoken';

            // set curl params
            $params = [
                'Authorization: Basic '.base64_encode(self::$api_key.':'.self::$api_secret),
                'Content-Type: application/x-www-form-urlencoded',
            ];

            // set post field params
            $payload['grant_type'] = 'password';
            $payload['username'] = $email;
            $payload['password'] = $password;

            // set curl options
            $curl_options = [
                CURLOPT_POST => true,
                CURLOPT_POSTFIELDS => http_build_query($payload),
                CURLOPT_RETURNTRANSFER => true,
            ];

            // init curl request
            $result = self::initCurlRequest($url, $params, $curl_options);
        }

        return $result;
    }

    /**
     * refresh the user login access token
     *
     * @return void
     *
     */
    public static function refreshToken()
    {
        $access_tokens = self::getAccessToken();

        $url = self::$base_api_url.'oauth/oauth-business-users-for-applications/refresh';

        // set curl params
        $params = [
            'Authorization: Basic '.base64_encode(self::$api_key.':'.self::$api_secret),
            'Content-Type: application/x-www-form-urlencoded',
        ];

        // set post field params
        $payload['grant_type'] = 'refresh_token';
        $payload['refresh_token'] = $access_tokens['refresh_token'];

        // set curl options
        $curl_options = [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => http_build_query($payload),
            CURLOPT_RETURNTRANSFER => true,
        ];

        // init curl request
        $result = self::initCurlRequest($url, $params, $curl_options);

        $callback = json_decode($result);

        $access_token = TrustpilotSettings::query()->where('key', '=', 'ACCESS_TOKEN')->first();
        $access_token->value = $callback->access_token;
        $access_token->save();

        $refresh_token = TrustpilotSettings::query()->where('key', '=', 'REFRESH_TOKEN')->first();
        $refresh_token->value = $callback->refresh_token;
        $refresh_token->save();
    }

    /**
     * get the user login access token
     *
     * @return array $oauth_response
     *
     */
    public static function getAccessToken()
    {
        $tpSettings = new \Mtc\Plugins\TrustpilotAPI\Classes\TrustpilotSettings;
        $oauth_response = [];
        $oauth_response['access_token'] = $tpSettings->getByKey('ACCESS_TOKEN');
        $oauth_response['refresh_token'] = $tpSettings->getByKey('REFRESH_TOKEN');

        return $oauth_response;
    }

    /**
     * initiate curl request
     *
     * @param string $url
     * @param array $params
     * @param array $curl_options
     *
     * @return json $response
     *
     */
    public static function initCurlRequest($url, $params = [], $curl_options = [])
    {
        $curl_handle = curl_init($url);

        // check if specific params were passed in
        // if not, use the default api key param
        if (empty($params)) {
            // add api key to params
            $params[] = 'apikey: '.self::$api_key;
        }

        // set required curl option
        curl_setopt(
            $curl_handle,
            CURLOPT_HTTPHEADER,
            $params,
        );

        // check if specific curl options were passed in
        // if not, use the default curl options
        if (empty($curl_options)) {
            $curl_options = [
                CURLOPT_RETURNTRANSFER => '1',
            ];
        }

        // add curl connection options to array
        // CURLOPT_CONNECTTIMEOUT = maximum amount of time in seconds that is
        // allowed to make the connection to the server
        // CURLOPT_TIMEOUT = maximum amount of time in seconds to which the
        // execution of individual cURL extension function calls will be limited
        $curl_options[] = [
            CURLOPT_CONNECTTIMEOUT => '2',
            CURLOPT_TIMEOUT => '3',
        ];

        // set curl options
        // sets multiple options for a cURL session
        if (count($curl_options) > 0) {
            curl_setopt_array($curl_handle, $curl_options);
        }

        // run request
        $result = curl_exec($curl_handle);
        curl_close($curl_handle);

        return $result;
    }
}
