<?php
/**
 * Please see wp-cookie-allow.php for more details.
 *
 * @author $Vincent Weber <weepie-plugins@outlook.com>$
 */

use WpieFw\Helpers\WpieAjaxHelper;
 use WpieFw\Exceptions\WpieExceptionInterface;
use WpieFw\Templates\Files\WpieTemplatesFileFinder;
use WpieFw\Templates\WpieTemplate;

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

if( !class_exists( 'WpcaModuleCompatibility' ) ) {

	/**
	 * WpcaModleCompatibility Class
	 *
	 * Module to handle the compatibility with 3rd parties
	 *
	 * @author $Vincent Weber <weepie-plugins@outlook.com>$
	 *
	 * @since 3.2.13
	 */
	class WpcaModuleCompatibility extends WpieFw\Modules\Iterator\WpieModule {

		/**
		 * An array with CompatibilityItem items
		 *
		 * @since 3.2.13
		 *
		 * @var CompatibilityItem[]
		 */
		private $items = [];

		const HOOK_PREFIX = 'wpca_compatibility_';

		/**
		 * File name for the Automate settings JavaScript
		 *
		 * @since 3.2.13
		 *
		 * @var string
		 */
		const FILE_NAME_SETTINGS_JS = 'wpca-compatibility';

		/**
		 * The settings key in the general setting array
		 *
		 * @since 3.2.13
		 *
		 * @var string
		 */
		const SETTINGS_KEY_COMPAT_ITEMS = 'compatibility_data';

		/**
		 * Template file name of the automate 3rd party list table
		 *
		 * @since 3.2.13
		 *
		 * @var string
		 */
		const TEMPL_FILE_NAME_COMPATIBILITY_LIST_TABLE = 'wpca-general-compatibility-table.php';

        /**
		 * Constructor
		 *
		 * @since 3.2.13
		 *
		 * @acces public
		 */
		public function __construct() {}


		/* (non-PHPdoc)
		 * @see WpiePluginModule::start()
		 */
        public function start() {}

		/**
		 * Callback for the wpca_script_frontend_vars_before hook
		 *
		 * @access public
		 *
		 * @param array $vars
		 *
		 * @since 3.2.13
		 * @since 3.4.5 renamed to setScriptVars
		 *
		 * @return array
		 */
		public function setScriptVars( $vars )
		{
            $vars['init3rdPartyTimeout'] = apply_filters( 'wpca_init_3rd_party_timeout', 200 );
			$vars['init3rdPartyItems'] = apply_filters( 'wpca_init_3rd_party_items', [] );

			return $vars;
		}

		/**
		 * Callback for the wpca_settings_scripts_after hook
		 *
		 * Enqueue jQuery scripts for the admin settings page
		 *
		 * @access public
		 *
		 * @param string $hook_suffixgetCompatibilityItems
		 * @uses wp_enqueue_script()
		 */
		public function setScriptsSettings( $hook_suffix, $wp_scripts, $isScriptDebug )
		{
			$ext = ( $isScriptDebug ) ? '.js' : '.min.js';
			$relModulePath = parent::getIndex() . '/js/' . self::FILE_NAME_SETTINGS_JS . $ext;

			$path = $this->globals->get( 'moduleUri' ) . '/' . $relModulePath;

			wp_enqueue_script( self::FILE_NAME_SETTINGS_JS, $path, array( 'wpca-settings' ), $this->globals->get( 'version' ) );
		}

		/**
		 * Callback for the wpca_templ_vars hook
		 *
		 * Filter the Template vars array and add all variables that need to be available in the settings page Template
		 *
		 * @access public
		 *
		 * @param array	$vars
		 * @param string $tab
		 *
		 * @since 3.2.13
		 *
		 * @return array
		 */
		public function setTemplateVars( $vars, $tab )
		{
			// if not on general tab, do not go further
			if( 'general' !== $tab ) {
				return $vars;
			}

			try {
			    if( 'general' === $tab ) {
				    $vars['table_compatibility'] = $this->renderTable();
			    }
			} catch ( WpieExceptionInterface $e ) {
				throw $e;
			}

			return $vars;
		}

		/**
		 * Render the compatibility items table
		 *
		 * @uses self::_getAutomateData()
		 * @uses WpieTemplate::render()
		 *
		 * @since 3.2.13
		 *
		 * @return string
		 */
		public function renderTable()
		{
			try {
				$finder = new WpieTemplatesFileFinder( dirname(__FILE__) . '/templates', '', '', self::TEMPL_FILE_NAME_COMPATIBILITY_LIST_TABLE, false );
				$template = new WpieTemplate( $finder, self::TEMPL_FILE_NAME_COMPATIBILITY_LIST_TABLE );
			} catch( WpieExceptionInterface $e ) {
				throw $e;
			}

			// only show non-silent items
			$items = array_filter( $this->items, function( $item ) { return !$item->config->silent; } );

			$template->setVar( 'setting', $this->settingsProcessor->getName( 'general' ) );
			$template->setVar( self::SETTINGS_KEY_COMPAT_ITEMS, $items );

			return $template->render( false, true );
		}

		/**
		 * Callback for the wpca_validated_data_wpca_settings_general hook
		 *
		 * @acces public
		 *
		 * @param array $data
		 * @param bool $hasWarnings
		 *
		 * @since 3.2.13
		 *
		 * @return array the filtered array
		 */
		public function validated( $data, $hasWarnings = false )
		{
			if( isset( $data[self::SETTINGS_KEY_COMPAT_ITEMS] ) ) {
				$compatibilityData = $data[self::SETTINGS_KEY_COMPAT_ITEMS];
				$compatibilityDataBefore = $this->items;

				foreach( $compatibilityData as $type => $compatData ) {
					if( !isset( $compatibilityDataBefore[$type] ) ) {
						unset( $data[self::SETTINGS_KEY_COMPAT_ITEMS][$type] );
						continue;
					}

					if( !isset( $compatData['active'] ) )
						$data[self::SETTINGS_KEY_COMPAT_ITEMS][$type]['active'] = 0;
					else {
						$data[self::SETTINGS_KEY_COMPAT_ITEMS][$type]['active'] = (int)$compatData['active'];
					}
				}
			}

			return $data;
		}

		/**
		 * Callback for the wpca_update_settings hook
		 *
		 * Filter the 'updates' array. Main tasks are:
		 *  - update the 'compatibility_data' setting form fields
		 *
		 * @access public
		 *
		 * @param array $updates
		 * @param array	$data
		 * @param string $setting the current settings DB name
		 *
		 * @since 3.2.13
		 *
		 * @return array with updated setting form fields
		 */
		public function updateSetting( $updates, $data, $setting )
		{
		    if( $this->settingsProcessor->getName( 'general' ) !== $setting ) {
				return $updates;
			}

			// Update the compatibility items active values
			if( $this->settingsProcessor->getName( 'general' ) === $setting ) {
			    if( isset( $data[self::SETTINGS_KEY_COMPAT_ITEMS] ) ) {
					array_walk(  $this->items, function($item, $k, $data) {
						if( isset( $data[self::SETTINGS_KEY_COMPAT_ITEMS][$item->config->type] ) ) {
							$item->config->active = ( $data[self::SETTINGS_KEY_COMPAT_ITEMS][$item->config->type]['active'] === 1 );
						}
					}, $data );

			        $updates[self::SETTINGS_KEY_COMPAT_ITEMS] = $this->items;
			    }
			}

			return $updates;
		}

		/**
		 * Callback for the wpca_activate_plugin hook
		 *
		 * Unset the compatibility_data entry in the wpca_settings_general wp_options table
		 *
		 * @access public
		 *
		 * @param bool $upgrading flag is plugin is being updated to a newer version
		 *
		 * @uses WpcaModuleCompatibility::reset()
		 *
		 * @since 3.3
		 */
		public function activatePlugin( $upgrading )
		{
			$this->reset();
		}

		/**
		 * Callback for the wpca_ajax_json_return hook
		 *
		 * @param array $return
		 * @param string $action
		 * @param array $data
		 *
		 * @uses self::reset()
		 * @uses self::renderTable()
		 *
		 * @since 3.2.13
		 *
		 * @return array
		 */
		public function process( $return, $action, $data )
		{
			// if action does not match one of the following, return directly
			if( 'wpca-compatibility-refresh' !== $action ) {
				return $return;
			}

			$return = [];
			$old = $this->items;
			// reset the compatibility data
			$new = $this->reset();

			// array keys
			$keysOld = array_keys( $old );
			$keysNew = array_keys( $new );

			// the newwly added items, empty array with no diff
			$diff = array_values( array_diff( $keysNew, $keysOld ) );

			// add the data to the return array
			$table = $this->renderTable();

			$return['table'] = $table;
			$return['added'] = ( count( $diff ) ) ? $diff : [];

			return $return;
		}

		/**
		 * {@inheritDoc}
		 * @see \WpieFw\Modules\Iterator\WpieModuleInterface::init()
		 *
		 * @since 3.2.13
		 */
		public function init()
		{
			/** @var WpcaModuleCore $moduleCore */
			if( false === ( $moduleCore = $this->getModule( 'core' ) ) ) {
				return;
			}

			$currentTabIdx = $this->settingsProcessor->settingsPage->currentTabIdx;

			$this->loadCompatibilityItems();
			$this->applyLogic();

			add_filter( 'wpca_script_frontend_vars_before', array( $this, 'setScriptVars' ) );

			// ensure the activate (reset) logic is always triggered, also when activating due to a silent upgrade
			add_action( 'wpca_activate_plugin', array( $this, 'activatePlugin' ) );

			if( is_admin() ) {
				if( 'general' === $currentTabIdx || 'content' === $currentTabIdx ) {
					add_filter( 'wpca_templ_vars', array( $this, 'setTemplateVars' ), 10, 2 );
					add_action( 'wpca_settings_scripts_after', array( $this, 'setScriptsSettings' ), 10, 3 );
				}

				if( !WpieAjaxHelper::doingAjax() ) {
					add_filter( 'wpca_validated_data_wpca_settings_general', array( $this, 'validated' ), 10, 2 );
					add_filter( 'wpca_update_settings', array( $this, 'updateSetting' ), 10, 3 );
				}

				if( $moduleCore->doingWpcaAjaxAction ) {
					add_filter( 'wpca_ajax_json_return', array( $this,'process' ), 10, 3 );
				}
			}
		}

		/**
		 * Get compatibility items
		 *
		 * @since 3.2.13
		 *
		 * @return CompatibilityItem[]
		 */
		private function getCompatibilityItems()
		{
			$items = [];

			$key = 'avada-theme';
			$items[$key] = new CompatibilityItem(
				'Avada theme',
				'Avada theme causing the bar/box not to show',
				new CompatibilityItemConfig( $key ),
				'callbackForAvadaTheme'
			);

			$key = 'avada-live';
			$items[$key] = new CompatibilityItem(
				'Avada Live',
				'Avada Live editor not loading',
				new CompatibilityItemConfig( $key ),
				'callbackForAvadaLive'
			);

			$key = 'avada-lightbox';
			$items[$key] = new CompatibilityItem(
				'Avada Live',
				'Avada lightbox script being blocked',
				new CompatibilityItemConfig( $key ),
				'callbackForAvadaLightbox'
			);

			$key = 'avada-fusion-gmaps';
			$items[$key] = new CompatibilityItem(
				'Avada (Fusion) Google Maps',
				'Google Maps build with fusion builder not loading',
				new CompatibilityItemConfig(
					$key,
					WpcaModuleAutomate::CC_IDX_OTHER,
					false, false, true
					// select and  jsFunc are set in frontend js
				)
			);

			$key = 'divi-builder';
			$items[$key] = new CompatibilityItem(
				'Divi Builder',
				'Divi builder not loading',
				new CompatibilityItemConfig( $key ),
				'callbackForDivi'
			);

			$key = 'divi-gmaps';
			$items[$key] = new CompatibilityItem(
				'Divi Google Maps',
				'Google Maps build with Divi Builder not loading',
				new CompatibilityItemConfig(
					$key,
					WpcaModuleAutomate::CC_IDX_OTHER,
					false, false, true
					// select and  jsFunc are set in frontend js
				)
			);

			$key = 'wpgmp-gmaps';
			$items[$key] = new CompatibilityItem(
				'WP Google Maps',
				'Add compatibility for WP Google Maps Plugin',
				new CompatibilityItemConfig(
					$key,
					WpcaModuleAutomate::CC_IDX_OTHER,
					false, false, true,
					'',
					'wpgmpInitMap',
					'wpgmp'
				)
			);

			$key = 'maplistpro-gmaps';
			$items[$key] = new CompatibilityItem(
				'Map list pro',
				'Map list pro Plugin not working',
				new CompatibilityItemConfig(
					$key,
					WpcaModuleAutomate::CC_IDX_OTHER,
					false, false, true,
					'',
					'wpieReloadMapListPro', // call js function to trigger map resize
					'google'
				),
				'callbackForMapListPro'
			);

			$key = 'acl';
			$items[$key] = new CompatibilityItem(
				'Agile Store Locator',
				'Google Maps in the store locator not working',
				new CompatibilityItemConfig(
					$key,
					WpcaModuleAutomate::CC_IDX_OTHER,
					false, false, true,
					'',
					'asl_store_locator',
					'google'
				)
			);
				
			$key = 'avada_grecaptcha';
			$items[$key] = new CompatibilityItem(
				'Avada form Google reCAPTCHA',
				'Add compatibility for Avada fusion form with Google reCAPTCHA',
				new CompatibilityItemConfig(
					$key,
					WpcaModuleAutomate::CC_IDX_OTHER,
					false, false, true,
					'',
					'fusionOnloadCallback',
					'grecaptcha'
				),
				'callbackForAvadaGrecaptcha'
			);				
				
			$key = 'wp-rocket';
			$items[$key] = new CompatibilityItem(
				'WP Rocket',
				'Exclude bar/box HTML from WP Rocket Automatic Lazy Rendering',
				new CompatibilityItemConfig( $key, '', true, true ),
				'callbackForWPRocket'
			);			

			/**
			 * Let others add compatibility items
			 *
			 * @param array $items
			 *
			 * @since 3.2.13
			 */
			$items = apply_filters( 'wpca_compatibility_items', $items );

			foreach( $items as $key => $item ) {
				// for internal callbacks
				$item->hasInternalCallback = (
					is_string( $item->callback ) && method_exists( $this, $item->callback )
				) ? true : false;
			}

			// sort by key
			ksort( $items );

			return $items;
		}

		/**
		 * Load the compatibility items
		 *
		 * If not in the database yet, re-init
		 *
		 * @uses self::getCompatibilityItems()
		 *
		 * @since 3.2.13
		 */
		private function loadCompatibilityItems()
		{
			$hasItems = false;
			if( $this->settingsProcessor->get( 'general' )->has( self::SETTINGS_KEY_COMPAT_ITEMS ) ) {
				$items = $this->settingsProcessor->get( 'general' )->get( self::SETTINGS_KEY_COMPAT_ITEMS );

				$hasItems = ( !is_array( $items ) || ( is_array( $items ) && empty( $items ) ) )
				? false
				: true;
			}

			$this->items = ( !$hasItems )
				? $this->getCompatibilityItems()
				: $items;

			if( !$hasItems ) {
				$this->settingsProcessor->get( 'general' )->set( self::SETTINGS_KEY_COMPAT_ITEMS, $this->items, true );
			}
		}

		private function applyLogic()
		{
			foreach( $this->items as $item ) {
				if( $item->config->active ) {

					$callback = null;
					$hasInternalCallback = is_string( $item->callback ) && method_exists( $this, $item->callback );
					$hasExternalCallback = is_string( $item->callback ) && function_exists( $item->callback );

					if( $hasInternalCallback ) {
						$callback = [ $this, $item->callback ];
					} elseif($hasExternalCallback) {
						$callback = $item->callback;
					}

					if( $callback ) {
						call_user_func_array( $callback, [ $item ] );
					}

					if( !is_admin() && $item->doJsLogic() ) {
						add_filter( 'wpca_init_3rd_party_items', function( $items ) use( $item ) {
							$items[] = $item->config;
							return $items;
						} );
					}
				}
			}
		}

		private function callbackForAvadaTheme( $item )
		{
			// fix Avada placing content added by the `wp_footer` hook inside a hidden <div>
			if( !is_admin() && class_exists( 'Avada' ) ) {
				// remove the original action that renders the bar/box inside the hidden avada <div>
				add_action( 'wpca_init_frontend_only_hooks', function() {
					if( $frontend = $this->getModule( 'frontend' ) ) {
						remove_action( 'wp_footer', array( $frontend, 'addHtml' ), 999999 );
					}
				}, 100 ); // prio 100 to make sure to hook after module frontend

				add_filter( 'wpca_frontend_wrapper_html', function( $html ) {
					// add the bar/box html after the avada main content
					add_action( 'avada_after_main_container', function() use($html) {
						echo $html;
					} );

					return $html;
				} );
			}
        }

		private function callbackForAvadaLive( $item )
		{
            // fix for the Fusion builder not working
            if( isset( $_GET['fb-edit'] ) && ! is_admin() ) {
                add_filter( 'wpca_do_frontend_logic' , '__return_false' );
            }
        }

		private function callbackForAvadaLightbox( $item )
		{
			if( ! is_admin() ) {
				add_filter( 'wpca_replacement_skip', function( $skip, $type, $context, $match ) {	
					if( str_contains( $match, 'var fusionLightboxVars =') ) {
						return true;
					}
					
					return $skip;				
				}, 10, 4 );		
			}
        }
		
		private function callbackForDivi( $item )
		{
            // fix for the Divi Builder not working
            if( isset( $_GET['et_fb'] ) && ! is_admin() ) {
                add_filter( 'wpca_do_frontend_logic' , '__return_false' );
            }
		}

		private function callbackForMapListPro( $item )
		{
			if( ! is_admin() ) {
				add_action( 'wp_footer', function() {
					?><script>function wpieReloadMapListPro(){jQuery && jQuery(document).trigger({type: 'resizeMap'})}</script><?php
					} );
			}
        }

		private function callbackForAvadaGrecaptcha( $item )
		{
			if( ! is_admin() ) {
				add_filter( 'wpca_replacement_skip', function( $skip, $type, $context, $match ) {	
					if( $type === 'google_recaptcha' && str_contains( $match, 'fusionOnloadCallback') && str_contains( $match, 'grecaptcha.ready') ) {
						return true;
					}
					
					return $skip;	
				}, 10, 4 );
			}
        }
		
		private function callbackForWPRocket( $item ) 
		{
			/**
			 * WeePie: exclude bar/box HTML from WP Rockets Automatic Lazy Rendering
			 * 
			 * @see https://docs.wp-rocket.me/article/1835-automatic-lazy-rendering#how-to-exclude-elements-from-this-feature
			 */
			add_filter( 'rocket_lrc_exclusions', function( $exclusions ) {
				$exclusions[] = 'id="wpca-lay-out-wrapper"';

				return $exclusions;
			} );			
		}

		/**
		 * Get the 'checked' compatibility items
		 *
		 * @since 3.2.13
		 *
		 * @return CompatibilityItem[]
		 */
		private function getChecked() {
			return array_filter( $this->items, function( $item ) {
				return $item->config->active;
			} );
		}

		/**
		 * Re init the compatibility items and save it
		 *
		 * @access private
		 *
		 * @uses self::getCompatibilityItems()
		 * @uses self::getChecked()
		 *
		 * @since 3.2.13
		 *
		 * @return CompatibilityItem[]
		 */
		private function reset()
		{
			$compatibilityItems = $this->getCompatibilityItems();
			$checked = $this->getChecked();

			foreach ( $compatibilityItems as $type => $item ) {
				if( isset( $checked[$type] ) && $checked[$type] || $item->config->active ) {
					$compatibilityItems[$type]->config->active = true;
				} else {
					$compatibilityItems[$type]->config->active = false;
				}
			}

			$this->settingsProcessor->get('general')->set( self::SETTINGS_KEY_COMPAT_ITEMS, $compatibilityItems, true );
			// store the new resetted items
			$this->items = $compatibilityItems;

			return $compatibilityItems;
		}
    }
}