<?php
/**
 * Gratify Pay Plugin Merchant Handler Cass.
 */

class GratifyPay_Merchant{
    /**
     * Protected variables.
     * 
     * @var     WC_Gratify_Pay_Gateway  $gateway
     * 
     * @var     string                  $payment_types_url
     * @var     string                  $configuration_url
     * @var     string                  $orders_url
     * @var     string                  $direct_payment_capture_url
     * 
     */
    protected $gateway, $payment_types_url, $configuration_url, $orders_url, $direct_payment_capture_url;

    /**
     * Constructor 
     * 
     * @uses WC_Gratify_Pay_Gateway::get_api_url()
     */
    public function __construct(){
        include('endpoints.php');
        $this->gateway = WC_Gratify_Pay_Gateway::getInstance();
        $api_url = $this->gateway->get_api_url();
		$gp_api = "https://gratifypay.com/gp/";
		$this->configuration_url = $api_url . $endpoints['v1']['configuration'];
		$this->orders_url = $api_url . $endpoints['v1']['orders'];
		$this->payments_url = $api_url . $endpoints['v1']['payments'];
		$this->direct_payment_capture_url = $api_url . $endpoints['v1']['capture-payment'];
		$this->merchant_url = $gp_api . $endpoints["gp"]["merchant"];
		$this->instalments_url = $gp_api . $endpoints["gp"]["calculate-instalments"];
    }
	/**
	 * Filters the string used for Merchant IDs & Secret Keys.
	 *
	 * @since	1.0.0
	 * @param	string	$str
	 * @return	string
	 * @used-by	self::build_authorization_header()
	 */
	private function cleanup_string($str) {
		return preg_replace('/[^a-z0-9]+/i', '', $str);
	}
    /**
	 * Build the Gratify Pay Authorization header for use with the APIs.
	 *
	 * @since	1.0.0
	 * @uses	self::cleanup_string()
	 * @uses	WC_Gratify_Pay_Gateway::get_merchant_id()
	 * @uses	WC_Gratify_Pay_Gateway::get_secret_key()
	 * @return	string
	 * @used-by	self::get_from_api()
	 * @used-by	self::post_to_api()
	 */
	private function build_authorization_header() {
		$cleaned_merchant_id = $this->cleanup_string($this->gateway->get_merchant_id());
		$cleaned_secret_key = $this->cleanup_string($this->gateway->get_secret_key());

		return 'Basic ' . base64_encode($cleaned_merchant_id . ':' . $cleaned_secret_key);
	}
	/**
	 * Build the Gratify Pay User-Agent header for use with the APIs.
	 *
	 * @since	1.0.0
	 * @global	string	$wp_version
	 * @uses	WC()
	 * @return	string
	 * @used-by	self::get_from_api()
	 * @used-by	self::post_to_api()
	 */
	private function build_user_agent_header() {
		global $wp_version;

		$plugin_version = GratifyPay_Plugin::$version;
		$php_version = PHP_VERSION;
		$woocommerce_version = WC()->version;
		$merchant_id = $this->gateway->get_merchant_id();

		$extra_detail_1 = '';
		$extra_detail_2 = '';

		$matches = array();
		if (array_key_exists('SERVER_SOFTWARE', $_SERVER) && preg_match('/^[a-zA-Z0-9]+\/\d+(\.\d+)*/', $_SERVER['SERVER_SOFTWARE'], $matches)) {
			$s = $matches[0];
			$extra_detail_1 .= "; {$s}";
		}

		if (array_key_exists('REQUEST_SCHEME', $_SERVER) && array_key_exists('HTTP_HOST', $_SERVER)) {
			$s = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'];
			$extra_detail_2 .= " {$s}";
		}

		return "Gratify Pay Gateway for WooCommerce/{$plugin_version} (PHP/{$php_version}; WordPress/{$wp_version}; WooCommerce/{$woocommerce_version}; Merchant/{$merchant_id}{$extra_detail_1}){$extra_detail_2}";
	}

	/**
	 * @todo Build an Gratify Pay-compatible Money object for use in the API posts.
	 *
	 * @since	1.0.0
	 * @param	float	$amount
	 * @param	string	$currency
	 * @uses	get_option('woocommerce_currency')
	 * @return	array   (amount, currency) tuple
	 * @used-by	self::get_payment_types_for_amount()
	 * @used-by	self::create_refund()
	 */
	private function build_money_object($amount, $currency = '') {
		if (empty($currency)) {
			$currency = get_option('woocommerce_currency');
		}

		return array(
			'amount' => number_format((float)$amount, 2, '.', ''),
			'currency' => $currency
		);
	}

