<?php
/**
 * The features settings page.
 */
namespace Greyd;

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

new Features( $config );
class Features {

	/**
	 * Holds the plugin config.
	 *
	 * @var object
	 */
	private $config;

	/**
	 * Hold the feature page config
	 * slug, title, url, cap, callback
	 */
	public static $page = array();

	/**
	 * Option name for the features array on site level.
	 *
	 * @var string
	 */
	const OPTION = 'greyd_features';

	/**
	 * Option name for the features array on network level.
	 *
	 * @var string
	 */
	const OPTION_GLOBAL = 'greyd_features_global';

	/**
	 * Constructor
	 */
	public function __construct( $config ) {
		// set config
		$this->config = (object) $config;

		// load Features
		add_action( 'plugins_loaded', array( $this, 'load_active_features' ), 0 );

		// define page details
		add_action( 'init', function() {
			self::$page = array(
				'slug'     => 'greyd_features',
				'title'    => __( 'Features', 'greyd_hub' ),
				'url'      => admin_url( 'admin.php?page=greyd_features' ),
				'cap'      => 'manage_options',
				'callback' => array( $this, 'render_features_page' ),
			);
		}, 0 );

		if ( is_admin() ) {
			// add menu and pages
			add_filter( 'greyd_submenu_pages_network', array( $this, 'add_greyd_submenu_page_network' ) );
			add_filter( 'greyd_submenu_pages', array( $this, 'add_greyd_submenu_page' ) );
		}

		// filter
		add_filter( 'greyd_features_files', array( $this, 'add_core_plugins' ) );
		// add_filter( 'greyd_features_files', array($this, 'add_test_features') );
	}


	/**
	 * Load active features.
	 */
	public function load_active_features() {

		// handle POST Data
		if ( isset( $_GET['page'] ) && $_GET['page'] === 'greyd_features' ) {
			// as soon as possible, before features register and inject menus
			if ( ! empty( $_POST ) ) {
				$this->handle_data( $_POST );
			}
		}

		// global config for incs
		global $config;
		$config = (array) $this->config;

		/**
		 * Use saved feature setup to include.
		 * uncomment to enable and override the dev-mode.
		 */
		$features = self::get_active_features();
		$plugins  = Helper::active_plugins();
		// debug($features);
		// debug($plugins);

		/**
		 * Classic SUITE: fixed list of features to include.
		 */
		if ( Helper::is_greyd_classic() ) {
			$features = array_merge( $features, array(
				'hub/init.php',
				'posttypes/init.php',
				'post-export/init.php',
				'multiselects/init.php',
				'license/init.php',
				'connections/init.php',
				'search/init.php',
				'popups/init.php',
				'dynamic/init.php',
				'query/init.php',
				'library/init.php',
				'user/init.php',
				'icons/icons.php',
				'search-and-replace/search-and-replace.php',
				'comments.php',
				'cookie-handler.php',
				'pagespeed.php',
				'seo.php',
				'uberall.php',
				'smtp.php',
				'accessibility.php',
				'global-dynamic-tags/init.php',
				'headless/init.php',
				// from blocks plugin
				'blocks/init.php',
				'layout/init.php',
				'trigger/init.php',
				'animations/init.php',
				'lottie/init.php'
			) );
		}


		foreach ( $features as $feature ) {

			// paths
			$plugin_path  = wp_normalize_path( GREYD_PLUGIN_PATH . '/' );
			$feature_path = wp_normalize_path( GREYD_PLUGIN_PATH . '/features/' );

			$file = '';
			if ( strpos( $feature, '..' ) !== false ) {
				// is internal plugin
				// convert relative path to absolute path
				$file = wp_normalize_path( realpath( $plugin_path . $feature ) );
			} elseif ( substr_count( $feature, '/' ) > 3 ) {
				// is external
				// is absolute path
				$file = wp_normalize_path( $feature );
			} else {
				// is feature
				// absolute feature path
				$file = wp_normalize_path( $feature_path . $feature );
			}

			// check if feature is active as plugin
			$maybe_plugin = str_replace( wp_normalize_path( WP_PLUGIN_DIR . '/' ), '', $file );

			if ( ! empty( $file ) && ! in_array( $maybe_plugin, $plugins ) && file_exists( $file ) ) {
				// include feature
				// debug($file);
				require_once $file;
			}
		}
	}


