<?php

/**
 * Stripe Payment Gateway
 *
 * Provides a Stripe Payment Gateway.
 *
 * @class  kkart_gateway_stripe_checkout
 * @package Kkart
 * @category Payment Gateways
 * @author Kkart
 */

if( ! defined( 'ABSPATH' ) ) {
	exit;
}

require_once dirname( __FILE__ ) . '/stripe-php/init.php';
use Stripe\Stripe;

class KKART_Gateway_Stripe extends KKART_Payment_Gateway {
	
	//log instance
	public static $log = false;
	public static $log_enabled = false;
	
	function __construct() {
		
		//NOTE:: Update payment method description according to supported payment methods
		$this->id = 'stripe';
		$this->method_title = esc_html__( 'Stripe', 'kkart' );
		$this->method_description = esc_html__( 'Stripe redirects you to the Stripe hosted secure payment page.', 'kkart' );
		$this->init_form_fields();
		$this->init_settings();
		
		// Define user set options 
		$this->title                = $this->get_option( 'title', esc_html__( 'Stripe', 'kkart' ) );
		$this->description          = $this->get_option( 'description', esc_html__( 'Pay via Stripe. Accept Credit Cards, Debit Cards, Alipay and more', 'kkart' ) );
		$this->enabled 				= $this->get_option( 'enabled' );
		$this->apiKey 				= $this->get_option( 'apiKey' );
		$this->payment_method		= $this->get_option( 'payment_method' );
		$this->business_name		= $this->get_option( 'business_name' );
		$this->webhook				= $this->get_option( 'webhook' );
		$this->webhook_secret		= $this->get_option( 'webhook_secret' );
		self::$log_enabled			= $this->get_option( 'log_enabled' );
		
		if( $this->webhook ) {
			add_action( 'kkart_api_kkart_gateway_'. $this->id, array( &$this, 'webhook_response' ) );
		}
		
		add_action( 'kkart_update_options_payment_gateways_' . $this->id, array( &$this, 'process_admin_options' ) );
		add_action( 'admin_notices', array( $this, 'admin_notices' ) );		
	}

