<?php
/**
 * IKHOKHA Payment Gateway
 * @class  		KKART_Gateway_Ikhokha
 * @package 	Kkart
 * @version 	1.0.0
 * @category 	Payment Gateways
 * @author 		Kkart
 */
 
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

define( 'KKART_GATEWAY_IKHOKHA_URL', untrailingslashit( plugin_dir_url(__FILE__) ) );
define( 'KKART_IKHOKHA_API_ENDPOINT', 'https://api.ikhokha.com/ecomm/v1/' );

class KKART_Gateway_Ikhokha extends KKART_Payment_Gateway {
	
	public function __construct() {
		
		$this->version = KKART_VERSION;
		$this->id = 'ikhokha';
		$this->icon =  KKART_GATEWAY_IKHOKHA_URL . '/assets/kkart_ikhokha.png';
		$this->has_fields = false; // in case you need a custom credit card form
		$this->method_title = 'iKhokha (beta)'; // Default Title
		$this->method_description = __('Secure credit, debit card and Instant EFT payments with iKhokha.');
		$this->supports = array( 'products', 'refunds' );
		$this->init_form_fields();
		$this->init_settings();
		
		// Setup default merchant data.
		$this->title = $this->get_option('title');
		$this->description = $this->get_option('description');
		$this->enabled = $this->get_option('enabled');
		$this->testmode = 'yes' === $this->get_option('testmode');
		$this->application_id = $this->get_option('application_id');
		$this->application_secret = $this->get_option('application_secret');
		
		$this->site_name = get_bloginfo('name');
		$this->available_currencies = (array)apply_filters('kkart_gateway_ikhokha_available_currencies', array( 'ZAR' ) );
		
		// save admin options
		add_action('kkart_update_options_payment_gateways_' . $this->id, array($this, 'process_admin_options')); 
		add_action( 'admin_notices', array( $this, 'admin_notices' ) );	
		add_action( 'kkart_receipt_'.$this->id, array( $this, 'receipt_page' ), 10, 1 );
		add_action('kkart_api_' . strtolower(get_class($this)), array($this, 'ikhokha_process_response'));
	}
	
	/**
	 * Initialise Gateway Settings Form Fields
	 *
	 * @since 1.0.0
	 */
	public function init_form_fields() {

		$this->form_fields = array(
			'enabled' => array(
				'title' => __( 'Enable/Disable', 'kkart' ),
				'label' => __( 'Enable iKhokha Payment Gateway', 'kkart' ),
				'type' => 'checkbox',
				'description' => '',
				'default' => 'no',
			),
			'title' => array(
				'title' => __('Title', 'kkart'),
				'type' => 'text',
				'description' => __('This controls the title which the user sees during checkout.', 'kkart'),
				'default' => __('iKhokha', 'kkart'),
				'desc_tip' => true,
			),
			'description' => array(
				'title' => __('Description', 'kkart'),
				'type' => 'textarea',
				'description' => __('This controls the description which the user sees during checkout.', 'kakrt'),
				'default' => __('Secure credit, debit card and Instant EFT payments with iKhokha.', 'kakrt'),
			),
			'testmode' => array(
				'title' => __('Test mode', 'kkart'),
				'label' => __('Enable Test Mode (Use Card Number: 1111 1111 1111 1111 Expiry Month: 11 Expiry Year: 25 CVV: 111)', 'kkart'),
				'type' => 'checkbox',
				'description' => __('Place the payment gateway in test mode and use the displayed test card details to conduct a test transaction. Note: Your website users will NOT be able to transact if this setting is enabled.', 'kkart'),
				'default' => __('no', 'kkart'),
				'desc_tip' => true,
			),
			'application_id' => array(
				'title' => __('Application ID', 'kkart'),
				'type' => 'text',
			),
			'application_secret' => array(
				'title' => __('Application Secret', 'kkart'),
				'type' => 'password',
			),
		);
	}
	
	public function check_requirements() {
		$errors = [];
		
		// Add more error if needed
		if (!in_array(get_kkart_currency(), $this->available_currencies)) {
			$errors[] = 'kkart-gateway-ikhokha-error-invalid-currency';
		}
		
		if (empty($this->get_option('application_id'))) {
			$errors[] = 'kkart-gateway-ikhokha-error-missing-application_id';
		}
		
		if (empty($this->get_option('application_secret'))) {
			$errors[] = 'kkart-gateway-ikhokha-error-missing-application_secret';
		}
		
		return array_filter( $errors );
	}

