<?php
/**
 * The Push notifications class
 * Using Minishlink PHP library which requires PHP 7.2+ and the following extensions enabled: gmp, mbstring, curl, openssl
 *
 * @since      9.10
 * @package    CartBounty Pro - Save and recover abandoned carts for WooCommerce
 * @subpackage CartBounty Pro - Save and recover abandoned carts for WooCommerce/includes
 * @author     Streamline.lv
 */
class CartBounty_Pro_Push_Notification{
	
	/**
	 * The admin handler that manages the plugin's settings, options, and backend functionality.
	 *
	 * @since    10.9
	 * @access   protected
	 * @var      CartBounty_Pro_Admin    $admin    Provides methods to control and extend the plugin's admin area.
	 */
	protected $admin = null;

	/**
	 * The public handler that manages the plugin's settings, options, and frontend functionality.
	 *
	 * @since    10.9
	 * @access   protected
	 * @var      CartBounty_Pro_Public    $public    Provides methods to control and extend the plugin's public area.
	 */
	protected $public = null;

	/**
	 * The automation handler that manages the plugin's abandoned cart reminder automation functionality.
	 *
	 * @since    10.9
	 * @access   protected
	 * @var      CartBounty_Pro_Automation    $automation    Provides methods to control and extend the plugin's automations.
	 */
	protected $automation = null;

	/**
	 * The coupons manager responsible for handling coupons.
	 *
	 * @since    10.9
	 * @access   protected
	 * @var      CartBounty_Pro_Coupons    $coupons    Provides methods to create, apply, and manage discount coupons.
	 */
	protected $coupons = null;

	/**
	 * Get the admin handler (lazy-loaded).
	 * Creates the connector on first use and then reuses the same instance.
	 *
	 * @since 10.9
	 * @access protected
	 * @return CartBounty_Pro_Admin
	 */
	protected function admin(){
		
		if( $this->admin === null ){
			$this->admin = new CartBounty_Pro_Admin( CARTBOUNTY_PRO_PLUGIN_NAME_SLUG, CARTBOUNTY_PRO_VERSION_NUMBER );
		}

		return $this->admin;
	}

	/**
	 * Get the public handler (lazy-loaded).
	 * Creates the connector on first use and then reuses the same instance.
	 *
	 * @since 10.9
	 * @access protected
	 * @return CartBounty_Pro_Public
	 */
	protected function public(){
		
		if( $this->public === null ){
			$this->public = new CartBounty_Pro_Public( CARTBOUNTY_PRO_PLUGIN_NAME_SLUG, CARTBOUNTY_PRO_VERSION_NUMBER );
		}

		return $this->public;
	}

	/**
	 * Get the automation handler (lazy-loaded).
	 * Creates the connector on first use and then reuses the same instance.
	 *
	 * @since 10.9
	 * @access protected
	 * @return CartBounty_Pro_Automation
	 */
	protected function automation(){
		
		if( $this->automation === null ){
			$this->automation = new CartBounty_Pro_Automation();
		}

		return $this->automation;
	}

	/**
	 * Get the coupon handler (lazy-loaded).
	 * Creates the connector on first use and then reuses the same instance.
	 *
	 * @since 10.9
	 * @access protected
	 * @return CartBounty_Pro_Coupons
	 */
	protected function coupons(){
		
		if( $this->coupons === null ){
			$this->coupons = new CartBounty_Pro_Coupons();
		}

		return $this->coupons;
	}

	/**
	 * Starting Push notification automation process
	 *
	 * @since    9.10
	 */
	public function auto_send(){
		$admin = $this->admin();
		$automation = $this->automation();

		if( !$admin->check_license() || !class_exists('WooCommerce') ) return;
			
		if( $automation->automation_enabled( 'push_notification' ) ){
			$this->recover_carts();
		}
	}