	/**
	 * =================================================================
	 *                          Utils
	 * =================================================================
	 */

	/**
	 * Get all active features.
	 *
	 * @return array
	 */
	public static function get_active_features() {

		$active_features = array();
		$all_features    = self::get_all_features();
		$_saved_features = self::get_saved_features();
		$saved_features  = array_merge(
			$_saved_features['global'],
			$_saved_features['site']
		);

		foreach ( $all_features as $feature ) {

			// skip if not valid
			if ( $feature['state'] !== 'valid' ) {
				continue;
			}

			// skip if disabled
			if ( $feature['Disabled'] ) {
				continue;
			}

			if (
				// or feature is forced
				$feature['Forced']
				// feature is set to active
				|| isset( $saved_features[ $feature['slug'] ] )
			) {
				$active_features[ $feature['slug'] ] = $feature['include'];
			}
		}
		return $active_features;
	}

	/**
	 * Get all available features, active and inactive.
	 */
	public static function get_all_features() {

		$path = wp_normalize_path( GREYD_PLUGIN_PATH . '/features/' );
		if ( ! file_exists( $path ) ) {
			return array();
		}

		// get files
		$files = self::get_feature_files();
		// debug($files);

		// get feature data
		foreach ( $files as &$file ) {

			// set features include path
			if ( strpos( $file['include'], '..' ) !== false ) {
				// convert relative path
				$abs = GREYD_PLUGIN_PATH . '/' . $file['include'];
				if ( realpath( $abs ) ) {
					$file['include'] = wp_normalize_path( realpath( $abs ) );
				}
			}

			// feature or plugin not found
			if ( ! realpath( $file['include'] ) ) {
				$file['state']    = 'not_found';
				$file['Priority'] = 999;
				continue;
			}

			$feature_data = self::get_feature_data( $file['include'], false );

			// register only if Plugin definitions are met
			if (
				$feature_data
				&& ! empty( $feature_data['Name'] )
				&& ! empty( $feature_data['Version'] )
				&& ! empty( $feature_data['Author'] )
			) {
				$file          = array_merge( $file, $feature_data );
				$file['state'] = 'valid';
			} else {
				$file['state'] = 'not_valid';
			}

			// default Priority
			if ( ! isset( $file['Priority'] ) || empty( $file['Priority'] ) ) {
				$file['Priority'] = 10;
			}

			// Hidden?
			$file['Hidden'] = isset( $file['Hidden'] ) ? $file['Hidden'] === 'true' : false;

			// Disabled?
			$file['Disabled'] = isset( $file['Disabled'] ) ? $file['Disabled'] === 'true' : false;

			// Forced?
			$file['Forced'] = isset( $file['Forced'] ) ? $file['Forced'] === 'true' : false;
		}
		// debug($files);

		/**
		 * @filter 'greyd_features'
		 *
		 * @param array $files   All integrated Features.
		 *      @property string slug      Feature Slug (Filename or Foldername)
		 *      @property string include   Absolute (or relative to 'plugin_path') Path of the main Feature file to include.
		 *      @property int Priority     1-99 (default: 10).
		 */
		return (array) apply_filters( 'greyd_features', $files );
	}

	/**
	 * Get all active features.
	 *
	 * @return array   The full Settings-Object (global and site).
	 */
	public static function get_saved_features() {

		// delete_option( self::OPTION ); // debug

		$features = array(
			'global' => (array) get_site_option(
				self::OPTION_GLOBAL,
				self::get_default_features( 'global' )
			),
			'site'   => (array) get_option(
				self::OPTION,
				self::get_default_features( 'site' )
			),
		);
		return apply_filters( 'greyd_active_features', $features );
	}