	public function init_form_fields() {
		$this->form_fields = array();

		$this->form_fields['enabled'] = array(
			'title'   => esc_html__( 'Enable/Disable', 'kkart' ),
			'type'    => 'checkbox',
			'label'   => esc_html__( 'Enable Stripe Payment Gateway', 'kkart' ),
			'default' => 'no'
		);

		$this->form_fields['title'] = array(
			'title'       => esc_html__( 'Title', 'kkart' ),
			'type'        => 'text',
			'description' => esc_html__( 'This controls the title which the user sees during checkout.', 'kkart' ),
			'default'     => esc_html__( 'Stripe', 'kkart' ),
			'desc_tip'    => true
		);

		$this->form_fields['description'] = array(
			'title'       => esc_html__( 'Description', 'kkart' ),
			'type'        => 'textarea',
			'description' => esc_html__( 'This controls the description which the user sees during checkout.', 'kkart' ),
			'default'     => esc_html__( 'Pay via Stripe. Accept Credit Cards, Debit Cards, Alipay and more', 'kkart' )
		);
		
		$this->form_fields['business_name'] = array(
			'title'       => esc_html__( 'Business Name', 'kkart' ),
			'type'        => 'text',
			'description' => esc_html__( 'This Will Be Shown on Payment page.', 'kkart' ),
			'default'     => '',
		);
		
		$this->form_fields['apiKey'] = array(
			'title'       => esc_html__( 'API Secret Key', 'kkart' ),
			'type'        => 'password',
			'description' => __( 'Please enter the <strong>Secret key</strong> from <a href="https://dashboard.stripe.com/" target="_blank">Stripe Dashboard</a>', 'kkart' ),
			'default'     => ''
		);
		
		$this->form_fields['payment_method'] = array(
			'title'		  => esc_html__( 'Payment Method', 'kkart' ),
			'type'		  => 'multiselect',
			'class' => 'chosen_select',
			'css' => 'width: 350px;',
			'options' 	  => array(
				'card'  			=> 'Card',
				'alipay' 			=> 'AliPay',
				'ideal' 			=> 'iDEAL',
				'bacs_debit' 		=> 'Bacs Direct Debit',
				'bancontact' 		=> 'Bancontact',
				'giropay' 	 		=> 'Giropay',
				'p24'				=> 'Przelewy24',
				'eps' 				=> 'EPS',
				'sofort'			=> 'Sofort',
				'sepa_debit' 		=> 'SEPA Direct Debit',
				'grabpay' 			=> 'GrabPay',
				'afterpay_clearpay' => 'Afterpay / Clearpay',
				'acss_debit' 		=> 'ACSS Debit',
				'wechat_pay' 		=> 'WeChat Pay',
				'boleto'			=> 'Boleto',
				'oxxo' 				=> 'OXXO',
			),
			'default'	 => array( 'card' ),
			'description' => sprintf( __( 'Please make sure the selected gateways are enabled in ', 'kkart' ). '<strong><a href="https://dashboard.stripe.com/account/payments/settings" target="_blank">Stripe Payment Method Settings</a></strong>' )
		);
		
		$this->form_fields['log_enabled'] = array(
			'title'   => esc_html__( 'Logging', 'kkart' ),
			'type'    => 'checkbox',
			'label'   => esc_html__( 'Enable Logging', 'kkart' ),
			'description'   => sprintf( __( 'Log Stripe events, <strong>DON\'T ALWAYS ENABLE THIS.</strong> You can check this log in %s.', 'kkart' ), '<a target="_blank" href="' . esc_url( admin_url( 'admin.php?page=kkart-status&tab=logs&log_file=' . esc_attr( $this->id ) . '-' . sanitize_file_name( wp_hash( $this->id ) ) . '.log' ) ) . '">' . esc_html__( 'System Status &gt; Logs', 'kkart' ) . '</a>' ),
			'default' => 'no'
		);
		
		$this->form_fields['webhook_title'] = array(
			'title' =>	esc_html__( 'Webhook Options' ),
			'type' 	=>	'title',
			'class'=> 'kkart-css-class'
		); 
		
		$this->form_fields['webhook'] = array(
			'title'       => esc_html__( 'Enable Webhook', 'kkart' ),
			'type'        => 'checkbox',
			'description' => sprintf( __('WebHook URL <code>','kkart'). add_query_arg( 'kkart-api','KKART_Gateway_Stripe', home_url( '/' ) ).'</code><br/>Add Webhook here :- <a href="https://dashboard.stripe.com/webhooks" target="_blank">Stripe Webhook</a>' ),
			'default'     => 'no'
		);
		
		$this->form_fields['webhook_secret'] = array(
			'title'       => esc_html__( 'Webhook Secret Key', 'kkart' ),
			'type'        => 'password',
			'description' => esc_html__( 'Webhook secret key is used to authenticate webhooks', 'kkart' ),
			'default'     => ''
		);
	}
	
	public function admin_options() {
        kkart_enqueue_js("
                        jQuery( function( $ ) {
							$('.kkart-css-class').css({'border-top': 'dashed 1px #ccc','padding-top': '15px','width': '68%'}); 
						})"
						);
		parent::admin_options();				
	}
	
	
	public function process_payment( $order_id ) {
		$order = kkart_get_order( $order_id );
		
		\Stripe\Stripe::setApiKey( $this->apiKey );
		
		$current_user = wp_get_current_user();
		$current_user = (string) $current_user->user_email;
		
		$session_data = array(
			'customer_email' => $current_user,
			'payment_method_types' => $this->payment_method,
			'line_items' => [[
				'price_data' => [
					'currency' => get_kkart_currency(),
					'product_data' => [
					  'name' => $this->business_name ? $this->business_name : get_bloginfo('name'),
					],
					'unit_amount_decimal' => $this->get_stripe_amount( $order->get_total(), get_kkart_currency() ),
				],
				'quantity' => 1,
			]],
			'mode' => 'payment',
			'success_url' => esc_url_raw( add_query_arg( 'utm_nooverride', '1', $this->get_return_url( $order ) ) ),
			'cancel_url' => $order->get_cancel_order_url(),
		);
		
		//setting payment_method_options 
		foreach( $this->payment_method as $payment_method ) {
			if( $this->setPaymentMethodOptions( $payment_method ) ) {
				$session_data['payment_method_options'] = $this->setPaymentMethodOptions( $payment_method );
			}
		}
		
		//setting payment_intent_data
		if( in_array('bacs_debit', $this->payment_method ) ) {
			$session_data['payment_intent_data'] = [
				'setup_future_usage' => 'off_session',
			];
		}
		
		$session = \Stripe\Checkout\Session::create( $session_data );
		
		if( isset( $session['id'] ) && isset( $session['url'] ) ) {
			self::log( '============Payment Initiated ==========', 'debug' );
			self::log( $session );
			self::log( '========Session Data Ends Here=======', 'debug' );
			
			return array(
				'result'  => 'success',
				'redirect' => $session['url']
			);
		}	
		
		self::log( 'Payment Initiation Failed', 'error' );
		return array(
			'result' => 'failed',
			//'message' => $['message']
		);
	}
	