	/**
	 * Starting abandoned cart recovery
	 * Each batch does not send out more than 6 abandoned cart reminder messages (2 for each step)
	 * Before sending out the message we validate that the user has provided phone consent, has not unsubscribed, has a valid phone number, is in the allowed country and is out of quiet hours
	 *
	 * @since    9.10
	 */
	private function recover_carts() {
		global $wpdb;
		$admin = $this->admin();
		$automation = $this->automation();
		$cart_table = $wpdb->prefix . CARTBOUNTY_PRO_TABLE_NAME;
		$time = $admin->get_time_intervals();

		//Retrieving all abandoned carts that are eligible for push notifications recovery
		//Excluding finished automations, unsubscribed carts and excluded carts (ones that have been manually removed from automation)
		$carts = $wpdb->get_results(
			$wpdb->prepare(
				"SELECT id, email, phone, name, surname, session_id, location, cart_contents, cart_total, currency, time, language, pn_subscription, pn_steps_completed, pn_last_sent
				FROM {$cart_table}
				WHERE (type = %d OR type = %d) AND
				pn_subscription != '' AND
				cart_contents != '' AND
				paused != 1 AND
				pn_excluded != 1 AND
				pn_complete != 1 AND
				anonymized != 1 AND
				time < %s AND
				time > %s",
				$admin->get_cart_type('abandoned'),
				$admin->get_cart_type('recovered_pending'),
				$time['cart_abandoned'],
				$time['maximum_sync_period']
			)
		);

		$active_steps = $automation->get_active_steps( 'push_notification' );
		$automation_steps = get_option( 'cartbounty_pro_push_notification_steps' );

		foreach( $active_steps as $key => $step ){ //Looping through active steps
			$automation_step = $automation_steps[$step];
			foreach( $carts as $cart_key => $cart ){
				
				if( $cart->pn_steps_completed == $key ){ //If current step must be complete
					$first_step = false;
					$time = $cart->pn_last_sent;
					
					if( $cart->pn_steps_completed == 0 ){ //If reminder about current cart has never been sent - use cart capture time instead of last step automation sent time
						$time = $cart->time;
						$first_step = true;
					}

					if( isset( $automation_step['interval'] ) ){
						$interval = $automation_step['interval'];

					}else{ //If custom interval is not set, fallback to default interval
						$interval = $this->get_defaults( 'interval', $step );
					}
					
					$step_wait_time = $admin->get_time_intervals( $interval, $first_step ); //Get time interval for the current step

					if( $time < $step_wait_time['pn_step_send_period'] ){ //Check if time has passed for current step

						if( !$admin->has_excluded_items( $cart, 'push_notification' ) ){ //If cart contents do not have excluded items
							$this->send_push_notification( $cart, $key ); //Time has passed - must prepare and send out reminder
						
						}else{
							$admin->exclude_cart( 'push_notification', $cart ); //Exclude cart from further recovery
						}

						unset( $carts[$cart_key] ); //Remove array element so the next step loop runs faster
					}
				}
			}
		}
	}

	/**
     * Returning Push notification automation defaults
     *
     * @since    9.10
     * @return   array or string
     * @param    string     $value    		  	  Value to return
     * @param    integer    $automation    		  Automation number
     */
	public function get_defaults( $value = false, $automation = false ){
		$defaults = array();
		switch ( $automation ) {
			case 0:

				$defaults = array(
					'name'			=> esc_html__( 'First message', 'woo-save-abandoned-carts' ),
					'heading'		=> esc_attr__( 'Forgot to complete your purchase? 🧸', 'woo-save-abandoned-carts' ),
					'heading_name'	=> 'Push notifications: First message heading',
					'content'		=> esc_attr__( 'Your cart contains some fantastic items! Would you like to complete your purchase?', 'woo-save-abandoned-carts' ),
					'content_name'	=> 'Push notifications: First message content',
					'interval'		=> 300000
				);

				break;

			case 1:

				$defaults = array(
					'name'			=> esc_html__( 'Second message', 'woo-save-abandoned-carts' ),
					'heading'		=> esc_attr__( 'Are you still on the fence?', 'woo-save-abandoned-carts' ),
					'heading_name'	=> 'Push notifications: Second message heading',
					'content'		=> esc_attr__( 'We are keeping items in your cart reserved for you, but do not wait too long or they will expire 🫠.', 'woo-save-abandoned-carts' ),
					'content_name'	=> 'Push notifications: Second message content',
					'interval'		=> 21600000
				);

				break;

			case 2:

				$defaults = array(
					'name'			=> esc_html__( 'Third message', 'woo-save-abandoned-carts' ),
					'heading'		=> esc_attr__( 'Your cart is still waiting ⏳', 'woo-save-abandoned-carts' ),
					'heading_name'	=> 'Push notifications: Third message heading',
					'content'		=> esc_attr__( 'Claim your cart while it is still here!', 'woo-save-abandoned-carts' ),
					'content_name'	=> 'Push notifications: Third message content',
					'interval'		=> 86400000
				);

				break;

			case 3:

				$defaults = array(
					'name'			=> esc_html__( 'Fourth message', 'woo-save-abandoned-carts' ),
					'heading'		=> esc_attr__( 'Your cart is about to expire! 🎈', 'woo-save-abandoned-carts' ),
					'heading_name'	=> 'Push notifications: Fourth message heading',
					'content'		=> esc_attr__( 'Do not miss out on the products in your shopping cart - take them with you before they expire.', 'woo-save-abandoned-carts' ),
					'content_name'	=> 'Push notifications: Fourth message content',
					'interval'		=> 259200000
				);

				break;
		}

		if( $value ){ //If a single value should be returned
			
			if( isset( $defaults[$value] ) ){ //Checking if value exists
				$defaults = $defaults[$value];
			}
		}

		return $defaults;
	}

