<?php
/**
 * Responsible for the recipe template editor.
 *
 * @link       https://bootstrapped.ventures
 * @since      4.0.0
 *
 * @package    WP_Recipe_Maker
 * @subpackage WP_Recipe_Maker/includes/public
 */

/**
 * Responsible for the recipe template editor.
 *
 * @since      4.0.0
 * @package    WP_Recipe_Maker
 * @subpackage WP_Recipe_Maker/includes/public
 * @author     Brecht Vandersmissen <brecht@bootstrapped.ventures>
 */
class WPRM_Template_Editor {
	/**
	 * Register actions and filters.
	 *
	 * @since	4.0.0
	 */
	public static function init() {
		add_action( 'admin_menu', array( __CLASS__, 'add_submenu_page' ), 20 );
		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_hubbub_preview_styles' ), 20 );
	}

	/**
	 * Add the template editor submenu to the WPRM menu.
	 *
	 * @since	4.0.0
	 */
	public static function add_submenu_page() {
		add_submenu_page( 'wprecipemaker', __( 'WPRM Template Editor', 'wp-recipe-maker' ), __( 'Template Editor', 'wp-recipe-maker' ), 'manage_options', 'wprm_template_editor', array( __CLASS__, 'template_editor_page_template' ) );
	}

	/**
	 * Get the template for the template editor page.
	 *
	 * @since	4.0.0
	 */
	public static function template_editor_page_template() {
		self::localize_admin_template();
		echo '<div id="wprm-template" class="wrap">Loading...</div>';
	}

	/**
	 * Enqueue Hubbub frontend styles on the template editor page so Save This and Action Buttons previews are styled.
	 *
	 * @since 10.4.0
	 */
	public static function enqueue_hubbub_preview_styles() {
		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : false;
		if ( ! $screen || 'wp-recipe-maker_page_wprm_template_editor' !== $screen->id ) {
			return;
		}
		if ( ! self::is_hubbub_active() ) {
			return;
		}

		// Action Buttons are block-based and rely on core block styling as well.
		wp_enqueue_style( 'wp-block-library' );
		wp_enqueue_style( 'wp-block-library-theme' );

		// Load theme preset CSS and only the global button rules needed for Action Buttons.
		// Avoid full global styles because those can interfere with the template editor admin UI.
		if ( function_exists( 'wp_get_global_stylesheet' ) ) {
			$preset_css = wp_get_global_stylesheet( array( 'variables', 'presets' ) );
			$button_css = self::get_global_button_css_for_preview();

			if ( $preset_css ) {
				wp_register_style( 'wprm-template-editor-global-presets', false, array(), WPRM_VERSION );
				wp_enqueue_style( 'wprm-template-editor-global-presets' );
				wp_add_inline_style( 'wprm-template-editor-global-presets', $preset_css );
			}

			if ( $button_css ) {
				wp_register_style( 'wprm-template-editor-global-button-styles', false, array(), WPRM_VERSION );
				wp_enqueue_style( 'wprm-template-editor-global-button-styles' );
				wp_add_inline_style( 'wprm-template-editor-global-button-styles', $button_css );
			}
		}

		// Use Hubbub's registered style if available.
		if ( function_exists( 'wp_styles' ) && isset( wp_styles()->registered['dpsp-frontend-style-pro'] ) ) {
			wp_enqueue_style( 'dpsp-frontend-style-pro' );
		} else {
			$url = self::get_hubbub_frontend_style_url();
			if ( '' !== $url ) {
				wp_enqueue_style( 'wprm-hubbub-preview', $url, array(), WPRM_VERSION );
			}
		}

		// Action Buttons use a separate block style file in Hubbub.
		if ( function_exists( 'wp_styles' ) && isset( wp_styles()->registered['social-pug-action-button-style'] ) ) {
			wp_enqueue_style( 'social-pug-action-button-style' );
		} else {
			$url = self::get_hubbub_action_buttons_style_url();
			if ( '' !== $url ) {
				wp_enqueue_style( 'wprm-hubbub-action-buttons-preview', $url, array(), WPRM_VERSION );
			}
		}
	}