	//Processing webhook response
	public function webhook_response() {
		
		if ( ! isset( $_SERVER['REQUEST_METHOD'] )
			|| ( 'POST' !== $_SERVER['REQUEST_METHOD'] )
			|| ! isset( $_GET['kkart-api'] )
			|| ( 'KKART_Gateway_Stripe' !== $_GET['kkart-api'] )
		) {
			return;
		}
			
		$endpoint_secret = $this->webhook_secret;
		$payload = @file_get_contents('php://input');
		$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
		$event = null;

		try {
			$event = \Stripe\Webhook::constructEvent(
				$payload, $sig_header, $endpoint_secret
			);
		  
		} catch(\UnexpectedValueException $e) {
			// Invalid payload
			self::log( 'Webhook Source authentication Unexpected Value'. $e );
			http_response_code(400);
			exit();
		} catch(\Stripe\Exception\SignatureVerificationException $e) {
			// Invalid signature
			self::log( 'Webhook Source authentication failed Invalid Signature'. $e );
			http_response_code(400);
			exit();
		}
		
		$this->process_webhook( $event );
		return;
		
	}
	
	public function setPaymentMethodOptions( $payment_method ) {
		
		switch ( $payment_method ) {
			
			case 'wechat_pay': 
				return array(
					'wechat_pay' => array(
						'client' => "web"
					),
				);
				
		}
		return;
		
	}
	
	//Icon to be displayed on checkout page
	public function get_icon() {
		$icon_url = untrailingslashit( plugin_dir_url( __FILE__ ) ). '/assets/poweredbystripe.svg' ;
		$icons_str = sprintf( '<img src="%s" alt="Powered By Stripe" />', $icon_url );
		return apply_filters( 'kkart_gateway_icon', $icons_str, $this->id );
	}
	
	
	/**
	 * Get Stripe amount to pay
	 *
	 * @param float  $total Amount due.
	 * @param string $currency Accepted currency.
	 *
	 * @return float|int
	 */
	public static function get_stripe_amount( $total, $currency = '' ) {
		if ( ! $currency ) {
			$currency = get_kkart_currency();
		}

		if ( in_array( strtolower( $currency ), self::no_decimal_currencies() ) ) {
			return absint( $total );
		} else {
			return absint( kkart_format_decimal( ( (float) $total * 100 ), kkart_get_price_decimals() ) ); // In cents.
		}
	}
	
		/**
	 * List of currencies supported by Stripe that has no decimals
	 * https://stripe.com/docs/currencies#zero-decimal from https://stripe.com/docs/currencies#presentment-currencies
	 *
	 * @return array $currencies
	 */
	public static function no_decimal_currencies() {
		return ['bif', 'clp', 'djf', 'gnf',  'jpy', 'kmf', 'krw', 'mga', 'pyg', 'rwf', 'ugx', 'vnd', 'vuv', 'xaf', 'xof', 'xpf'];
	}
	
	
	//Processes the incoming webhook.
	public function process_webhook( $event ) {
		$checkout 	= $event->data->object;
		
		switch ( $event->type ) {
			case 'checkout.session.async_payment_failed':
			case 'checkout.session.expired':
				$this->process_webhook_checkout_failed( $checkout );
				break;
				
			case 'checkout.session.async_payment_succeeded':
				$this->process_webhook_checkout_succeeded( $checkout );
				break;
			
			case 'checkout.session.completed':
				$this->process_webhook_checkout_completed( $checkout );
			// ... handle other event types
			default:
				self::log( 'Received unknown event type \n' . $event->type, 'warning' );
			}		
	}
	