	/**
	* Return push notification settings
	*
	* @since    9.10
	* @return   array
	* @param    string     $value                Value to return
	*/
	public function get_settings( $value = false ){
		$saved_options = get_option( 'cartbounty_pro_push_notification_settings' );
		$defaults = array( //Setting defaults
			'permission_heading' 	=> '',
			'permission_content'	=> '',
			'permission_style' 		=> 1,
			'main_color' 			=> '',
			'inverse_color' 		=> '',
			'permission_image' 		=> '',
			'test_mode' 			=> false
		);

		if( is_array( $saved_options ) ){
			$settings = array_merge( $defaults, $saved_options ); //Merging default settings with saved options
			
		}else{
			$settings = $defaults;
		}

		if( $value ){ //If a single value should be returned
			
			if( isset( $settings[$value] ) ){ //Checking if value exists
				$settings = $settings[$value];
			}
		}

		return $settings;
	}

	/**
	 * Check if Notification permission soft ask enabled or not
	 *
	 * @since    9.10
	 * @return   boolean
	 */
	function soft_ask_enabled(){
		return apply_filters( 'cartbounty_pro_push_notification_soft_ask_enabled', true );
	}

	/**
	 * Outputting Push notification permission request form
	 *
	 * @since    9.10
	 */
	function display_push_notification_permission_request(){
		$public = $this->public();
		$automation = $this->automation();

		if( !$this->soft_ask_enabled() ) return; //If soft ask disabled, exit

		if( !class_exists( 'WooCommerce' ) ) return;

		if( !$automation->automation_enabled( 'push_notification' ) || !WC()->cart ) return; //If Push notification permission request disabled or WooCommerce cart does not exist

		if( !$this->display_permission_request() ) return; //Stop in case test mode enabled and user not Admin

		$output = $this->build_push_notification_permission_request_output(); //Creating Push notification permission request output
		echo "<script>localStorage.setItem( 'cartbounty_pro_product_count', " . $public->get_cart_product_count() . " )</script>";
		echo $output;
	}

	/**
	 * Building the Push notification request output
	 *
	 * @since    9.10
	 * @return   string
	 */
	function build_push_notification_permission_request_output(){
		$admin = $this->admin();
		$public = $this->public();
		$permission_request_heading = $admin->get_notification_permission_request_heading();
		$heading = apply_filters( 'wpml_translate_single_string', $permission_request_heading['value'], 'woo-save-abandoned-carts', $permission_request_heading['name'] );
		$permission_request_content = $admin->get_notification_permission_request_content();
		$content = apply_filters( 'wpml_translate_single_string', $permission_request_content['value'], 'woo-save-abandoned-carts', $permission_request_content['name'] );
		$main_color = $this->get_settings( 'main_color' );
		$inverse_color = $this->get_settings( 'inverse_color' );
		$image_id = $this->get_settings( 'permission_image' );
		$image_url = '';

		if( !$main_color ){
			$main_color = '#e3e3e3';
		}

		if( !$inverse_color ){
			$inverse_color = $public->invert_color( $main_color );
		}
		
		if( $image_id ){
			$image = wp_get_attachment_image_src( $image_id, 'full' );
			
			if( is_array( $image ) ){
				$image_url = $image[0];
			}
		}

		$args = apply_filters(
			'cartbounty_pro_push_notification_permission_args',
			array(
				'heading' 			=> $heading,
				'content' 			=> $content,
				'image_url' 		=> $image_url,
				'type' 				=> $this->get_settings( 'permission_style' ),
				'main_color' 		=> $main_color,
				'inverse_color' 	=> $inverse_color
			)
		);

		return $public->get_template( 'cartbounty-pro-push-notification-request.php', $args );
	}