	/**
	 * Get default features
	 */
	public static function get_default_features( $mode = 'site' ) {
		if ( $mode === 'global' ) {
			$features = array(
				'hub'         => 'hub/init.php',
				'connections' => 'connections/init.php',
			);
		} elseif ( $mode === 'site' ) {
			$features = array(
				'dynamic'        => 'dynamic/init.php',
				'posttypes'      => 'posttypes/init.php',
				'popups'         => 'popups/init.php',
				'query'          => 'query/init.php',
				// forced features
				'license'        => 'license/init.php',
				'wizard'         => 'wizard/wizard.php',
				'icons'          => 'icons.php',
				'post-export'    => 'post-export/init.php',
				'pagespeed'      => 'pagespeed.php',
				'cookie-handler' => 'cookie-handler.php',
				'multiselects'   => 'multiselects/init.php',
				'icons'          => 'icons/icons.php',
				// from blocks plugin
				'blocks/init.php',
				'layout/init.php',
				'trigger/init.php',
				'animations/init.php',
				'lottie/init.php'
			);

			if ( Helper::is_greyd_classic() ) {
				$features['accessibility'] = 'accessibility.php';
				$features['comments']      = 'comments.php';
				$features['seo']           = 'seo.php';
				$features['search']        = 'search/init.php';
			}
		}

		return apply_filters( 'greyd_features_default_' . $mode, $features );
	}

	/**
	 * Get all available features files.
	 *
	 * @return array
	 */
	public static function get_feature_files() {

		$files = array();

		$path = wp_normalize_path( GREYD_PLUGIN_PATH . '/features/' );
		if ( ! file_exists( $path ) ) {
			return array();
		}

		// get files
		$results = scandir( $path );
		foreach ( $results as $result ) {
			if ( $result[0] == '.' ) {
				continue;
			}
			if ( is_dir( $path . $result ) ) {
				$init = '';
				if ( file_exists( $path . $result . '/init.php' ) ) {
					$init = '/init.php';
				} elseif ( file_exists( $path . $result . '/index.php' ) ) {
					$init = '/index.php';
				} elseif ( file_exists( $path . $result . '/' . $result . '.php' ) ) {
					$init = '/' . $result . '.php';
				}

				if ( ! empty( $init ) ) {
					array_push(
						$files,
						array(
							'slug'    => $result,
							'include' => $path . $result . $init,
						)
					);
				}
			} elseif ( preg_match( '~\.(php)$~', $result ) ) {
				array_push(
					$files,
					array(
						'slug'    => str_replace( '.php', '', $result ),
						'include' => $path . $result,
					)
				);
			}
		}

		/**
		 * @filter 'greyd_features_files'
		 *
		 * @param array $files   All integrated Features.
		 *      @property string slug      Feature Slug (Filename or Foldername)
		 *      @property string include   Absolute (or relative to 'plugin_path') Path of the main Feature file to include.
		 */
		return (array) apply_filters( 'greyd_features_files', $files );
	}