	/**
	 * Get the Hubbub (Social Pug) frontend stylesheet URL for template editor preview.
	 *
	 * @since 10.4.0
	 * @return string Style URL or empty string if not found.
	 */
	private static function get_hubbub_frontend_style_url() {
		$url   = '';
		$base  = self::get_hubbub_plugin_base_url();
		if ( '' !== $base ) {
			$url = $base . 'assets/dist/style-frontend-pro.css';
		}

		return apply_filters( 'wprm_template_editor_hubbub_preview_style_url', $url );
	}

	/**
	 * Get the Hubbub Action Buttons block stylesheet URL for template editor preview.
	 *
	 * @since 10.4.0
	 * @return string Style URL or empty string if not found.
	 */
	private static function get_hubbub_action_buttons_style_url() {
		$url  = '';
		$base = self::get_hubbub_plugin_base_url();
		if ( '' !== $base ) {
			$url = $base . 'inc/admin/block-action-buttons/style-index.css';
		}

		return apply_filters( 'wprm_template_editor_hubbub_action_buttons_style_url', $url );
	}

	/**
	 * Get only button-related global styles for template preview.
	 *
	 * @since 10.4.0
	 * @return string Button-related CSS.
	 */
	private static function get_global_button_css_for_preview() {
		if ( ! function_exists( 'wp_get_global_stylesheet' ) ) {
			return '';
		}

		$css = wp_get_global_stylesheet( array( 'styles' ) );
		if ( ! $css ) {
			return '';
		}

		$button_css = '';
		$patterns = array(
			'/[^{}]*\.wp-element-button[^{}]*\{[^{}]*\}/m',
			'/[^{}]*\.wp-block-button__link[^{}]*\{[^{}]*\}/m',
		);

		foreach ( $patterns as $pattern ) {
			if ( preg_match_all( $pattern, $css, $matches ) ) {
				$button_css .= implode( "\n", $matches[0] ) . "\n";
			}
		}

		return trim( $button_css );
	}

	/**
	 * Get the Hubbub plugin base URL.
	 *
	 * @since 10.4.0
	 * @return string Plugin base URL or empty string if not found.
	 */
	private static function get_hubbub_plugin_base_url() {
		$base   = '';
		$active = get_option( 'active_plugins', array() );

		foreach ( $active as $plugin ) {
			if ( false !== strpos( $plugin, 'social-pug' ) || false !== strpos( $plugin, 'hubbub' ) ) {
				$base = plugin_dir_url( WP_PLUGIN_DIR . '/' . $plugin );
				break;
			}
		}

		return $base;
	}