    /**
	 * Store an API error. If the last request to an API resulted in an error, store it in the options table
	 * so we can show it throughout the admin.
	 *
	 * @since	1.0.0
	 * @param	mixed	$error
	 * @return	bool
	 */
	private function store_api_error($error) {
		return update_option( 'woocommerce_gratify_pay_api_error', $error );
	}

    /**
	 * Clear the last API error. This should be called each time an API call executes successfully.
	 *
	 * @since	1.0.0
	 * @return	bool
	 */
	private function clear_api_error() {
		return delete_option( 'woocommerce_gratify_pay_api_error' );
	}

    	/**
	 * Retrieve the Content-Type, HTTP_CORRELATION_ID and CF-Ray headers from the HTTP response.
	 *
	 * @since	1.0.0
	 * @param	array	$response	The array returned from wp_remote_get or wp_remote_post.
	 * @uses	wp_remote_retrieve_headers
	 * @return	array				An array of header values with lowercased keys.
	 * @used-by	self::parse_response
	 */
	private function get_response_headers($response) {
		$headers_obj = wp_remote_retrieve_headers( $response );
		return $headers_obj->getAll();
	}



    	/**
	 * Parse the response from Gratify Pay API.
	 * Returns an object if the response is JSON, and if it can be parsed.
	 *
	 * @since	2.1.0
	 * @param	array	$response	The array returned from wp_remote_get or wp_remote_post.
	 * @uses	wp_remote_retrieve_response_code
	 * @uses	self::get_response_headers
	 * @uses	wp_remote_retrieve_body
	 * @uses	WC_Gratify_Pay_Gateway::log
	 * @return	StdClass|false		An object on success, or false on failure.
	 * @used-by	self::get_from_api
	 * @used-by	self::post_to_api
	 */
	private function parse_response($response) {
		$response_code = wp_remote_retrieve_response_code( $response );
		$headers_arr = $this->get_response_headers( $response );
		$body_str = wp_remote_retrieve_body( $response );

		if (array_key_exists('content-type', $headers_arr) && strpos(strtolower($headers_arr['content-type']), 'application/json') === 0) {
			$body_obj = json_decode($body_str);

			if (is_null($body_obj)) {
				WC_Gratify_Pay_Gateway::log("API {$response_code} RESPONSE - PARSING ERROR #" . json_last_error() . ': ' . json_last_error_msg());
			}
		} else {
			$body_obj = null;

			WC_Gratify_Pay_Gateway::log("API {$response_code} RESPONSE - UNEXPECTED FORMAT! content-type: " . (array_key_exists('content-type', $headers_arr) ? $headers_arr['content-type'] : 'null') . '; http_correlation_id: ' . (array_key_exists('http_correlation_id', $headers_arr) ? $headers_arr['http_correlation_id'] : 'null') . '; cf-ray: ' . (array_key_exists('cf-ray', $headers_arr) ? $headers_arr['cf-ray'] : 'null'));
		}

		if ($body_obj) {
			$return_obj = new \StdClass;
			$return_obj->body = $body_obj;

			return $return_obj;
		}

		return false;
	}

    /**
	 * GET from an API endpoint.
	 *
	 * @since	1.0.0
	 * @param	string	$url
	 * @uses	wp_remote_get()
	 * @uses	self::build_authorization_header()
	 * @uses	self::build_user_agent_header()
	 * @uses	self::parse_response
	 * @uses	WC_Gratify_Pay_Gateway::log()
	 * @return	StdClass|WP_Error|false
	 * @used-by	self::get_payment_types()
	 * @used-by	self::get_configuration()
	 */
	private function get_from_api($url) {
		WC_Gratify_Pay_Gateway::log("GET {$url}");

		$response = wp_remote_get( $url, array(
			'timeout' => 80,
			'headers' => array(
				'Authorization' => $this->build_authorization_header(),
				'User-Agent' => $this->build_user_agent_header(),
				'Accept' => 'application/json'
			)
		));

		if (!is_wp_error( $response )) {
			return $this->parse_response( $response );
		} else {
			# Unable to establish a secure connection with the Gratify Pay API endpoint.
			# Likely a TLS or network error.
			# Log the error details.
			foreach ($response->errors as $code => $messages_arr) {
				$messages_str = implode("\n", $messages_arr);
				WC_Gratify_Pay_Gateway::log("API NETWORK ERROR! Code: \"{$code}\"; Message(s):\n" . $messages_str);
			}

			# Return the WP_Error object.
			return $response;
		}

		return false;
	}