	public function is_available() {
		
		if ( 'yes' === $this->enabled ) {
			$errors = $this->check_requirements();
			return 0 === count( $errors );
		}
		
		return parent::is_available();
	}

	public function admin_notices() {

		// Get requirement errors.
		$errors_to_show = $this->check_requirements();

		// If everything is in place, don't display it.
		if( ! count( $errors_to_show ) ){
			return;
		}

		// If the gateway isn't enabled, don't show it.
		if ( "no" ===  $this->enabled ) {
			return;
		}

		// Use transients to display the admin notice once after saving values.
		if ( ! get_transient( 'kkart-gateway-ikhokha-admin-notice-transient' ) ) {
			set_transient( 'kkart-gateway-ikhokha-admin-notice-transient', 1, 1);

			echo '<div class="notice notice-error is-dismissible"><p>'
				. __( 'To use ikhokha as a payment provider, you need to fix the problems below:', 'kkart' ) . '</p>'
				. '<ul style="list-style-type: disc; list-style-position: inside; padding-left: 2em;">'
				. array_reduce( $errors_to_show, function( $errors_list, $error_item ) {
					$errors_list = $errors_list . PHP_EOL . ( '<li>' . $this->get_error_message($error_item) . '</li>' );
					return $errors_list;
				}, '' )
				. '</ul></p></div>';
		}
		
	}

	public function get_error_message( $key ) {
		
		$error_msg = '';
		
		switch ( $key ) {
			case 'kkart-gateway-ikhokha-error-invalid-currency':
				$error_msg = __( 'Your store uses a currency that IKHOKHA doesnt support yet.', 'kkart' );
				break;
			case 'kkart-gateway-ikhokha-error-missing-application_id':
				$error_msg = __( 'You forgot to fill your Application ID.', 'kkart' ); 
				break;
			case 'kkart-gateway-ikhokha-error-missing-application_secret':
				$error_msg = __( 'You forgot to fill your Application secret.', 'kkart' );
				break;
		}

		return $error_msg;
		
	}

	public function process_payment($order_id) {
		global $kkart;
		$order = new KKART_Order($order_id);
		return array(
			'result' => 'success',
			'redirect' => $order->get_checkout_payment_url(true),
		);
	}
	
	public function receipt_page($order) {
		echo $this->generate_post_form($order);
	}