	//Parses order_id from success url and returns order using ID
	public function get_order_by_order_id( $checkout ) {
		
		//parses order_id from success url
		$parsed_url = parse_url( $checkout->success_url );
		parse_str( $parsed_url['query'], $param );
		
		$order_id 	= absint( $param['order_id'] );
		$order 		= kkart_get_order( $order_id );
		return $order;
	}
	
	/*
	* Checkout Succeeded when the payment has been made 
	* but yet to be confirmed from the payment gateway
	*/
	public function process_webhook_checkout_succeeded( $checkout ) {
		
		$order = $this->get_order_by_order_id( $checkout );
		
		// If order status is already in on-hold status don't continue.
		if ( $order->has_status( 'on-hold' ) ) {
			return;
		}

		$message = __( 'We are waiting for Payment confirmation from the gateway.', 'kkart' );
		self::log( $message );
		$order->update_status( 'on-hold', $message );
	}
	
	
	//Payment Failed
	public function process_webhook_checkout_failed( $checkout ) {
		
		$order = $this->get_order_by_order_id( $checkout );
		
		// If order status is already in failed status don't continue.
		if ( $order->has_status( 'failed' ) ) {
			return;
		}

		$message = __( 'This payment failed to clear.', 'kkart' );
		self::log( $message );
		$order->update_status( 'failed', $message );
	}
	
	//Payment completion confirmed by thr gateway
	public function  process_webhook_checkout_completed( $checkout ) {
		
		$order = $this->get_order_by_order_id( $checkout );
		
		// If order status is already in on-hold status don't continue.
		if ( $order->has_status( 'processing' ) ) {
			return;
		}
		
		// Store other data such as fees
		$order->set_transaction_id( $checkout->id );
		$order->payment_complete( $checkout->id );
		
		$order->update_status( 'processing', $message );
		self::log( 'Order payment Success Confirmed' );
		
	}
	
	
	public function process_refund( $order_id, $amount = null, $reason = '' ) {
		$order = kkart_get_order( $order_id );

		if ( ! $order or ! $order->get_transaction_id() ) {
			return new WP_Error('error', __('Refund failed: No transaction ID', 'kkart'));
		}
		
		$paymentId = $order->get_transaction_id();
		
		$stripe = new \Stripe\StripeClient( $this->apiKey );
		
		$refund_data = array(
		  'charge' => $paymentId,
		);
		
		//checks if amount is given else the default is full amount of the order
		if ( $amount ) {
			$refund_data['amount'] = $this->get_stripe_amount( $amount, get_kkart_currency() );
		}
		
		if ( !empty( $reason ) ) {
			$refund_data['reason'] = $reason;
		}
		
		try{
			$refund = $stripe->refunds->create( $refund_data );
			$order->add_order_note( __( 'Refund Id: ' . $refund->id, 'kkart' ) );
			
			return true;
		}
		catch(Exception $e)
		{
			self::log( 'Refund Failed' . $e->getMessage() );
			return new WP_Error('error', __($e->getMessage(), 'kkart'));
		}
	}
	
	public function admin_notices() {
		
		// If the gateway isn't enabled, don't show it.
		if ( "no" ===  $this->enabled ) {
			return;
		}
		
		$errors = $this->check_requirements();

		foreach( $errors as $error ) {
			if( ! $error ){
				continue;
			}
			
			$this->update_option( 'enabled', 'no' );
			
			$error_message = $this->get_error_message( $error );
			$this->admin_notice_html( $error_message );
		}
		
		if( !empty( $this->payment_method ) ) {
			array_walk( $this->payment_method, array( $this, 'supported_currency' ) );
		}
	}
	
