<?php
namespace SabaiApps\Directories\Component\WordPress;

use SabaiApps\Directories\Component\AbstractComponent;
use SabaiApps\Directories\Component\Form;

/**
 * WordPressComponent provides integration between WordPress and SabaiApps applications.
 * Handles WordPress-specific environment info, page slugs, scheduled tasks, and field types.
 */
class WordPressComponent extends AbstractComponent implements
	Form\IFields {

	const VERSION = '2.0.6', PACKAGE = 'directories-pro';

	/**
	 * Returns a description of the component.
	 *
	 * @return string
	 */
	public static function description() {
		return 'Provides integration between WordPress and SabaiApps applications.';
	}

	/**
	 * Returns the events this component listens to with custom priority.
	 */
	public static function events() {
		return array(
			'systemtoolruntask' => 1,
		);
	}

	/**
	 * Adds WordPress environment info to the system info array.
	 *
	 * @param array $info
	 */
	public function onSystemAdminSystemInfoFilter( &$info ) {
		$platform          = $this->_application->getPlatform();
		$info['wordpress'] = array(
			'label'           => '<i class="fab fa-fw fa-wordpress"></i> ' . $this->_application->H( __( 'WordPress environment', 'directories-pro' ) ),
			'label_no_escape' => true,
			'weight'          => 2,
			'info'            => array(
				'version'                     => array(
					'name'  => 'Version',
					'value' => $GLOBALS['wp_version'],
				),
				'site_url'                    => array(
					'name'  => 'Site URL',
					'value' => site_url(),
				),
				'main_url'                    => array(
					'name'  => 'Main URL',
					'value' => $platform->getMainUrl(),
				),
				'admin_url'                   => array(
					'name'  => 'Admin URL',
					'value' => $platform->getSiteAdminUrl(),
				),
				'abspath'                     => array(
					'name'  => 'Path (ABSPATH)',
					'value' => ABSPATH,
				),
				'plugin_path'                 => array(
					'name'  => 'Plugins directory',
					'value' => $this->_application->getPackagePath(),
				),
				'writeable_dir'               => array(
					'name'  => 'Writeable directory',
					'value' => $dir = $platform->getVarDir(),
					'error' => ! is_writeable( $dir ),
				),
				'lang_dir'                    => array(
					'name'  => 'Languages directory (WP_LANG_DIR)',
					'value' => WP_LANG_DIR,
				),
				'locale'                      => array(
					'name'  => 'Locale',
					'value' => get_locale(),
				),
				'debug'                       => array(
					'name'  => 'Debug mode (WP_DEBUG)',
					'value' => defined( 'WP_DEBUG' ) && WP_DEBUG ? 'On' : 'Off',
				),
				'debug_display'               => array(
					'name'  => 'Debug display (WP_DEBUG_DISPLAY)',
					'value' => defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY ? 'On' : 'Off',
				),
				'debug_log'                   => array(
					'name'  => 'Debug log (WP_DEBUG_LOG)',
					'value' => defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ? 'On' : 'Off',
				),
				'disable_fatal_error_handler' => array(
					'name'  => 'WP_DISABLE_FATAL_ERROR_HANDLER',
					'value' => defined( 'WP_DISABLE_FATAL_ERROR_HANDLER' ) && WP_DISABLE_FATAL_ERROR_HANDLER ? 'On' : 'Off',
				),
				'memory_limit'                => array(
					'name'  => 'WP memory limit (WP_MEMORY_LIMIT)',
					'value' => defined( 'WP_MEMORY_LIMIT' ) ? WP_MEMORY_LIMIT : '',
				),
				'theme'                       => array(
					'name'  => 'Theme',
					'value' => wp_get_theme( get_template() )->get( 'Name' ),
				),
			),
		);
		if ( $rewrite_rules = $platform->getAllRewriteRules() ) {
			$info['wordpress']['info']['rewrite_rules'] = array(
				'name'      => 'Rewrite rules',
				'no_escape' => true,
			);
			foreach ( $rewrite_rules as $regex => $rewrite ) {
				$rewrite_rules[ $regex ] = '<li class="' . DRTS_BS_PREFIX . 'mb-2"><code>' . $regex . '</code>  <i class="fas fa-fw fa-long-arrow-alt-right"></i>  ' . $rewrite . '</li>';
			}
			$info['wordpress']['info']['rewrite_rules']['value'] = '<div style="max-height:300px; overflow:scroll;"><ul class="' . DRTS_BS_PREFIX . 'px-0">' . implode( PHP_EOL, $rewrite_rules ) . '</ul></div>';
		}
		$slugs      = $this->_application->System_Slugs( null, 'Directory' );
		$page_slugs = $this->_application->getPlatform()->getPageSlugs();
		foreach ( array_keys( $slugs ) as $component ) {
			foreach ( array_keys( $slugs[ $component ] ) as $slug_name ) {
				if ( ! empty( $slugs[ $component ][ $slug_name ]['parent'] )
					|| ( ! $real_slug = @$page_slugs[1][ $component ][ $slug_name ] )
				) {
					continue;
				}

				$error = false;
				if ( ( ! $page_id = isset( $page_slugs[2][ $real_slug ] ) ? $page_slugs[2][ $real_slug ] : null )
					|| ( ! $permalink = get_permalink( $page_id ) )
				) {
					if ( isset( $slugs[ $component ][ $slug_name ]['required'] )
						&& ! $slugs[ $component ][ $slug_name ]['required']
					) {
						continue;
					}

					$error = true;
				}

				$info['wordpress']['info'][ 'page_' . $slug_name ] = array(
					'name'      => 'Page - ' . $slugs[ $component ][ $slug_name ]['admin_title'],
					'value'     => $error ? '' : '<a href="' . $permalink . '">' . $this->_application->H( get_the_title( $page_id ) ) . '</a>',
					'no_escape' => ! $error,
					'error'     => $error,
				);
			}
		}
		if ( ! empty( $_GET['advanced'] ) ) {
			$info['wordpress']['info']['system_slugs'] = array(
				'name'      => 'System slugs',
				'no_escape' => true,
				'value'     => '<pre>' . $this->_application->H( print_r( $this->_application->System_Slugs(), true ) ) . '</pre>',
			);
			$info['wordpress']['info']['page_slugs']   = array(
				'name'      => 'Page slugs',
				'no_escape' => true,
				'value'     => '<pre>' . $this->_application->H( print_r( $this->_application->getPlatform()->getPageSlugs( false ), true ) ) . '</pre>',
			);
			foreach ( $this->_application->getPlatform()->getLanguages() as $lang ) {
				$info['wordpress']['info'][ 'page_slugs_' . $lang ] = array(
					'name'      => 'Page slugs (' . $lang . ')' . ( $this->_application->getPlatform()->getCurrentLanguage() === $lang ? ' - Current' : '' ),
					'no_escape' => true,
					'value'     => '<pre>' . $this->_application->H( print_r( $this->_application->getPlatform()->getPageSlugs( $lang ), true ) ) . '</pre>',
				);
			}
		}
	}

	/**
	 * Handles event after ISlugs interface upgrade success.
	 *
	 * @param AbstractComponent $component
	 */
	public function onSystemISlugsUpgradeSuccess( AbstractComponent $component ) {
		$this->_createSlugPages( $component );
	}

	/**
	 * Handles event after ISlugs interface install success.
	 *
	 * @param AbstractComponent $component
	 */
	public function onSystemISlugsInstallSuccess( AbstractComponent $component ) {
		$this->_createSlugPages( $component );
	}

	/**
	 * Handles event after ISlugs interface uninstall success.
	 *
	 * @param AbstractComponent $component
	 */
	public function onSystemISlugsUninstallSuccess( AbstractComponent $component ) {
		$this->_deleteSlugPages( $component );
	}

	/**
	 * Creates WordPress pages for slugs defined by the component.
	 *
	 * @param AbstractComponent $component
	 */
	protected function _createSlugPages( $component ) {
		$platform = $this->_application->getPlatform();
		if ( $languages = $platform->getLanguages() ) {
			foreach ( $languages as $lang ) {
				$this->_doCreateSlugPages( $component, $lang );
			}
		} else {
			$this->_doCreateSlugPages( $component );
		}
	}

	/**
	 * Helper to create slug pages for a specific language.
	 *
	 * @param AbstractComponent $component
	 * @param string|false      $lang
	 */
	protected function _doCreateSlugPages( $component, $lang = false ) {
		$page_slugs     = $this->_application->getPlatform()->getPageSlugs( $lang );
		$component_name = $component->getName();
		$slugs          = (array) $this->_application->System_Slugs( $component_name, null, false );
		foreach ( $slugs as $slug_name => $slug ) {
			if ( isset( $page_slugs[1][ $component_name ][ $slug_name ] ) ) {
				continue; // already exists, do not overwrite
			}

			if ( empty( $slug['parent'] ) ) {
				if ( ! $slug['title'] ) {
					continue;
				}

				if ( $page_id = $this->_application->getPlatform()->createPage( $slug['slug'], $slug['title'], $lang ) ) {
					$page_slugs[0][ $slug['slug'] ]                 = $slug['slug'];
					$page_slugs[1][ $component_name ][ $slug_name ] = $slug['slug'];
					$page_slugs[2][ $slug['slug'] ]                 = $page_id;
				}
			} else {
				if ( ! isset( $page_slugs[1][ $component_name ][ $slug['parent'] ] ) // no valid parent
					|| empty( $slug['bundle_type'] )
					|| ( ! $bundle = $this->_application->Entity_Bundle( $slug['bundle_type'], isset( $slug['bundle_group'] ) ? $slug['bundle_group'] : '' ) )
				) {
					continue;
				}

				// Save post type and slug
				$_slug = $page_slugs[1][ $component_name ][ $slug['parent'] ] . '/' . $slug['slug'];
				call_user_func_array(
					array( $this->_application, 'WordPress_PageSettingsForm_saveSingle' ),
					array( $bundle, $slug_name, $_slug, &$page_slugs )
				);
			}
		}
		// Remove slugs that no longer exist
		if ( ! empty( $page_slugs[1][ $component_name ] ) ) {
			foreach ( $page_slugs[1][ $component_name ] as $slug_name => $slug ) {
				if ( ! isset( $slugs[ $slug_name ] ) ) {
					// Slug was removed
					unset( $page_slugs[1][ $component_name ][ $slug_name ], $page_slugs[0][ $slug ], $page_slugs[2][ $slug ], $page_slugs[5][ $slug ] );
				}
			}
		}

		$this->_application->getPlatform()->setPageSlugs( $page_slugs, $lang );
	}

	/**
	 * Deletes WordPress pages for slugs defined by the component.
	 *
	 * @param AbstractComponent $component
	 */
	protected function _deleteSlugPages( $component ) {
		$platform = $this->_application->getPlatform();
		if ( $languages = $platform->getLanguages() ) {
			foreach ( $languages as $lang ) {
				$this->_doDeleteSlugPages( $component, $lang );
			}
		} else {
			$this->_doDeleteSlugPages( $component );
		}
	}

	/**
	 * Helper to delete slug pages for a specific language.
	 *
	 * @param AbstractComponent $component
	 * @param string|false      $lang
	 */
	protected function _doDeleteSlugPages( $component, $lang = false ) {
		$page_slugs = $this->_application->getPlatform()->getPageSlugs( $lang );
		if ( ! empty( $page_slugs[0] ) && ! empty( $page_slugs[1][ $component->getName() ] ) ) {
			// Remove slugs and ids of the uninstalled plugin from the global slug list
			$component_slugs = array_flip( array_values( $page_slugs[1][ $component->getName() ] ) ); // slugs as key
			$page_slugs[0]   = array_diff_key( $page_slugs[0], $component_slugs ); // remove from slugs by slug list
			$page_slugs[2]   = array_diff_key( $page_slugs[2], $component_slugs ); // remove from page ids by slug list
			$page_slugs[5]   = array_diff_key( $page_slugs[5], $component_slugs ); // remove from post_type/taxonomy info by slug list
			unset( $page_slugs[1][ $component->getName() ] ); // unset slugs, page titles by component
		}
		$this->_application->getPlatform()->setPageSlugs( $page_slugs, $lang );
	}

	/**
	 * Handles component upgrade event for scheduling cron jobs.
	 *
	 * @param object $componentEntity
	 * @param string $previousVersion
	 */
	public function onSystemComponentUpgraded( $componentEntity, $previousVersion ) {
		if ( $componentEntity->name === $this->_name
			&& version_compare( $previousVersion, self::VERSION, '<' )
		) {
			wp_clear_scheduled_hook( 'drts_cron' );
		}
	}

	/**
	 * Returns available custom field types for forms.
	 *
	 * @return array
	 */
	public function formGetFieldTypes() {
		return array( 'wp_editor', 'wp_media_manager' );
	}

	/**
	 * Returns the field object for a given field type.
	 *
	 * @param string $type
	 * @return object|null
	 */
	public function formGetField( $type ) {
		switch ( $type ) {
			case 'wp_editor':
				return new FormField\EditorFormField( $this->_application, $type );
			case 'wp_media_manager':
				return new FormField\MediaManagerFormField( $this->_application, $type );
		}
	}

	/**
	 * Initializes WordPress hooks and shortcodes.
	 */
	public function onCorePlatformWordPressInit() {
		$application = $this->_application;
		if ( file_exists( __DIR__ . '/Updater.php' ) ) {
			\SabaiApps\Directories\Component\WordPress\Updater::getInstance( $this->_application )->onInit();
		}

		$application->WordPress_Blocks_init();

		if ( ! is_admin() ) {
			// Init page specific shortcodes
			if ( $shortcodes = $application->WordPress_Shortcodes() ) {
				foreach ( array_keys( $shortcodes ) as $shortcode ) {
					add_shortcode( $shortcode, array( $application, 'WordPress_Shortcodes_doShortcode' ) );
				}
			}
		} else {
			add_action(
				'plugin_row_meta',
				function ( $meta, $file, $data, $status ) {
					if ( $file === 'directories-pro/directories-pro.php' ) {
						$meta['documentation'] = '<a href="https://directoriespro.com/documentation/" target="_blank" rel="noopener">Documentation</a>';
						$meta['changelog']     = '<a href="https://directoriespro.com/category/releases/" target="_blank" rel="noopener">Change log</a>';
					}
					return $meta;
				},
				10,
				4
			);
			add_action(
				'in_plugin_update_message-directories-pro/directories-pro.php',
				function ( $plugin, $response ) {
					if ( ( $new_version_parts = explode( '.', $response->new_version ) )
					&& ( $old_version_parts = explode( '.', $plugin['Version'] ) )
					&& $new_version_parts[0] > $old_version_parts[0]
					) {
						echo '<br /><br /><strong>' . esc_html__( 'Attention!', 'directories-pro' ) . '</strong> ' . esc_html__( 'The update available is a major update of the plugin. Please be sure to backup your site before updating since there is no going back.', 'directories-pro' );
					}
					if ( $pos = strpos( $response->new_version, '-dev' ) ) {
						printf(
							'<br /><br /><strong>' . esc_html__( 'Attention!', 'directories-pro' ) . '</strong> ' . esc_html__( 'The update available is a development version of %s. If you do not feel comfortable to use a development version, please wait for the stable release.', 'directories-pro' ),
							substr( $response->new_version, 0, $pos )
						);
					}
				},
				10,
				2
			);
		}

		// Override lost password URL
		if ( $application->isComponentLoaded( 'FrontendSubmit' )
			&& ( $login_slug = $application->getComponent( 'FrontendSubmit' )->hasCustomLoginRegistrationPage() )
			&& $application->getComponent( 'FrontendSubmit' )->showLostPasswordLink()
		) {
			add_filter(
				'lostpassword_url',
				function ( $url, $redirect ) use ( $login_slug ) {
					return (string) $this->_application->Url( '/' . $login_slug . '/lost_password', array( 'redirect_to' => $redirect ) );
				},
				10,
				2
			);
		}

		add_action(
			'after_switch_theme',
			function () use ( $application ) {
				$application->System_Component_reload();
			}
		);
	}

	/**
	 * Handles component uninstallation, optionally removing data.
	 *
	 * @param bool $removeData
	 */
	public function uninstall( $removeData = false ) {
		wp_clear_scheduled_hook( 'drts_cron' );

		if ( ! $removeData ) {
			return;
		}

		// Delete pages
		if ( ( $page_slugs = $this->_application->getPlatform()->getPageSlugs() )
			&& ! empty( $page_slugs[2] )
		) {
			foreach ( $page_slugs[2] as $page_id ) {
				wp_trash_post( $page_id );
			}
		}
	}

	/**
	 * Enqueues scripts and styles for WordPress media manager/editor fields.
	 *
	 * @param array $options
	 */
	public function onFormScripts( $options ) {
		if ( empty( $options ) || in_array( 'wordpress_mediamanager', $options ) ) {
			wp_enqueue_media();
			wp_enqueue_editor();
			$this->_application->getPlatform()
				->addJsFile( 'wordpress-mediamanager.min.js', 'drts-wordpress-mediamanager', array( 'jquery-ui-sortable', 'drts' ) );
		}
	}

	/**
	 * Handles event after all routes are reloaded to flush rewrite rules.
	 */
	public function onSystemAllRoutesReloaded() {
		$this->_application->getPlatform()->flushRewriteRules();
	}

	/**
	 * Handles event when a system tool task is run.
	 *
	 * @param string $toolName
	 */
	public function onSystemToolRunTask( $toolName ) {
		do_action( 'qm/cease' ); // stop Query Monitor timing here to avoid timeouts
	}
}