	/**
	 * Saving endpoint data inside abandoned cart
	 * Returning false if saving unsuccessful
	 *
	 * @since    9.10
	 * @return   boolean
	 */
	function save_push_notification_subscription(){

		if ( check_ajax_referer( 'update_notification_subscription', 'nonce', false ) == false ) { //If the request does not include our nonce security check, stop executing the function
			wp_send_json_error( esc_html__( 'Subscription failed. Looks like you are not allowed to do this.', 'woo-save-abandoned-carts' ) );
		}

		$admin = $this->admin();
		$public = $this->public();

		if( !$admin->check_license() ) return wp_send_json_error(); //Exit if license key not valid 

		if( !WC()->cart ) return wp_send_json_error(); //Exit if WooCommerce cart has not been initialized

		$cart = $public->read_cart();
		$cart_saved = $public->cart_saved( $cart );

		if( !$cart_saved ) return wp_send_json_error();

		global $wpdb;
		$cart_table = $wpdb->prefix . CARTBOUNTY_PRO_TABLE_NAME;
		$pn_subscription_data = array();

		if( isset( $_POST['endpoint'] ) && isset( $_POST['public_key'] ) && isset( $_POST['private_key'] ) ){
			$pn_subscription_data['endpoint'] = $_POST['endpoint'];
			$pn_subscription_data['public_key'] = $_POST['public_key'];
			$pn_subscription_data['private_key'] = $_POST['private_key'];
		}

		$result = $wpdb->query(
			$wpdb->prepare(
				"UPDATE {$cart_table}
				SET pn_subscription = %s,
				language = %s
				WHERE session_id = %s",
				maybe_serialize( $pn_subscription_data ),
				get_locale(),
				$cart['session_id']
			)
		);

		$public->increase_recoverable_cart_count();

		return wp_send_json_success( $result );
	}

	/**
	 * Removing endpoint data from abandoned cart
	 * Returning false if removal unsuccessful
	 *
	 * @since    9.10
	 * @return   boolean
	 */
	function remove_push_notification_subscription(){

		if ( check_ajax_referer( 'update_notification_subscription', 'nonce', false ) == false ) { //If the request does not include our nonce security check, stop executing the function
			wp_send_json_error( esc_html__( 'Subscription removal failed. Looks like you are not allowed to do this.', 'woo-save-abandoned-carts' ) );
		}

		if( !WC()->cart ) return wp_send_json_error(); //Exit if WooCommerce cart has not been initialized

		$public = $this->public();
		$cart = $public->read_cart();
		$cart_saved = $public->cart_saved( $cart );

		if( !$cart_saved ) return wp_send_json_error();

		global $wpdb;
		$cart_table = $wpdb->prefix . CARTBOUNTY_PRO_TABLE_NAME;

		$result = $wpdb->query(
			$wpdb->prepare(
				"UPDATE {$cart_table}
				SET pn_subscription = %s
				WHERE session_id = %s",
				'',
				$cart['session_id']
			)
		);

		$public->decrease_recoverable_cart_count();

		return wp_send_json_success( $result );
	}