	/**
	 * Check if Hubbub (Social Pug) is active.
	 *
	 * @since 10.4.0
	 * @return bool
	 */
	private static function is_hubbub_active() {
		// Main Hubbub plugin class.
		if ( class_exists( 'Social_Pug' ) ) {
			return true;
		}

		// Namespaced classes used by recent versions.
		if ( class_exists( 'Mediavine\\Grow\\Asset_Loader' ) || class_exists( 'Mediavine\\Grow\\Activation' ) ) {
			return true;
		}

		// Fallback check for active plugins list.
		$active_plugins = get_option( 'active_plugins', array() );
		foreach ( $active_plugins as $plugin ) {
			if ( false !== strpos( $plugin, 'social-pug' ) || false !== strpos( $plugin, 'hubbub' ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Localize JS for the template editor page.
	 *
	 * @since	5.8.0
	 */
	public static function localize_admin_template() {
		// Get all modern templates.
		$modern_templates = array();
		$templates = WPRM_Template_Manager::get_templates();

		foreach ( $templates['modern'] as $template ) {
			$modern_templates[ $template['slug'] ] = self::prepare_template_for_editor( $template );
		}

		wp_localize_script( 'wprm-admin-template', 'wprm_admin_template', array(
			'templates' => $modern_templates,
			'default_template_usages' => self::get_default_template_usages( $modern_templates ),
			'shortcodes' => WPRM_Template_Shortcodes::get_shortcodes(),
			'icons' => WPRM_Icon::get_all(),
			'thumbnail_sizes' => get_intermediate_image_sizes(),
			'preview_recipe' => WPRM_Settings::get( 'template_editor_preview_recipe' ),
			'undo_redo_history' => WPRM_Settings::get( 'template_editor_undo_redo_history' ),
		) );
	}

	/**
	 * Get a map of template slug to default usage labels.
	 *
	 * @since	10.5.0
	 * @param	array $modern_templates Modern templates available in the editor.
	 */
	private static function get_default_template_usages( $modern_templates ) {
		$usages = array();

		// Contexts resolved through template manager.
		self::add_template_usage_by_type( $usages, $modern_templates, 'single', 'food', __( 'Food Recipe Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'single', 'howto', __( 'How-to Instructions Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'single', 'other', __( 'Other Recipe Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'archive', 'food', __( 'Archive Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'amp', 'food', __( 'AMP Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'feed', 'food', __( 'RSS Feed Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'print', 'food', __( 'Food Recipe Print Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'print', 'howto', __( 'How-to Instructions Print Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'print', 'other', __( 'Other Recipe Print Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'pdf', 'food', __( 'Food Recipe PDF Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'pdf', 'howto', __( 'How-to Instructions PDF Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'pdf', 'other', __( 'Other Recipe PDF Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'snippet', 'food', __( 'Food Recipe Snippet Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'snippet', 'howto', __( 'How-to Instructions Snippet Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'snippet', 'other', __( 'Other Recipe Snippet Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'roundup', 'food', __( 'Food Recipe Roundup Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'roundup', 'howto', __( 'How-to Instructions Roundup Template', 'wp-recipe-maker' ) );
		self::add_template_usage_by_type( $usages, $modern_templates, 'roundup', 'other', __( 'Other Recipe Roundup Template', 'wp-recipe-maker' ) );

		$collections_active = WPRM_Addons::is_active( 'elite' );
		if ( $collections_active ) {
			self::add_template_usage_by_type( $usages, $modern_templates, 'print-collection', 'food', __( 'Recipe Collections Print Template (Food)', 'wp-recipe-maker' ) );
			self::add_template_usage_by_type( $usages, $modern_templates, 'print-collection', 'howto', __( 'Recipe Collections Print Template (How-to)', 'wp-recipe-maker' ) );
			self::add_template_usage_by_type( $usages, $modern_templates, 'print-collection', 'other', __( 'Recipe Collections Print Template (Other)', 'wp-recipe-maker' ) );
		}

		if ( WPRM_Addons::is_active( 'premium' ) ) {
			self::add_template_usage_by_type( $usages, $modern_templates, 'favorites-list', 'food', __( 'Favorites Template (Food)', 'wp-recipe-maker' ) );
			self::add_template_usage_by_type( $usages, $modern_templates, 'favorites-list', 'howto', __( 'Favorites Template (How-to)', 'wp-recipe-maker' ) );
			self::add_template_usage_by_type( $usages, $modern_templates, 'favorites-list', 'other', __( 'Favorites Template (Other)', 'wp-recipe-maker' ) );
		}

		// Contexts resolved directly from setting values.
		$manual_contexts = array(
			array(
				'setting' => 'default_print_template_admin',
				'label' => __( 'Admin Print Template', 'wp-recipe-maker' ),
				'aliases' => array(
					'default_recipe_template' => 'default_recipe_template_modern',
				),
			),
			array(
				'setting' => 'post_type_archive_output_template',
				'label' => __( 'Archive Pages Template', 'wp-recipe-maker' ),
				'aliases' => array(),
			),
		);

		if ( $collections_active ) {
			$manual_contexts[] = array(
				'setting' => 'recipe_collections_template_modern',
				'label' => __( 'Recipe Collections Template (Food)', 'wp-recipe-maker' ),
				'aliases' => array(
					'default_recipe_template' => 'default_recipe_template_modern',
				),
			);
			$manual_contexts[] = array(
				'setting' => 'recipe_collections_howto_template_modern',
				'label' => __( 'Recipe Collections Template (How-to)', 'wp-recipe-maker' ),
				'aliases' => array(
					'default_recipe_template' => 'default_howto_recipe_template_modern',
				),
			);
			$manual_contexts[] = array(
				'setting' => 'recipe_collections_other_template_modern',
				'label' => __( 'Recipe Collections Template (Other)', 'wp-recipe-maker' ),
				'aliases' => array(
					'default_recipe_template' => 'default_other_recipe_template_modern',
				),
			);
		}
		if ( WPRM_Addons::is_active( 'premium' ) ) {
			$manual_contexts[] = array(
				'setting' => 'favorite_recipes_template_modern',
				'label' => __( 'Favorites Template (Food)', 'wp-recipe-maker' ),
				'aliases' => array(
					'default_recipe_template' => 'default_recipe_template_modern',
				),
			);
			$manual_contexts[] = array(
				'setting' => 'favorite_recipes_howto_template_modern',
				'label' => __( 'Favorites Template (How-to)', 'wp-recipe-maker' ),
				'aliases' => array(
					'default_recipe_template' => 'default_howto_recipe_template_modern',
				),
			);
			$manual_contexts[] = array(
				'setting' => 'favorite_recipes_other_template_modern',
				'label' => __( 'Favorites Template (Other)', 'wp-recipe-maker' ),
				'aliases' => array(
					'default_recipe_template' => 'default_other_recipe_template_modern',
				),
			);
		}

		foreach ( $manual_contexts as $context ) {
			$slug = self::get_template_slug_for_setting( $context['setting'], $modern_templates, $context['aliases'] );
			self::add_template_usage( $usages, $modern_templates, $slug, $context['label'] );
		}

		return $usages;
	}

	/**
	 * Add usage label for template resolved by manager type.
	 *
	 * @since	10.5.0
	 * @param	array  $usages           Map of slug to labels.
	 * @param	array  $modern_templates Modern templates available in the editor.
	 * @param	string $type             Template type.
	 * @param	string $recipe_type      Recipe type.
	 * @param	string $label            Usage label.
	 */
	private static function add_template_usage_by_type( &$usages, $modern_templates, $type, $recipe_type, $label ) {
		$template = WPRM_Template_Manager::get_template_by_type( $type, $recipe_type );
		$slug = $template && isset( $template['slug'] ) ? $template['slug'] : false;

		self::add_template_usage( $usages, $modern_templates, $slug, $label );
	}

	/**
	 * Add usage label for a template slug.
	 *
	 * @since	10.5.0
	 * @param	array  $usages           Map of slug to labels.
	 * @param	array  $modern_templates Modern templates available in the editor.
	 * @param	mixed  $slug             Template slug.
	 * @param	string $label            Usage label.
	 */
	private static function add_template_usage( &$usages, $modern_templates, $slug, $label ) {
		if ( ! self::is_valid_usage_slug( $slug, $modern_templates ) ) {
			return;
		}

		if ( ! isset( $usages[ $slug ] ) ) {
			$usages[ $slug ] = array();
		}

		if ( ! in_array( $label, $usages[ $slug ], true ) ) {
			$usages[ $slug ][] = $label;
		}
	}

	/**
	 * Resolve setting value to a valid template slug.
	 *
	 * @since	10.5.0
	 * @param	string $setting_id       Setting ID to resolve.
	 * @param	array  $modern_templates Modern templates available in the editor.
	 * @param	array  $aliases          Map of alias value to setting IDs.
	 * @param	array  $visited          Already visited setting IDs.
	 */
	private static function get_template_slug_for_setting( $setting_id, $modern_templates, $aliases = array(), $visited = array() ) {
		if ( in_array( $setting_id, $visited, true ) ) {
			return false;
		}

		$visited[] = $setting_id;

		$value = WPRM_Settings::get( $setting_id );
		$slug = self::resolve_template_slug_value( $value, $modern_templates, $aliases, $visited );
		if ( $slug ) {
			return $slug;
		}

		$default_value = WPRM_Settings::get_default( $setting_id );
		if ( $default_value !== $value ) {
			$slug = self::resolve_template_slug_value( $default_value, $modern_templates, $aliases, $visited );
			if ( $slug ) {
				return $slug;
			}
		}

		return false;
	}

	/**
	 * Resolve a setting value to a valid template slug.
	 *
	 * @since	10.5.0
	 * @param	mixed $value             Setting value.
	 * @param	array $modern_templates  Modern templates available in the editor.
	 * @param	array $aliases           Map of alias value to setting IDs.
	 * @param	array $visited           Already visited setting IDs.
	 */
	private static function resolve_template_slug_value( $value, $modern_templates, $aliases = array(), $visited = array() ) {
		if ( ! $value ) {
			return false;
		}

		if ( isset( $aliases[ $value ] ) ) {
			return self::get_template_slug_for_setting( $aliases[ $value ], $modern_templates, $aliases, $visited );
		}

		return self::is_valid_usage_slug( $value, $modern_templates ) ? $value : false;
	}

	/**
	 * Check if slug exists as an available modern template.
	 *
	 * @since	10.5.0
	 * @param	mixed $slug             Template slug.
	 * @param	array $modern_templates Modern templates available in the editor.
	 */
	private static function is_valid_usage_slug( $slug, $modern_templates ) {
		if ( ! $slug || ! isset( $modern_templates[ $slug ] ) ) {
			return false;
		}

		$premium_active = WPRM_Addons::is_active( 'premium' ) || WPRM_Addons::is_active( 'pro' ) || WPRM_Addons::is_active( 'elite' );
		if ( $modern_templates[ $slug ]['premium'] && ! $premium_active ) {
			return false;
		}

		return true;
	}

	/**
	 * Prepare a template for the template editor.
	 *
	 * @since	4.0.0
	 * @param	mixed $template Template to prepare.
	 */
	public static function prepare_template_for_editor( $template ) {
		$template['style'] = self::extract_style_with_properties( $template );

		// Fix deprecated shortcodes.
		$template['html'] = str_ireplace( '[wprm-recipe-author-container', 			'[wprm-recipe-author label_container="1"', $template['html'] );
		$template['html'] = str_ireplace( '[wprm-recipe-cost-container', 			'[wprm-recipe-cost label_container="1"', $template['html'] );
		$template['html'] = str_ireplace( '[wprm-recipe-custom-field-container', 	'[wprm-recipe-custom-field label_container="1"', $template['html'] );
		$template['html'] = str_ireplace( '[wprm-recipe-nutrition-container', 		'[wprm-recipe-nutrition label_container="1"', $template['html'] );
		$template['html'] = str_ireplace( '[wprm-recipe-servings-container', 		'[wprm-recipe-servings label_container="1"', $template['html'] );
		$template['html'] = str_ireplace( '[wprm-recipe-tag-container', 			'[wprm-recipe-tag label_container="1"', $template['html'] );
		$template['html'] = str_ireplace( '[wprm-recipe-time-container', 			'[wprm-recipe-time label_container="1"', $template['html'] );

		// Migrate tags and times container.
		$pattern = get_shortcode_regex( array( 'wprm-recipe-tags-container' ) );
		if ( preg_match_all( '/' . $pattern . '/s', $template['html'], $matches ) && array_key_exists( 2, $matches ) ) {
			foreach ( $matches[2] as $key => $value ) {
				if ( 'wprm-recipe-tags-container' === $value ) {
					$old_shortcode = $matches[0][ $key ];
					
					$new_shortcode = $old_shortcode;
					$new_shortcode = str_ireplace( '[wprm-recipe-tags-container', '[wprm-recipe-meta-container fields="tags"', $new_shortcode );
					$new_shortcode = str_ireplace( ' separator=', ' tag_separator=', $new_shortcode );

					$template['html'] = str_ireplace( $old_shortcode, $new_shortcode, $template['html'] );
				}
			}
		}

		$pattern = get_shortcode_regex( array( 'wprm-recipe-times-container' ) );
		if ( preg_match_all( '/' . $pattern . '/s', $template['html'], $matches ) && array_key_exists( 2, $matches ) ) {
			foreach ( $matches[2] as $key => $value ) {
				if ( 'wprm-recipe-times-container' === $value ) {
					$old_shortcode = $matches[0][ $key ];
					
					$new_shortcode = $old_shortcode;
					$new_shortcode = str_ireplace( '[wprm-recipe-times-container', '[wprm-recipe-meta-container fields="times"', $new_shortcode );
					$new_shortcode = str_ireplace( ' shorthand=', ' time_shorthand=', $new_shortcode );
					$new_shortcode = str_ireplace( ' icon_prep=', ' icon_prep_time=', $new_shortcode );
					$new_shortcode = str_ireplace( ' icon_cook=', ' icon_cook_time=', $new_shortcode );
					$new_shortcode = str_ireplace( ' icon_custom=', ' icon_custom_time=', $new_shortcode );
					$new_shortcode = str_ireplace( ' icon_total=', ' icon_total_time=', $new_shortcode );
					$new_shortcode = str_ireplace( ' label_prep=', ' label_prep_time=', $new_shortcode );
					$new_shortcode = str_ireplace( ' label_cook=', ' label_cook_time=', $new_shortcode );
					$new_shortcode = str_ireplace( ' label_total=', ' label_total_time=', $new_shortcode );

					$template['html'] = str_ireplace( $old_shortcode, $new_shortcode, $template['html'] );
				}
			}
		}
		
		return $template;
	}

	/**
	 * Extract the style and optional properties from a template stylesheet.
	 *
	 * @since	4.0.0
	 * @param	mixed $template Template to extract from.
	 */
	private static function extract_style_with_properties( $template ) {
		$css = WPRM_Template_Manager::get_template_css( $template, false );

		// Find properties in CSS.
		$properties = array();

		preg_match_all( "/:([^:;]+);\s*\/\*([^*]*)\*+([^\/*][^*]*\*+)*\//im", $css, $matches );
		foreach ( $matches[2] as $index => $comment ) {
			$value = trim( $matches[1][ $index ] );
			$comment = trim( $comment );
			
			// Check if it's one of our comments.
			if ( 'wprm_' === substr( $comment, 0, 5 ) ) {
				$parts = explode( ' ', $comment );

				// First part should be variable name.
				$id = substr( $parts[0], 5 );
				unset( $parts[0] );

				if ( $id ) {
					$property = array(
						'id' => $id,
						'name' => ucwords( str_replace( '_', ' ', $id ) ),
						'default' => $value,
						'value' => $value,
					);

					// Check if there are any parts left.
					foreach ( $parts as $part ) {
						$pieces = explode( '=', $part );

						if ( 2 === count( $pieces ) ) {
							if ( ! array_key_exists( $pieces[0], $property ) ) {
								$property[ $pieces[0] ] = $pieces[1];
							}
						}
					}

					// Add to properties.
					$properties[ $id ] = $property;

					// Replace with variable in CSS.
					$css = str_ireplace( $matches[0][ $index ], ': %wprm_' . $id .'%;', $css );
				}
			}
		}

		return array(
			'properties' => $properties,
			'css' => $css,
		);
	}
}

WPRM_Template_Editor::init();