	/**
	 * Get Data from Feature File.
	 * Same as get_plugin_data, but with extended headers
	 *
	 * @see https://developer.wordpress.org/reference/functions/get_plugin_data/
	 * @see https://developer.wordpress.org/reference/functions/get_file_data/
	 *
	 * @param string $feature_file  The full path the the main feature php.
	 * @param bool   $markup          Render html-markup (default: true)
	 * @param bool   $translate       Translate strings (default: true)
	 * @return array $feature_data  Feature Data/Header Array
	 */
	public static function get_feature_data( $feature_file, $markup = true, $translate = true ) {

		if ( empty( $feature_file ) ) {
			return false;
		}
		if ( realpath( $feature_file ) == false ) {
			return false;
		}

		$default_headers = array(
			// default wp plugin headers
			'Name'        => 'Feature Name',
			'PluginURI'   => 'Plugin URI',
			'Version'     => 'Version',
			'Description' => 'Description',
			'Author'      => 'Author',
			'AuthorURI'   => 'Author URI',
			'TextDomain'  => 'Text Domain',
			'DomainPath'  => 'Domain Path',
			'Network'     => 'Network',
			'RequiresWP'  => 'Requires at least',
			'RequiresPHP' => 'Requires PHP',
			'UpdateURI'   => 'Update URI',
			// additional greyd sub plugin headers
			'RequiresHub' => 'Requires Features',
			'Priority'    => 'Priority',
			'Disabled'    => 'Disabled',
			'Hidden'      => 'Hidden',
			'Forced'      => 'Forced',
		);

		if ( ! function_exists( 'get_plugin_data' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}
		$feature_data = get_file_data( $feature_file, $default_headers );
		$feature_data = _get_plugin_data_markup_translate( $feature_file, $feature_data, $markup, $translate );

		return $feature_data;
	}

	/**
	 * Check if Greyd Blocks Features should be loaded based on Version:
	 * <= 1.14.0: Features are loaded in greyd_blocks
	 * >= 1.15.0: Fearures are loaded here
	 * 
	 * Called before includes of the Features
	 * blocks, layout, trigger, animations and lottie
	 * to decide from where to include
	 * 
	 * @return bool
	 */
	public static function load_blocks_features() {

		// check version constant
		$blocks_version = GREYD_VERSION;
		if ( defined( 'GREYD_BLOCKS_VERSION' ) ) {
			// echo '<pre>'; print_r( GREYD_BLOCKS_VERSION ); echo '</pre>';
			$blocks_version = GREYD_BLOCKS_VERSION;
		}

		// sometimes the constant is defined too late
		if ( $blocks_version != GREYD_VERSION ) {
			// check theme
			// in theme version <= 1.8.5, the greyd_blocks plugin is forced
			// but maybe not yet in the active plugins
			$blocks_forced = false;
			// echo '<pre>'; print_r( wp_get_theme() ); echo '</pre>';
			if ( wp_get_theme()->get('Name') == 'GREYD.SUITE' ) {
				$theme_version = wp_get_theme()->get('Version');
				if ( version_compare( $theme_version, '1.8.5', '<=' ) ) {
					$blocks_forced = true;
				}
			}

			// check plugin
			// if greyd_blocks is forced or active we check the version manually
			// echo '<pre>'; print_r( Helper::active_plugins() ); echo '</pre>';
			if ( $blocks_forced || Helper::is_active_plugin('greyd_blocks/init.php') ) {
				if ( !function_exists( 'get_plugin_data' ) ) {
					require_once ABSPATH.'wp-admin/includes/plugin.php';
				}
				$plugin_data = get_plugin_data( WP_PLUGIN_DIR."/greyd_blocks/init.php" );
				// echo '<pre>'; print_r( $plugin_data ); echo '</pre>';
				$blocks_version = $plugin_data["Version"];
			}
		}
	
		if ( version_compare( $blocks_version, '1.14.0', '<=' ) ) {
			return false;
		}
		return true;
	}


	/*
	=======================================================================
		admin menu
	=======================================================================
	*/

	/**
	 * Add the network submenu item to Greyd.Suite
	 *
	 * @see filter 'greyd_submenu_pages_network'
	 */
	public function add_greyd_submenu_page_network( $submenu_pages ) {
		// debug($submenu_pages);

		if ( Helper::is_greyd_classic() ) {
			return $submenu_pages;
		}

		array_push(
			$submenu_pages,
			array(
				'title'    => self::$page['title'],
				'cap'      => self::$page['cap'],
				'slug'     => self::$page['slug'],
				'callback' => self::$page['callback'],
				'position' => 1,
			)
		);

		return $submenu_pages;
	}

	/**
	 * Add the submenu item to Greyd.Suite
	 *
	 * @see filter 'greyd_submenu_pages'
	 */
	public function add_greyd_submenu_page( $submenu_pages ) {
		// debug($submenu_pages);

		if ( Helper::is_greyd_classic() ) {
			return $submenu_pages;
		}

		array_push(
			$submenu_pages,
			array(
				'page_title' => __( 'Greyd.Suite', 'greyd_hub' ) . ' ' . self::$page['title'],
				'menu_title' => self::$page['title'],
				'cap'        => self::$page['cap'],
				'slug'       => self::$page['slug'],
				'callback'   => self::$page['callback'],
				'position'   => 1,
			)
		);

		return $submenu_pages;
	}


	/*
	=======================================================================
		HANDLE FEATURE SETTINGS
	=======================================================================
	*/

	/**
	 * Prepare the settings mode.
	 *
	 * @return string
	 */
	public function get_mode() {
		// $mode = is_multisite() ? (is_network_admin() ? "network_admin" : "network_site") : "site";
		return is_multisite() && is_network_admin() ? 'global' : 'site';
	}

	/**
	 * Prepare the plugins data.
	 *
	 * @return array
	 */
	public function get_plugins_data() {
		return array(
			'site'   => Helper::active_plugins( 'site' ),
			'global' => Helper::active_plugins( 'global' ),
		);
	}


	/*
	=======================================================================
		FEATURES PAGE
	=======================================================================
	*/

	/**
	 * handle POST Data
	 *
	 * @param array $post_data  Raw $_POST data.
	 */
	public function handle_data( $post_data ) {

		// check nonce
		$nonce_action = 'greyd_features_save';
		$nonce        = isset( $post_data['_wpnonce'] ) ? $post_data['_wpnonce'] : null;
		$mode         = is_multisite() && is_network_admin() ? 'global' : 'site';

		// echo '<pre>';
		// print_r($post_data);
		// echo '</pre>';

		$success = false;
		if ( $nonce && wp_verify_nonce( $nonce, $nonce_action ) ) {

			if ( isset( $post_data['mode'] ) && $post_data['mode'] == $mode ) {

				if ( isset( $post_data['include'] ) ) {
					$settings = array();
					if ( isset( $post_data['feature'] ) ) {
						foreach ( $post_data['feature'] as $feature => $value ) {
							if ( $value != 'on' ) {
								continue;
							}
							if ( isset( $post_data['include'][ $feature ] ) ) {
								// add feature setting
								$settings[ $feature ] = $post_data['include'][ $feature ];
							}
						}
					}
					// debug($settings);

					if ( isset( $post_data['requires'] ) ) {
						foreach ( $settings as $setting => $value ) {

							if ( isset( $post_data['requires'][ $setting ] ) && ! empty( $post_data['requires'][ $setting ] ) ) {
								// feature has requirements
								$dependencies = explode( ', ', $post_data['requires'][ $setting ] );
								$check        = array();
								foreach ( $dependencies as $i => $dependency ) {
									$dependencies[ $i ] = trim( $dependency );
									// check if requirement is met
									if ( isset( $settings[ $dependencies[ $i ] ] ) ||
										( $mode == 'site' && isset( self::get_saved_features()['global'][ $dependencies[ $i ] ] ) ) ) {
										array_push( $check, 'true' );
									} else {
										array_push( $check, 'false' );
									}
								}
								$check = array_unique( $check );
								if ( count( $check ) == 2 || $check[0] == 'false' ) {
									Helper::show_message( sprintf( __( "'%s' Feature disabled (required feature no longer available).", 'greyd_hub' ), $setting ), 'info' );
									unset( $settings[ $setting ] );
								}
							}
						}
					}

					// save data
					// echo '<pre>';
					// print_r($settings);
					// echo '</pre>';

					$success = self::update_features( $mode, $settings );
				}
			}

			if ( $success ) {
				Helper::show_message( __( 'Feature Setup saved.', 'greyd_hub' ), 'success' );
			}
		}
		if ( ! $success ) {
			Helper::show_message( __( 'Feature Setup could not be saved.', 'greyd_hub' ), 'error' );
		}

	}

	/**
	 * Save Feature Settings
	 *
	 * @param string $mode  Type of Setting to save (site or global).
	 * @param array  $value  Settings-Object with all values.
	 * @return bool         True if Option is updated, False otherwise.
	 */
	public static function update_features( $mode, $value ) {
		if ( $mode == 'site' ) {
			return update_option( self::OPTION, $value );
		}
		if ( $mode == 'global' ) {
			return update_site_option( self::OPTION_GLOBAL, $value );
		}
		return false;
	}

	/**
	 * Prepare features data for rendering.
	 *
	 * @return array
	 */
	public static function prepare_features() {

		$path = wp_normalize_path( GREYD_PLUGIN_PATH . '/features/' );
		if ( ! file_exists( $path ) ) {
			return array();
		}

		$files = self::get_all_features();

		// sort by name and Priority
		usort(
			$files,
			function( $a, $b ) {
				// Sort by prio
				$prio_a = intval( $a['Priority'] );
				$prio_b = intval( $b['Priority'] );
				if ( $prio_a > $prio_b ) {
					return 1; // move up
				} elseif ( $prio_a < $prio_b ) {
					return -1; // move down
				}

				// Sort by name
				$name_a = isset( $a['Name'] ) ? $a['Name'] : $a['slug'];
				$name_b = isset( $b['Name'] ) ? $b['Name'] : $b['slug'];
				return strcasecmp( $name_a, $name_b );
			}
		);
		// debug($files);

		// sort features in core|bundled|internal|external
		$features = array(
			'core'     => array(),
			'bundled'  => array(),
			'internal' => array(),
			'external' => array(),
		);

		foreach ( $files as &$file ) {
			if ( strpos( $file['include'], wp_normalize_path( WP_PLUGIN_DIR ) ) === 0 ) {
				// inside wp plugin directory
				if ( strpos( $file['include'], $path ) === 0 ) {
					// is feature inside greyd plugin directory
					$file['include'] = str_replace( $path, '', $file['include'] );
					if ( $file['state'] != 'valid' || $file['Author'] == 'Greyd' ) {
						// Author is Greyd, or feature not valid
						array_push( $features['core'], $file );
					} else {
						// any non-Greyd Author
						array_push( $features['bundled'], $file );
					}
				} else {
					// is wp Plugin - make relative url
					$file['include'] = str_replace( wp_normalize_path( WP_PLUGIN_DIR ), '..', $file['include'] );
					array_push( $features['internal'], $file );
				}
			} else {
				// not inside wp plugin directory
				if ( substr_count( $file['include'], '/' ) > 3 ) {
					// is absolute path
					array_push( $features['external'], $file );
				} else {
					// probably non valid
					if ( strpos( $file['include'], '..' ) !== false ) {
						// if has rel path, it is a non valid Plugin
						array_push( $features['internal'], $file );
					} else {
						// otherwise, it is a non valid Feature
						array_push( $features['core'], $file );
					}
				}
			}
		}
		// debug($features);

		return $features;
	}

	/**
	 * render features page
	 *
	 * @param array $data   All current features.
	 */
	public function render_features_page( $data ) {

		// load data
		$this->data    = self::get_saved_features();
		$this->mode    = $this->get_mode();
		$this->plugins = $this->get_plugins_data();

		// prepare features
		$features = self::prepare_features();

		?>
		<div class="wrap greyd-dashboard greyd-features" style="<?php echo Admin::get_admin_bar_color(); ?>">

			<form method="post">
				<input type="hidden" name="mode" value="<?php echo $this->mode; ?>">

				<h1><?php _e( 'Features', 'greyd_hub' ); ?></h1>
				<h2><?php _e( 'Customize your Greyd.Suite experience and make it your own.', 'greyd_hub' ); ?></h2>
				
				<hr>

				<?php
				wp_nonce_field( 'greyd_features_save' );

				$features_found = false;

				foreach ( $features as $section => $items ) {

					if ( count( $items ) == 0 ) {
						continue;
					}

					$features_found = true;

					$this->render_features_section( $section, $items );

				}

				if ( $features_found ) {
					echo get_submit_button();
				} else {
					echo '<p>' . __( 'No Features available.', 'greyd_hub' ) . '</p>';
				}
				?>
			</form>
		</div>
		<style> #wpcontent { height: auto !important } </style>
		<?php
	}

	/**
	 * Render a features section
	 *
	 * @param string $section   Slug of the section.
	 * @param array  $features  Features.
	 */
	public function render_features_section( $section, $features ) {

		$section_metadata = array(
			'core'     => array(
				'type'        => __( 'Feature', 'greyd_hub' ),
				'headline'    => '',
				'description' => '',
			),
			'bundled'  => array(
				'type'        => __( 'Plugin', 'greyd_hub' ),
				'headline'    => __( "Bundled plugins", 'greyd_hub' ),
				'description' => sprintf(
					__( 'Non-Greyd Plugins, bundled inside the "features" Folder of the %s Plugin.', 'greyd_hub' ),
					$this->config->plugin_name_full
				),
			),
			'internal' => array(
				'type'        => __( 'Plugin', 'greyd_hub' ),
				'headline'    => __( "Recommended plugins", 'greyd_hub' ),
				'description' => __( "Plugins within this WordPress installation.", 'greyd_hub' ),
			),
			'external' => array(
				'type'        => __( "External plugin", 'greyd_hub' ),
				'headline'    => __( 'External Plugins', 'greyd_hub' ),
				'description' => __( 'External Plugins in different DIR on same server (or with execute permission), linked by Filter: "greyd_features_files".', 'greyd_hub' ),
			),
		);

		$metadata = isset( $section_metadata[ $section ] ) ? $section_metadata[ $section ] : array(
			'type'        => __( 'Unknown', 'greyd_hub' ),
			'headline'    => '',
			'description' => '',
		);

		echo "<h2>{$metadata['headline']}</h2>
			<small class='color_light'>{$metadata['description']}</small>
			<div class='greyd-card-grid'>";

		foreach ( $features as $feature ) {
			$this->render_features_item( $feature, $metadata['type'] );
		}

		echo '</div>';
	}

	/**
	 * Render a feature card
	 *
	 * @param array  $feature   Metadata.
	 * @param string $type      Type of the Feature. (default: 'Feature')
	 */
	public function render_features_item( $feature, $type ) {

		// hidden
		if ( isset( $feature['Hidden'] ) && $feature['Hidden'] ) {
			return;
		}

		// invalid
		if ( ! isset( $feature['state'] ) || $feature['state'] != 'valid' ) {
			return;
		}

		$checked    = isset( $this->data[ $this->mode ][ $feature['slug'] ] ) ? 'checked' : '';
		$classNames = array();
		$disabled   = '';
		$info       = '';
		$state      = '';
		$dashicon   = 'info';

		// beta features
		if ( strpos( $feature['Name'], 'Beta' ) !== false ) {
			$info            = __( 'Beta', 'greyd_hub' );
			$state           = 'orange';
			$feature['Name'] = trim(
				str_replace(
					array(
						'(Beta)',
						'[FSE Beta]',
						'Beta',
					),
					'',
					$feature['Name']
				)
			);
		}

		// check if feature has requirements
		if ( ! empty( $feature['RequiresHub'] ) ) {

			$disabled     = 'disabled';
			$dependencies = explode( ',', $feature['RequiresHub'] );
			$check        = array();

			foreach ( $dependencies as $i => $dependency ) {

				$dependencies[ $i ] = trim( $dependency );

				// check if requirement is met
				if (
					isset( $this->data[ $this->mode ][ $dependencies[ $i ] ] ) ||
					( $this->mode == 'site' && isset( $this->data['global'][ $dependencies[ $i ] ] ) )
				) {
					array_push( $check, 'true' );
				} else {
					array_push( $check, 'false' );
				}
			}
			$check = array_unique( $check );

			if ( count( $check ) == 1 && $check[0] == 'true' ) {
				$disabled = '';
				$dashicon = 'saved';
			} elseif ( $checked != '' ) {
				$checked = '';
			}

			$feature['RequiresHub'] = implode( ', ', $dependencies );
			if ( ! empty( $disabled ) ) {
				$info = sprintf( __( 'Requires: %s', 'greyd_hub' ), $feature['RequiresHub'] );
			}
		}

		// check if feature is globally active
		if ( $this->mode == 'site' && isset( $this->data['global'][ $feature['slug'] ] ) ) {
			$checked  = 'checked';
			$disabled = 'disabled';
			$info     = __( 'Globally enabled in Network Admin', 'greyd_hub' );
			$dashicon = 'saved';
		}

		// check if feature is active as plugin
		$maybe_plugin = str_replace( '../', '', $feature['include'] );
		if (
			in_array( $maybe_plugin, $this->plugins[ $this->mode ] ) ||
			( $this->mode == 'site' && in_array( $maybe_plugin, $this->plugins['global'] ) )
		) {
			// plugin is enabled
			$checked  = 'checked';
			$disabled = 'disabled';
			$dashicon = 'saved';

			if ( in_array( $maybe_plugin, $this->plugins['global'] ) ) {
				$info = __( 'Sitewide enabled in Network Plugins', 'greyd_hub' );
			} else {
				$info = __( "Enabled in plugins", 'greyd_hub' );
			}
		}

		if ( $feature['Disabled'] ) {
			$info     = __( 'in Development', 'greyd_hub' );
			$dashicon = 'editor-code';
		}

		// forced
		if ( $feature['Forced'] ) {
			$checked      = 'checked';
			$classNames[] = 'is-hidden';
		}

		echo "<label class='greyd-card greyd-feature " . implode( ' ', $classNames ) . "' for='feature-{$feature["slug"]}' data-disabled=" . ( $feature['Disabled'] ? 'true' : '' ) . ">
			<div>
			<span class='components-form-toggle'>
				<input type='hidden' name='include[{$feature["slug"]}]' value='{$feature["include"]}'>
				<input type='hidden' name='requires[{$feature["slug"]}]' value='{$feature["RequiresHub"]}'>
				<input class='components-form-toggle__input' type='checkbox' name='feature[{$feature["slug"]}]' id='feature-{$feature["slug"]}' {$checked} {$disabled}>
				<span class='components-form-toggle__track'></span>
				<span class='components-form-toggle__thumb'></span>
			</span>
			</div>
			<div>
				<h5 class='greyd-card--title'>{$feature["Name"]}</h5>
				<p class='greyd-card--desc'>{$feature["Description"]}</p>
				" . (
					empty( $info ) ? '' : "<p class='greyd-card--info {$state}'><span class='dashicons dashicons-{$dashicon}'></span>{$info}</p>" ) . '
			</div>
		</label>';
	}