	/**
	 * Sending push notification
	 * Send out email reminder according to selecte template, subject, contents
	 * Create a new row in emails table
	 * Update abandoned cart last sent time, completed steps and if the automation is completed or not
	 *
	 * @since    9.10
	 * @param    object     $cart    		  	  Cart data
	 * @param    integer    $step_nr              Automation step number
	 * @param    boolean    $test                 Whether this is a test notification or not
	 * @param    string     $subscription         Push notification subscription that is used for sending a test notification
	 * @param    array    	$preview_data         Automation step input data passed from frontend to allow template preview
	 */
	function send_push_notification( $cart, $step_nr, $test = false, $subscription = false, $preview_data = array() ){
		$admin = $this->admin();
		$automation = $this->automation();
		$keys = $this->get_vapid_keys();
		$send_failed = false;
		$error_reason = '';
		$current_time = current_time( 'mysql', true );
		$subscription = maybe_unserialize( $cart->pn_subscription );
		$payload = json_encode( $this->get_notification_data( $cart, $step_nr, $test, $preview_data ) );

		$notification = array(
			'subscription' 	=> Minishlink\WebPush\Subscription::create(
				array(
					"endpoint" 	=> $subscription['endpoint'],
					"keys" 			=> array(
						'p256dh' 	=> $subscription['public_key'],
						'auth' 		=> $subscription['private_key']
					)
				)
			),
			'payload' 		=> $payload
		);

		$sender = 'WordPress@' . preg_replace( '#^www.#', '', $admin->get_current_domain_name() );

		$authentication = array(
			'VAPID' => array(
				'subject' => apply_filters( 'cartbounty_pro_push_notification_vapid_subject', 'mailto:' . esc_html( $sender ) ),
				'publicKey' => $keys['publicKey'],
				'privateKey' => $keys['privateKey'],
			),
		);

		$webPush = new Minishlink\WebPush\WebPush( $authentication );

		$report = $webPush->sendOneNotification(
			$notification['subscription'],
			$notification['payload'] // optional (defaults null)
		);

		$endpoint = $report->getEndpoint();

		if( !$report->isSuccess() ) { //If notification delivery was unsuccessful 
			$send_failed = true;
			$error_reason = $report->getReason();
			$admin->log( 'notice', sprintf( 'Push notification: Unable to send reminder notification to cart ID %d. Reason: %s', esc_html( $cart->id ), esc_html( $error_reason ) ) );
		}

		if( !$test ){ //If this is not a test notification
			$automation->update_cart( $cart, $current_time, 'push_notification' ); //Update cart information
			$this->add_notification( $cart->id, $step_nr, $current_time, $send_failed, $error_reason ); //Create a new row in the emails table
		}

		return;
	}

	/**
	* Send a test notification.
	*
	* @since    9.10
	* @return   HTML
	*/
	public function send_test_push_notification(){
		
		if ( check_ajax_referer( 'test_push_notification', 'nonce', false ) == false ) { //If the request does not include our nonce security check, stop executing function
			wp_send_json_error( esc_html__( 'Looks like you are not allowed to do this.', 'woo-save-abandoned-carts' ) );
		}

		$step_nr = false;
		$preview_data = array();
		$direction = is_rtl() ? 'rtl' : 'ltr';

		if( isset( $_POST['step'] ) ) $step_nr = $_POST['step'];

		if( isset( $_POST ) ) $preview_data = $this->get_preview_data( $_POST );

		$payload = $this->get_notification_data( $cart = false, $step_nr, $test = true, $preview_data );

		wp_send_json_success( 
			array(
				'message' 	=> '<span class="license-status license-active cartbounty-pro-response cartbounty-pro-no-border fadeOutSlow" dir="'. $direction .'"><i class="license-status-icon"><img src="'. esc_url( plugin_dir_url( __DIR__ ) ) . 'admin/assets/active-icon.svg" /></i>'. esc_html__( 'Notification successfully sent', 'woo-save-abandoned-carts' ) .'</span>',
				'options' 	=> $payload
			)
		);
	}