	public function generate_post_form($order_id) {
		
		$order = new KKART_Order($order_id);
		$payment_page = $order->get_checkout_payment_url();
		$cart_page_id = kkart_get_page_id('cart');
		$cart_page_url = $cart_page_id ? get_permalink($cart_page_id) : '';
		
		/* Define test or live mode */
		if ($this->get_option('testmode') == "yes") {
			$mode = true;
		} else {
			$mode = false;
		}
		
		$client_details = array(
			"platformName" => "Kkart",
			"platformVersion" => KKART_VERSION,
			"pluginVersion" => KKART_VERSION,
			"website" => get_site_url(),
		);

		/* Amount Validation */
		$getTotal = $order->get_total();
		$getDecimal = kkart_get_price_decimal_separator();
		$resetDecimal = str_replace($getDecimal, '.', $getTotal);
		$orderAmount = number_format($resetDecimal, 2, '.', '');

		/* Payload Info */
		$payload = array(
			"amount" => round($orderAmount * 100),
			"callbackUrl" => str_replace('http:', 'https:', add_query_arg(array('kkart-api' => 'KKART_Gateway_Ikhokha', 'reference' => $order_id), home_url('/'))),
			"successUrl" => $this->get_return_url($order),
			"failUrl" => $payment_page,
			"test" => $mode,
			"customerEmail" => $order->get_billing_email(),
			"customerPhone" => $order->get_billing_phone(),
			"customerName" => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
			"client" => $client_details,
		);
		
		$auth = $this->ikhokha_order_auth($payload);
		
		// replace once we have an actual auth api
		if (is_array($auth) && array_key_exists('paymentUrl', $auth)) {
			
			// save payment link to order
			$order->update_meta_data('kkart_ikhokha_payment_url', esc_url_raw($auth['paymentUrl']));
			$order->save();
			
			// Enqueue Script for loading Form & Auto submit
			kkart_enqueue_js('
					$.blockUI({
							message: "' . esc_js(__('Thank you for your order. We are now redirecting you to iKhokha to make payment.', 'kkart')) . '",
							baseZ: 99999,
							overlayCSS:
							{
								background: "#fff",
								opacity: 0.6
							},
							css: {
								padding:		"20px",
								zindex:		 "9999999",
								textAlign:	  "center",
								color:		  "#555",
								border:		 "3px solid #aaa",
								backgroundColor:"#fff",
								cursor:		 "wait",
								lineHeight:		"24px",
							}
						});
					window.location.href = "' . esc_attr($auth['paymentUrl']) . '";
				');
			$html = '';
		}else{

			// make ssl if needed
			if (get_option('kkart_force_ssl_checkout') == 'yes') {
				$payment_page = str_replace('http:', 'https:', $payment_page);
			}

			// display an HTML error briefly before redirecting to payment page
			$html = '<div class="ikhokha-payment-notice" style="border:2px solid #ff0000;padding:10px;font-weight: 700;color: #ff0000;">Invalid connection to iKhokha - <a href="' . $payment_page . '">Try again.</a></div>';

			wp_redirect($payment_page);
		}

		return $html;
	}

	public function generate_signature($payload) {

		$signature = false;

		if(isset($payload) && !empty($payload) && $this->application_secret !== ''){
			$string = "/ecomm/v1/paymentlinks" . addslashes(json_encode($payload, JSON_UNESCAPED_SLASHES));
			$signature = hash_hmac("sha256", $string, $this->application_secret);
		}
		
		return $signature;
	}

	public function ikhokha_order_auth($payload) {
		$signature = $this->generate_signature($payload);
		
		if(!$signature){
			return false;
		}
		
		$args = [
			'method' => 'POST',
			'sslverify' => true,
			'headers' => [
				'Cache-Control' => 'no-cache',
				'Content-Type' => 'application/json',
				'IK-APPID' => $this->application_id,
				'IK-SIGN' => $signature,
			],
			'body' => json_encode($payload),
		];
		
		$uri = KKART_IKHOKHA_API_ENDPOINT . 'paymentlinks';
		$request = wp_remote_post($uri, $args);
		$response = wp_remote_retrieve_body($request);
		$response = json_decode($response, true);
	   
		if (is_wp_error($response)) {
			$error_message = $response->get_error_message();
			return $error_message;
		} else {
			return $response;
		}
	}

	public function generate_callback_signature($payload) {
		$signature = false;
		
		if (isset($payload) && !empty($payload) && $this->application_secret !== '') {
			$string = "/" . addslashes(json_encode($payload, JSON_UNESCAPED_SLASHES));
			$signature = hash_hmac("sha256", $string, $this->application_secret);
		}
		
		return $signature;
	}

	public function ikhokha_process_response(){
		global $kkart;
		
		$check = false;
		
		// Get data via post
		if (isset($_POST['status']) && !empty($_POST['status'])) {
			
			$data = array('status' => sanitize_text_field($_POST['status']));
			
			// extra data
			if (isset($_POST['transactionId']) && !empty($_POST['transactionId'])) {
				$data['transactionId'] = sanitize_key($_POST['transactionId']);
			}
			if (isset($_POST['responseCode']) && !empty($_POST['responseCode'])) {
				$data['responseCode'] = sanitize_text_field($_POST['responseCode']);
			}
			if (isset($_POST['responseMessage']) && !empty($_POST['responseMessage'])) {
				$data['responseMessage'] = sanitize_text_field($_POST['responseMessage']);
			}
			
		} else {
			$data = json_decode(file_get_contents("php://input"), true);
		}
		
		// if we getting endpoint data
		if (isset($data) && isset($_GET['reference']) && isset($data['status'])) {
			
			// get headers
			$headers = getallheaders();
			$keys = array();
			foreach ($headers as $headerKey => $headerValue) {
				$keys[strtoupper($headerKey)] = $headerValue;
			}
			
			// generate signature
			$signature = $this->generate_callback_signature($data);

			// if the generated signature does not match the signature received in header return 500 error
			if ($signature !== $keys['IK-SIGN']) {
				status_header(500, "HTTP/1.1 500 Internal Server Error");
				die();
			}

			if($this->application_id !== $keys['IK-APPID']) {
				status_header(500, "HTTP/1.1 500 Internal Server Error");
				die();
			}

			// set reference and ensure it returns as a number
			$reference = sanitize_key($_GET['reference']);
			
			if (!is_numeric($reference)) {
				$reference = +$reference;
			}
			
			$order = new KKART_Order($reference);
			$orderStatus = $order->get_status();
			$transactionStatus = strtolower($data['status']);
			
			// if we waiting for a payment
			// if order status is success from api endpoint we mark the order as processing in WC
			if ($orderStatus != 'processing' && $transactionStatus == 'success') {
				
				status_header(200);
				
				$order->update_status('processing', sprintf(__('%s %s was successfully processed through iKhokha', 'kkart'), get_kkart_currency(), $order->get_total()));
				$order->payment_complete();
				kkart_reduce_stock_levels($order->get_id());
				
				$order->update_meta_data('kkart_ikhokha_data', $data);
				$order->save();
				
				KKART()->cart->empty_cart();
				
				$check = true;
				
			} else if ($orderStatus != 'processing' && $transactionStatus == 'failed') {
				status_header(200);
				$order->update_status('failed', sprintf(__('%s %s failed to connect through iKhokha', 'kkart'), get_kkart_currency(), $order->get_total()));
			}
		} else {
			// if no api data
			status_header(500, "HTTP/1.1 500 Internal Server Error");
			die();
		}
		
		if (!$check) {
			status_header(500, "HTTP/1.1 500 Internal Server Error");
			die();
		}
		
		$return = array(
			'status' => $check,
		);
		
		print_r($return);
		die();
	}

	public function generate_refund_signature($refund_payload, $order_id){
		
		$refund_signature = false; // default to false
		
		if(isset($refund_payload) && !empty($refund_payload) && $this->application_secret !== ''){
			$transaction_id = null;
			$getData = get_post_meta($order_id, 'kkart_ikhokha_data', true) ? get_post_meta($order_id, 'kkart_ikhokha_data', true) : '';
			
			if(isset($getData['transactionId']) && !empty($getData['transactionId'])){
				$transaction_id = $getData['transactionId'];
			}
			
			if(empty($transaction_id)){
				$payment_url = get_post_meta($order_id, 'kkart_ikhokha_payment_url', true);
				$arr_payment = explode("/", $payment_url);
				$arr_payment_reversed = array_reverse($arr_payment);
				$transaction_id = $arr_payment_reversed[0];
			}
			
			$string = "/ecomm" . "/" . $transaction_id . "/refunds" . addslashes(json_encode($refund_payload, JSON_UNESCAPED_SLASHES));
			$refund_signature = hash_hmac("sha256", $string, $this->application_secret);
		}
		
		return $refund_signature;
	}

	public function process_refund($order_id, $amount = null, $reason = ''){
		
		$convert = round($amount * 100);
		
		//Payload Info
		$refund_payload = array(
			"amount" => $convert,
			"reason" => $reason,
		);
		
		$refund_signature = $this->generate_refund_signature($refund_payload, $order_id);
		
		if(!$refund_signature){
			return false;
		}
			
		$transaction_id = null;
		$getData = get_post_meta($order_id, 'kkart_ikhokha_data', true) ? get_post_meta($order_id, 'kkart_ikhokha_data', true) : '';
		
		if(isset($getData['transactionId']) && !empty($getData['transactionId'])){
			$transaction_id = $getData['transactionId'];
		}
		
		if(empty($transaction_id)){
			$payment_url = get_post_meta($order_id, 'kkart_ikhokha_payment_url', true);
			$arr_payment = explode("/", $payment_url);
			$arr_payment_reversed = array_reverse($arr_payment);
			$transaction_id = $arr_payment_reversed[0];
		}
		
		$refund_url_full =  "https://api.ikhokha.com/ecomm/$transaction_id/refunds";
		$args = [
			'method' => 'POST',
			'sslverify' => true,
			'timeout' => 30,
			'headers' => [
				'Cache-Control' => 'no-cache',
				'Content-Type' => 'application/json',
				'IK-APPID' => $this->application_id,
				'IK-SIGN' => $refund_signature,
			],
			'body' => json_encode($refund_payload),
		];
		
		$request = wp_remote_post($refund_url_full, $args);
		$response = wp_remote_retrieve_body($request);
		$response = json_decode($response, true);
		
		if(is_wp_error($response)){
			$error_message = $response->get_error_message();
			return false;
		}else{
			if (isset($response['status']) && $response['status'] == "SUCCESS") {
				$order = new KKART_Order($order_id);
				$order->add_order_note(sprintf(__('%s %s was successfully refunded through iKhokha', 'kkart'), get_kkart_currency(), $amount));
				return true;
			} else if (isset($response['status']) && $response['status'] == "FAILURE") {
				return new WP_Error('kkart_' . $order_id . '_refund_failed', $response['responseMessage']);
			} else {
				return new WP_Error('kkart_' . $order_id . '_refund_failed', 'Unable to process a refund.');
			}
		}
	}
}