	public function get_error_message( $key ) {
		switch ( $key ) {
			case 'kkart-gateway-stripe-error-missing-apiKey':
				return __( 'You forgot to fill your API Key.', 'kkart' );
			case 'kkart-gateway-stripe-error-missing-payment-method':
				return __( 'You forgot to fill your Payment Method.', 'kkart' );
			case 'kkart-gateway-stripe-error-missing-business-name':
				return __( 'You forgot to fill your Business Name.', 'kkart' );
			case 'kkart-gateway-stripe-error-missing-webhook-secret':
				return __( 'You forgot to fill your Webhook Secret.', 'kkart' );
		}
	}
	
	public function check_requirements() {

		$errors = [
			empty( $this->get_option( 'apiKey' ) )  ? 'kkart-gateway-stripe-error-missing-apiKey' : null,
			empty( $this->get_option( 'payment_method' ) ) ? 'kkart-gateway-stripe-error-missing-payment-method' : null,
			empty( $this->get_option( 'business_name' ) )  ? 'kkart-gateway-stripe-error-missing-business-name' : null,
			('yes' === $this->get_option( 'webhook' ) && empty( $this->get_option( 'webhook_secret' ) ) ) ?'kkart-gateway-stripe-error-missing-webhook-secret' : null,
		];

		return $errors;
	}
	
	// Checks for supported currencies by the payment method
	public function supported_currency( $gateway, $key ) {
		if( is_callable( array( $this, 'is_supported_currency_by_'. $gateway ) ) ){ 
			$is_supported = call_user_func( array( $this, 'is_supported_currency_by_'. $gateway ) );
			
			if( $is_supported ) {
				return;
			}
			$gateway_name = str_replace( '_', ' ', ucwords( $gateway, '_' ) );
			
			$this->update_payment_method( $key );
			$this->admin_notice_html( $gateway_name . ' does not support the store\'s currency that is ' . get_kkart_currency() );
			$this->update_option( 'enabled', 'no' );
		}
	}
	
	//Checks if the currency is supported by the gateway
	public function is_supported_currency( $supported_currencies ) {
		$store_currency = get_kkart_currency();
		
		if( in_array( $store_currency, $supported_currencies ) ) {
			return true;
		}
		return false;
	}
	
	public function is_supported_currency_by_giropay() {
		$supported_currencies = [ 'EUR' ];
		return $this->is_supported_currency( $supported_currencies );
		
	}
	
	public function is_supported_currency_by_ideal() {
		$supported_currencies = [ 'EUR' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_p24() {
		$supported_currencies = [ 'EUR', 'PLN' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_sofort() {
		$supported_currencies = [ 'EUR' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_eps() {
		$supported_currencies = [ 'EUR' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_bancontact() {
		$supported_currencies = [ 'EUR' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_grabpay() {
		$supported_currencies = [ 'SGD', 'MYR' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_wechat_pay() {
		$supported_currencies = [ 'CNY', 'AUD', 'CAD', 'EUR', 'GBP', 'HKD', 'JPY', 'SGD', 'USD', 'DKK', 'NOK', 'SEK', 'CHF' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_oxxo() {
		$supported_currencies = [ 'MXN' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_boleto() {
		$supported_currencies = [ 'BRL' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_sepa_debit() {
		$supported_currencies = [ 'EUR' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_bacs_debit() {
		$supported_currencies = [ 'GBP' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	public function is_supported_currency_by_afterpay_clearpay() {
		$supported_currencies = [ 'USD', 'CAD', 'GBP', 'AUD', 'NZD' ];
		return $this->is_supported_currency( $supported_currencies );
	}
	
	//Updates Payment Method in Stripe
	public function update_payment_method( $index ) {
		unset( $this->payment_method[$index] );
		$this->payment_method = array_values( $this->payment_method );
		$this->update_option( 'payment_method', $this->payment_method );
		return;
	}
	
	//Echos Admin Notice HTML
	public function admin_notice_html( $error ) {
		echo '<div class="notice notice-error is-dismissible"><p>'
				. __( 'To use Stripe 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;">'
				. $error
				. '</ul></p></div>';
	}
	
	public static function log( $message, $level = 'info' ) {
		if ( self::$log_enabled ) {
			if ( empty( self::$log ) ) {
				self::$log = kkart_get_logger();
			}
			self::$log->log( $level, $message, array( 'source' => 'stripe-checkout' ) );
		}
	}	
}	