	/**
	 * Return notification data
	 *
	 * @since    9.10
	 * @return   array
	 * @param    object     $cart    		  	  Cart data
	 * @param    integer    $step_nr              Automation step number
	 * @param    boolean    $test                 Whether this is a test notification or not
	 * @param    array    	$preview_data         Automation step input data passed from frontend to allow template preview
	 */
	function get_notification_data( $cart, $step_nr, $test = false, $preview_data = array() ){
		$admin = $this->admin();
		$public = $this->public();
		$automation = $this->automation();
		$recovery_link = $admin->get_landing_url();

		if( $test ){
			$language = get_locale();

		}else{
			$language = $cart->language;
		}

		$admin->set_message_language( $language );
		$automation_steps = get_option( 'cartbounty_pro_push_notification_steps' );
		$step = $automation_steps[$step_nr];
		$source_language = apply_filters( 'cartbounty_pro_gtranslate_source_language', 'en' );
		$target_language = '';

		if( $test ){
			$step = $preview_data;
			$target_language = apply_filters( 'cartbounty_pro_gtranslate_target_language_preview', 'es' );
		}

		$heading_field = $automation->get_content_input_fields( $step_nr, $step, 'heading', 'push_notification' );
		$heading = apply_filters( 'wpml_translate_single_string', $heading_field['value'], 'woo-save-abandoned-carts', $heading_field['name'] );
		$heading = html_entity_decode( $heading ); //If heading field includes encoded emojis - must decode them
		$contents = $this->get_reminder_contents( $cart, $step_nr, $test, $preview_data );
		$contents = html_entity_decode( $contents ); //If content field includes encoded emojis - must decode them
		
		if( $cart ){ //If we have a cart
			$recovery_link = apply_filters( 'cartbounty_pro_automation_recovery_url', $admin->create_cart_url( $cart->email, $cart->session_id, $cart->id, 'push_notification' ), $step_nr, 'push_notification' ); //Recovery link
		}

		$include_icon = false;
		$include_image = false;
		$icon = '';
		$main_image = '';
		
		if( isset( $step['include_icon'] ) ){

			if( !empty( $step['include_icon'] ) ){
				$include_icon = $step['include_icon'];
			}
		}

		if( $include_icon ){

			if( isset( $step['main_icon'] ) ){ //Check if custom image added

				if( !empty( $step['main_icon'] ) ){
					$image_id = $step['main_icon'];
					$image = wp_get_attachment_image_src( $image_id, 'full' );

					if( is_array( $image ) ){
						$icon = $image[0];
					}

				}elseif( isset( $cart->cart_contents ) ){ //Check if product image should be included
			
					if( !empty( $cart->cart_contents ) ){
						$cart_contents = $admin->get_saved_cart_contents( $cart->cart_contents );
						
						if( is_array( $cart_contents ) ){
							foreach( $cart_contents as $key => $item ){ //Building product array
								$icon = $admin->get_product_thumbnail_url( $item );
								
								if( !empty( $icon ) ){ //Stop looping if we find product image
									break;
								}
							}
						}
					}

				}elseif( $test ){ //If testing notification preview - output product image placeholder
					$icon = $public->get_plugin_url() . 'public/assets/push-notification-product-icon.png';
				}
			}
		}

		if( isset( $step['include_image'] ) ){

			if( !empty( $step['include_image'] ) ){
				$include_image = $step['include_image'];
			}
		}

		if( $include_image ){

			if( isset( $step['main_image'] ) ){

				if( !empty( $step['main_image'] ) ){
					$image_id = $step['main_image'];
					$image = wp_get_attachment_image_src( $image_id, 'full' );

					if( is_array( $image ) ){
						$main_image = $image[0];
					}

				}elseif( $test ){ //If testing notification preview - output large image placeholder
					$main_image = $public->get_plugin_url() . 'public/assets/push-notification-product-image.png';
				}
			}
		}

		if( isset( $cart->language ) ){
			$target_language = $cart->language;
		}

		$payload = apply_filters(
			'cartbounty_pro_push_notification_recovery_args',
			array(	
				'title' 				=> $admin->sanitize_field( stripslashes( $admin->prepare_tags( $heading, $cart ) ) ), //The length of the visible area varies between 43 to 69 characters depending on different OS and browsers
				'options'	=> array(
					'body' 					=> $contents, //The length of the visible area varies between 50 to 136 characters depending on the OS and browser
					'icon' 					=> $icon, //The icon has to be a square image preferably at least 256 x 256 px
					'image' 				=> $main_image, //Rrecommended size being 1024 x 512 px
					'data' 					=> array(
						'url' 				=> $recovery_link
					),
					'actions'				=> array(
						array(
							'action'		=> 'complete-checkout',
							'title'			=> esc_html__( 'Complete checkout', 'woo-save-abandoned-carts' )
						)
					),
					'dir' 					=> 'auto', //The direction of the notification; it can be auto, ltr or rtl
					'renotify' 				=> false, //A boolean that indicates whether to suppress vibrations and audible alerts when reusing a tag value. If options's renotify is true and options's tag is the empty string a TypeError will be thrown
					'requireInteraction' 	=> false,
					'silent'	 			=> false,
					'tag' 					=> '', //An ID for a given notification that allows you to find, replace, or remove the notification using a script if necessary
					'vibrate' 				=> array(),
					'badge' 				=> '', //Badge is only used in Chrome on Android. Recommended size 96 x 96 px
				),
				'source_language'		=> $source_language, //Introduced for GTranslate
				'target_language'		=> $target_language, //Introduced for GTranslate
			)
		);

		if( $test ){
			unset( $payload['options']['actions'] ); //Removing actions as Google Chrome does not allow sending action to local notification (without a running service worker)
		}

		restore_previous_locale();

		return $payload;
	}

