<?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 MTC\Core\Setting;
use Mtc\Plugins\TrustpilotAPI\Classes\TrustpilotReview;
use Mtc\Plugins\TrustpilotAPI\Classes\TrustpilotSettings;
use Order;
use Item;

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;
    }

    /**
     * send product review invitation link
     *
     * used for creating product invitation links after an order is placed
     * store the invitation links in the db for sending email at a later date
     *
     * @param object order
     *
     * @return array $result
     *
     */
    public static function sendInvitationLink(Order $order)
    {

        $access_tokens = self::getAccessToken();
        $invitation = self::createReviewInvitationLink($access_tokens['access_token'], $order);

        if (isset($invitation->reviewLinkId)) {
            $duplicate = TrustpilotReview::where('order_id', '=', $order->getId())->first();
            if ($duplicate == null) {
                $review = new TrustpilotReview();
                $review->link_id = $invitation->reviewLinkId;
                $review->review_url = $invitation->reviewUrl;
                $review->status = 0;
                $review->date = $order->getOrderDate('Y-m-d H:i:s');
                $review->send_date = date("Y-m-d", strtotime($order->getOrderDate('Y-m-d H:i:s') . " + " . TrustpilotSettings::getByKey('SEND_AFTER') ." week"));
                $review->order_id = $order->getId();
                $review->firstname = $order->address['billing']['firstname'];
                $review->lastname = $order->address['billing']['lastname'];
                $review->email = $order->info['email'];
                $review->save();
            }
        }

    }

    /**
     * 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 (sizeof($curl_options) > 0) {
            curl_setopt_array($curl_handle, $curl_options);
        }

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

        return $result;
    }
}