	/**
	 * Add test features for development fixes
	 *
	 * @param array $files
	 *
	 * @return array
	 */
	public function add_test_features( $files ) {

		// test: add normal (internal) plugin
		array_push(
			$files,
			array(
				// with absolute path
				'slug'    => 'advanced-wp-reset',
				'include' => wp_normalize_path( WP_PLUGIN_DIR . '/advanced-wp-reset/advanced-wp-reset.php' ),
			)
		);
		array_push(
			$files,
			array(
				// with relative path to plugin_dir
				'slug'    => 'enable-media-replace',
				'include' => '../enable-media-replace/enable-media-replace.php',
			)
		);
		array_push(
			$files,
			array(
				// with strange relative path to plugin_dir
				'slug'     => 'debug-bar',
				'include'  => '../../plugins/debug-bar/debug-bar.php',
				'Priority' => 1,
			)
		);

		// test: add remote (external) plugin
		array_push(
			$files,
			array(
				// dev: with correct path on dev server -> works, but very unstable
				'slug'    => 'editorplus',
				'include' => 'D:/web/greyd/web_rc2/wp-content/plugins/editorplus/index.php',
			)
		);

		// test: non valid
		array_push(
			$files,
			array(
				// feature not found
				'slug'    => 'mystery-feature',
				'include' => 'mystery-feature.php',
			)
		);
		array_push(
			$files,
			array(
				// plugin not found
				'slug'    => 'mystery-plugin',
				'include' => '../mystery-plugin/init.php',
			)
		);
		array_push(
			$files,
			array(
				// external plugin not found -> external urls are not allowed
				'slug'    => 'external-plugin-sample',
				'include' => 'http://mf23.net/exchange/external-plugin-sample/external-plugin-sample.php',
			)
		);

		// debug($files);
		return $files;
	}

	/**
	 * Add test features for development fixes
	 *
	 * @param array $files
	 *
	 * @return array
	 */
	public function add_core_plugins( $files ) {

		array_push(
			$files,
			array(
				'slug'    => 'greyd_tp_forms',
				'include' => '../greyd_tp_forms/init.php',
			)
		);

		// array_push(
		// 	$files,
		// 	array(
		// 		'slug'    => 'greyd_blocks',
		// 		'include' => '../greyd_blocks/init.php',
		// 	)
		// );

		array_push(
			$files,
			array(
				'slug'    => 'greyd_globalcontent',
				'include' => '../greyd_globalcontent/init.php',
			)
		);

		// debug($files);
		return $files;
	}


}