	/**
	* Retrieving input data that is used during notification testing
	*
	* @since    9.10
	* @return   array
	*/
	public function get_preview_data( $data ){
		$include_icon = false;
		$include_image = false;

		if( isset( $data['include_icon'] ) ){
			
			if( $data['include_icon'] == 'true' ){
				$include_icon = true;
			}
		}

		if( isset( $data['include_image'] ) ){
			
			if( $data['include_image'] == 'true' ){
				$include_image = true;
			}
		}
		
		$preview_data = array(
			'heading' 				=> isset( $data['main_title'] ) ? $data['main_title'] : '',
			'content' 				=> isset( $data['content'] ) ? $data['content'] : '',
			'include_icon' 			=> $include_icon,
			'include_image' 		=> $include_image,
			'main_icon' 			=> isset( $data['main_icon'] ) ? $data['main_icon'] : '',
			'main_image' 			=> isset( $data['main_image'] ) ? $data['main_image'] : '',
			'coupon_generate'		=> isset( $data['coupon_generate'] ) ? $data['coupon_generate'] : '',
			'coupon_expiry'			=> isset( $data['coupon_expiry'] ) ? $data['coupon_expiry'] : '',
			'coupon_code'			=> isset( $data['coupon_code'] ) ? $data['coupon_code'] : '',
			'coupon_prefix'			=> isset( $data['coupon_prefix'] ) ? $data['coupon_prefix'] : ''			
		);

		return $preview_data;
	}


	/**
     * Building notification contents
     *
     * @since    9.10
     * @return   html
     * @param    object     $cart    		  	  Cart data. If false, then we must output
     * @param    array      $step_nr              Automation step number
     * @param    boolean    $test                 Weather request triggered by message test function
     * @param    array    	$preview_data         Automation step input data passed from frontend to allow preview
     */
	private function get_reminder_contents( $cart, $step_nr, $test = false, $preview_data = array() ){
		$admin = $this->admin();
		$coupons = $this->coupons();
		$automation = $this->automation();
		$coupon = $coupons->get_coupon( 'push_notification', 'cartbounty_pro_push_notification_steps', $step_nr, $cart ); //Coupon code creation
		$automation_steps = get_option( 'cartbounty_pro_push_notification_steps' );
		$step = $automation_steps[$step_nr];

		if( $test ){ //In case we are dealing with Test message - replace saved option array with dynamically loaded data from the form
			$step = $preview_data;
			$coupon = $preview_data;
		}

		//Setting defaults
		$message_contents = $automation->get_content_input_fields( $step_nr, $step, 'content', 'push_notification' );
		$content = apply_filters( 'wpml_translate_single_string', $message_contents['value'], 'woo-save-abandoned-carts', $message_contents['name'] );
		$coupon_code = '';
		$coupon_prefix = '';
		$coupon_expiry = false;

		if( isset( $coupon['coupon_code'] ) ){

			if( !empty( $coupon['coupon_code'] ) ){
				$coupon_code = $admin->sanitize_field( stripslashes( $coupon['coupon_code'] ) );
			}
		}

		if( $test ){ //In case we are sending a test message

			if( isset( $step['coupon_generate'] ) ){ //Handle additional coupon code generation functions in case coupon generation enabled

				if( ( $step['coupon_generate'] ) == 'true' ){

					if( isset( $step['coupon_prefix'] ) ){ //Get coupon prefix
						$coupon_prefix = $step['coupon_prefix'];
					}

					$coupon_code = $admin->sanitize_field( $coupons->generate_coupon_code( 'push_notification', 'cartbounty_pro_push_notification_steps', $step_nr, $coupon_prefix ) );

					if( isset( $step['coupon_expiry'] ) ){
						$coupon_expiry = $admin->sanitize_field( $coupons->get_preview_coupon_expiration_date( $step['coupon_expiry'] ) );
					}

				}else{

					if( isset( $step['coupon_code'] ) ){ //If coupon code selected, get date of the predefined coupon code
						$coupon_expiry = $admin->sanitize_field( $coupons->get_coupon_expiration_date( $step['coupon_code'] ) );
					}
				}
			}
		}
		
		$content = $admin->sanitize_field( stripslashes( $admin->prepare_tags( $content, $cart, strtoupper( $coupon_code ), $coupon_expiry ) ) );
		
		return $content;
	}