    /**
	 * POST JSON to an API endpoint and load the response.
	 *
	 * @since	1.0.0
	 * @param	string	$url	The full URL to the API Endpoint.
	 * @param	mixed	$data	The jsonifiable data to be posted to the API.
	 * @uses	wp_remote_post()
	 * @uses	self::build_authorization_header()
	 * @uses	self::build_user_agent_header()
	 * @uses	self::parse_response
	 * @uses	WC_Gratify_Pay_Gateway::log()
	 * @return	StdClass|false
	 * @used-by	self::get_payment_types_for_amount()
	 * @used-by	self::create_refund()
	 */
	private function post_to_api($url, $data) {
		WC_Gratify_Pay_Gateway::log("POST {$url}");

		$data_str = json_encode($data);

		$response = wp_remote_post( $url, array(
			'timeout' => 80,
			'headers' => array(
				'Authorization' => $this->build_authorization_header(),
				'User-Agent' => $this->build_user_agent_header(),
				'Content-Type' => 'application/json',
				'Content-Length' => strlen($data_str),
				'Accept' => 'application/json'
			),
			'body' => $data_str
		) );

		if (!is_wp_error( $response )) {
			return $this->parse_response( $response );
		} else {
			# Unable to establish a secure connection with the Gratify Pay API endpoint.
			# Likely a TLS or network error.
			# Log the error details.
			foreach ($response->errors as $code => $messages_arr) {
				$messages_str = implode("\n", $messages_arr);
				WC_Gratify_Pay_Gateway::log("API NETWORK ERROR! Code: \"{$code}\"; Message(s):\n" . $messages_str);
			}

			# Return the WP_Error object.
			return $response;
		}

		return false;
	}

    /**
	 * @todo Get the valid payment types for this merchant.
	 * @api --
	 * Note:	This is only for API v0.
	 *
	 * @since	1.0.0
	 * @uses	self::get_from_api()
	 * @uses	self::store_api_error()
	 * @uses	WC_Admin_Settings::add_error()
	 * @uses	self::clear_api_error()
	 * @uses	WC_Gratify_Pay_Gateway::log()
	 * @return	array|false					The list of available types, or false on error.
	 */
    public function get_payment_types() {
        return array('cc', 'direct');
      
	}

    /**
	 * @todo Get the configuration (valid payment types and limits) for this merchant.
	 *
	 * Note:	This is only for API v1.
	 *
	 * @since	2.0.0
	 * @uses	self::get_from_api()
	 * @uses	self::store_api_error()
	 * @uses	WC_Admin_Settings::add_error()
	 * @uses	self::clear_api_error()
	 * @uses	WC_Gratify_Pay_Gateway::log()
	 * @return	array|false					The list of available payment types, or false on error.
	 */
	public function get_configuration() {
        return array('cc' => array("min" => 10, "max" => "4000"), "direct" => array("min" => 10, "max" => "4000"));
    }

    /**
	 * Get the valid payment types available from Gratify Pay for this amount.
	 *
	 * Note:	This is only for API v0.
	 *
	 * @since	1.0.0
	 * @param	float	$order_total	Order Total Amount
	 * @uses	self::build_money_object()
	 * @uses	self::post_to_api()
	 * @return	array|false					The list of available types, or false on error.
	 */
	public function get_payment_types_for_amount($order_total) {
        return array('cc', 'direct');
	}

    public static function gen_random_token(){
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $token = '';
        for ($i = 0; $i < $length; $i++) {
            $token .= $characters[rand(0, $charactersLength - 1)];
        }
        return $token;
    }

    /**
	 * @todo Request an order token from GratifyPay for express checkout.
	 * Currently simulates it with a random string.
	 * @since	1.0.0
	 * @return	string|bool								Returns the token string, or false if no order token
	 *													could be generated.
	 */
	public function get_token_for_express_checkout() {
        return self::gen_random_token();
    }

    public function get_order_token_for_wc_order_in_v1($order = null) {
        return self::gen_random_token();
    }

    public function get_order_by_v1_token($token) {
        return false;
    }

 }