	/**
     * Add new notification to sent notifications table
     *
     * @since    9.10
     * @param    integer    $cart_id   			Cart ID
     * @param    integer    $step_nr            Automation step number
     * @param    string     $current_time       Current time
     * @param    boolean    $send_failed    	If the message was sent successfully or not
     * @param    string     $error_reason       Description of error in case notification was not sent
     */
	public function add_notification( $cart_id, $step_nr, $current_time, $send_failed = false, $error_reason = '' ){
		global $wpdb;
		$admin = $this->admin();
		$automation = $this->automation();
		$action = 'send';
		$notification_table = $wpdb->prefix . CARTBOUNTY_PRO_TABLE_NAME_NOTIFICATIONS;

		//Making sure that the notification table exists or is created
		if( !$admin->table_exists( 'push_notification_table_exists' ) ){
			$this->create_notification_table();
		}

		$data = array(
			'cart' 				=> $cart_id,
			'step' 				=> $step_nr,
			'time' 				=> $current_time,
			'failed' 			=> $send_failed,
			'failure_reason' 	=> $error_reason,
		);
		$format = array(
			'%d',
			'%d',
			'%s',
			'%d',
			'%s',
		);
		$wpdb->insert( $notification_table, $data, $format );

		if( $send_failed ){
			$action = 'send_failed';
		}

		$automation->increase_message_stats( $step_nr, $action, 'push_notification' );
	}

	/**
	 * Creating database table to save push notification history
	 *
	 * @since    9.10
	 */
	public static function create_notification_table(){
		global $wpdb;
		$admin = new CartBounty_Pro_Admin( CARTBOUNTY_PRO_PLUGIN_NAME_SLUG, CARTBOUNTY_PRO_VERSION_NUMBER );
		$notification_table = $wpdb->prefix . CARTBOUNTY_PRO_TABLE_NAME_NOTIFICATIONS;
		$charset_collate = $wpdb->get_charset_collate();
		$misc_settings = $admin->get_settings( 'misc_settings' );

		$sql = "CREATE TABLE $notification_table (
			id BIGINT(20) NOT NULL AUTO_INCREMENT,
			cart BIGINT(20) NOT NULL,
			step INT(3) DEFAULT 0,
			time DATETIME DEFAULT '0000-00-00 00:00:00',
			open TINYINT(1) DEFAULT 0,
			click TINYINT(1) DEFAULT 0,
			recovered TINYINT(1) DEFAULT 0,
			failed TINYINT(1) DEFAULT 0,
			failure_reason LONGTEXT,
			PRIMARY KEY (id)
		) $charset_collate;";

		require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
		dbDelta( $sql );
		
		//Resets table Auto increment index to 1
		$sql = "ALTER TABLE $notification_table AUTO_INCREMENT = 1";
		dbDelta( $sql );
		
		$misc_settings['push_notification_table_exists'] = true;
		update_option( 'cartbounty_pro_misc_settings', $misc_settings ); //Updating status and telling that table has been created
		return;
	}

	/**
	 * Returning VAPID keys required for push notification authorization
	 *
	 * @since    9.10
	 * @return   array
	 */
	function get_vapid_keys(){
		$keys = get_option( 'cartbounty_pro_push_notification_vapid_keys' );

		if( !$keys ){ //If keys have not been created - generate them
			$keys = $this->create_vapid_keys();

		}else{
			$keys = maybe_unserialize( $keys );
		}
		
		return $keys;
	}

	/**
	 * Generating VAPID keys
	 *
	 * @since    9.10
	 * @return   array
	 */
	function create_vapid_keys(){
		$keys = Minishlink\WebPush\VAPID::createVapidKeys();
		
		if( is_array( $keys )){
			update_option( 'cartbounty_pro_push_notification_vapid_keys', maybe_serialize( $keys ) );
		}
		
		return $keys;
	}

	/**
	 * Check if permission request should be displayed
	 * If Test mode enabled - display permission request only to admin users
	 *
	 * @since    9.10
	 * @return   boolean
	 */
	function display_permission_request(){
		$admin = $this->admin();
		$result = false;
		$test_mode_on = $this->get_settings( 'test_mode' );
		$user_is_admin = $admin->user_is_admin();

		if( $test_mode_on && $user_is_admin ){
			//Outputting tool for Testing purposes to Administrators
			$result = true;
		}elseif( ( !$test_mode_on ) ){
			//Outputting tool for all users in case test mode disabled
			$result = true;
		}

		return $result;
	}		
}