<?php
/**
 * RSM Suite admin menu clustering.
 *
 * Each rsm-* plugin registers its submenus under a shared parent so they
 * appear together in wp-admin regardless of install order.
 *
 * @package Rsmspark_Suite
 */

defined( 'ABSPATH' ) || exit;

/**
 * Suite menu coordinator.
 */
final class Rsmspark_Suite {

	/**
	 * Parent menu slug shared by all RSM plugins.
	 */
	public const MENU_SLUG = 'rsmspark-suite';

	/**
	 * Menu position (after Comments, before appearance-related items).
	 */
	public const MENU_POSITION = 26;

	/**
	 * Registered plugin metadata for ordering submenus.
	 *
	 * @var array<string, array{label: string, order: int}>
	 */
	private static $plugins = array();

	/**
	 * Whether the parent menu has been registered this request.
	 *
	 * @var bool
	 */
	private static $parent_registered = false;

	/**
	 * Optional image URL for the parent menu icon (e.g. set by RSM Spark).
	 *
	 * @var string
	 */
	private static $parent_menu_icon = '';

	/**
	 * Whether suite hooks are registered.
	 *
	 * @var bool
	 */
	private static $initialized = false;

	/**
	 * Submenu slugs to reorder once all plugins have registered menus.
	 *
	 * @var array<string, array<int, string>>
	 */
	private static $submenu_orders = array();

	/**
	 * Hooks.
	 *
	 * @return void
	 */
	public static function init() {
		if ( self::$initialized ) {
			return;
		}

		self::$initialized = true;

		add_action( 'admin_menu', array( __CLASS__, 'register_parent' ), 5 );
		add_action( 'admin_menu', array( __CLASS__, 'finalize_submenu_orders' ), 1002 );
		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_menu_assets' ) );
		add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_assets' ) );
	}

	/**
	 * Parent menu slug for submenu registration.
	 *
	 * @return string
	 */
	public static function parent_slug() {
		return self::MENU_SLUG;
	}

	/**
	 * Register a plugin in the suite (call before admin_menu priority 10).
	 *
	 * @param string $id    Plugin identifier (e.g. menu, forms).
	 * @param string $label Short admin label (e.g. Menu, Forms).
	 * @param int    $order Sort order among plugins.
	 * @return void
	 */
	public static function register_plugin( $id, $label, $order = 50 ) {
		self::$plugins[ $id ] = array(
			'label' => $label,
			'order' => (int) $order,
		);
	}

	/**
	 * Set a custom image URL for the top-level RSM admin menu icon.
	 *
	 * @param string $icon_url Absolute URL to a PNG, JPG, SVG, or GIF.
	 * @return void
	 */
	public static function set_parent_menu_icon( $icon_url ) {
		self::$parent_menu_icon = (string) $icon_url;
	}

	/**
	 * Create the shared top-level RSM menu if not already present.
	 *
	 * @return void
	 */
	public static function register_parent() {
		if ( self::$parent_registered ) {
			return;
		}

		$menu_icon = '' !== self::$parent_menu_icon ? self::$parent_menu_icon : 'dashicons-admin-plugins';

		add_menu_page(
			__( 'RSM', 'rsm-content-widgets' ),
			__( 'RSM', 'rsm-content-widgets' ),
			'edit_posts',
			self::MENU_SLUG,
			array( __CLASS__, 'render_welcome' ),
			$menu_icon,
			self::MENU_POSITION
		);

		self::$parent_registered = true;

		// Hide duplicate first submenu (same slug as parent).
		add_action(
			'admin_menu',
			static function () {
				global $submenu;
				if ( isset( $submenu[ self::MENU_SLUG ][0] ) ) {
					unset( $submenu[ self::MENU_SLUG ][0] );
				}
			},
			999
		);
	}

	/**
	 * Strip legacy em-dash wrappers from section heading labels.
	 *
	 * @param string $label Raw label.
	 * @return string
	 */
	private static function clean_section_heading_label( $label ) {
		$label = trim( (string) $label );
		$label = preg_replace( '/^\s*[\-–—]+\s*/u', '', $label );
		$label = preg_replace( '/\s*[\-–—]+\s*$/u', '', $label );
		return trim( $label );
	}

	/**
	 * Add a section divider submenu for visual grouping.
	 *
	 * @param string $plugin_id Plugin id passed to register_plugin().
	 * @return void
	 */
	public static function add_section_heading( $plugin_id ) {
		if ( ! isset( self::$plugins[ $plugin_id ] ) ) {
			return;
		}

		$label = self::clean_section_heading_label( (string) self::$plugins[ $plugin_id ]['label'] );

		add_submenu_page(
			self::MENU_SLUG,
			$label,
			$label,
			'edit_posts',
			'rsmspark-suite-' . $plugin_id,
			static function () use ( $plugin_id ) {
				/**
				 * Fires when a user lands on a plugin section heading in the RSM menu.
				 *
				 * @param string $plugin_id Plugin identifier.
				 */
				do_action( 'rsmspark_suite_section_redirect', $plugin_id );
				wp_safe_redirect( admin_url( 'admin.php?page=' . self::MENU_SLUG ) );
				exit;
			}
		);
	}

	/**
	 * Default welcome screen when visiting RSM with no plugin redirect.
	 *
	 * @return void
	 */
	public static function render_welcome() {
		?>
		<div class="wrap rsmspark-suite-admin rsmspark-suite-ui">
			<h1><?php esc_html_e( 'RSM', 'rsm-content-widgets' ); ?></h1>
			<p><?php esc_html_e( 'RSMSpark WordPress plugins. Use Spark to install suite plugins, or choose a section from the menu to manage Menu, Forms, Calendar, Popups, Directory, Careers, Invoice, Features, and more.', 'rsm-content-widgets' ); ?></p>
			<?php if ( ! empty( self::$plugins ) ) : ?>
				<ul class="rsmspark-suite-plugin-list">
					<?php
					uasort(
						self::$plugins,
						static function ( $a, $b ) {
							return $a['order'] <=> $b['order'];
						}
					);
					foreach ( self::$plugins as $id => $meta ) :
						?>
						<li><strong><?php echo esc_html( $meta['label'] ); ?></strong></li>
					<?php endforeach; ?>
				</ul>
			<?php endif; ?>
		</div>
		<?php
	}

	/**
	 * Sidebar section heading styles (all wp-admin screens).
	 *
	 * @return void
	 */
	public static function enqueue_menu_assets() {
		// Spark ships equivalent menu CSS/JS; skip duplicate enqueue (often 404 when an older plugin wins bootstrap).
		if ( defined( 'RSM_SPARK_VERSION' ) || defined( 'RSMSPARK_SPARK_VERSION' ) ) {
			return;
		}

		$css_file = 'assets/css/admin-suite-menu.css';
		$asset    = function_exists( 'rsmspark_suite_find_shared_asset' )
			? rsmspark_suite_find_shared_asset( $css_file )
			: null;

		if ( null === $asset ) {
			$css_url = function_exists( 'rsmspark_suite_shared_asset_url' )
				? rsmspark_suite_shared_asset_url( $css_file )
				: '';

			if ( '' === $css_url ) {
				return;
			}

			wp_enqueue_style( 'rsmspark-suite-admin-menu', $css_url, array(), RSMSPARK_SUITE_VERSION );
		} else {
			wp_enqueue_style( 'rsmspark-suite-admin-menu', $asset['url'], array(), RSMSPARK_SUITE_VERSION );
		}

		if ( ! wp_style_is( 'rsmspark-suite-admin-menu', 'enqueued' ) ) {
			return;
		}

		$js_file = 'assets/js/admin-suite-menu.js';
		$js_url  = function_exists( 'rsmspark_suite_shared_asset_url' )
			? rsmspark_suite_shared_asset_url( $js_file, $asset['dir'] ?? '' )
			: '';

		if ( '' === $js_url && function_exists( 'rsmspark_suite_find_shared_asset' ) ) {
			$js_asset = rsmspark_suite_find_shared_asset( $js_file );
			$js_url   = null !== $js_asset ? $js_asset['url'] : '';
		}

		if ( '' === $js_url ) {
			return;
		}

		wp_enqueue_script(
			'rsmspark-suite-admin-menu',
			$js_url,
			array( 'jquery' ),
			RSMSPARK_SUITE_VERSION,
			true
		);
	}

	/**
	 * Enqueue shared admin styles on RSM screens.
	 *
	 * @param string $hook Current admin page hook.
	 * @return void
	 */
	public static function enqueue_assets( $hook ) {
		if ( ! self::is_rsm_admin_screen( $hook ) ) {
			return;
		}

		wp_enqueue_style(
			'rsmspark-suite-admin',
			RSMSPARK_SUITE_URL . 'assets/css/admin-suite.css',
			array(),
			RSMSPARK_SUITE_VERSION
		);

		self::enqueue_button_styles();
	}

	/**
	 * Shared button/chip/link styles for surfaces marked with `rsmspark-suite-ui`.
	 *
	 * Call from modals, overlays, or other admin UI outside the main plugin wrap.
	 * Suite-wide enqueue can move here later; picker is the initial consumer.
	 *
	 * @return void
	 */
	public static function enqueue_button_styles() {
		if ( function_exists( 'rsmspark_suite_enqueue_button_styles' ) ) {
			rsmspark_suite_enqueue_button_styles();
			return;
		}

		if ( ! defined( 'RSMSPARK_SUITE_URL' ) || ! defined( 'RSMSPARK_SUITE_VERSION' ) ) {
			return;
		}

		wp_enqueue_style(
			'rsmspark-suite-buttons',
			RSMSPARK_SUITE_URL . 'assets/css/admin-suite-buttons.css',
			array( 'rsmspark-suite-admin' ),
			RSMSPARK_SUITE_VERSION
		);
	}

	/**
	 * Load admin-suite + button CSS from the calling plugin's bundled rsm-shared copy.
	 *
	 * @param string $shared_dir Absolute path to rsm-shared.
	 * @param string $shared_url URL to rsm-shared.
	 * @param string $version    Asset version (usually plugin version).
	 * @return void
	 */
	public static function enqueue_local_admin_styles( $shared_dir, $shared_url, $version ) {
		if ( function_exists( 'rsmspark_suite_enqueue_admin_styles' ) ) {
			rsmspark_suite_enqueue_admin_styles( $shared_dir, $shared_url, $version );
		}
	}

	/**
	 * Whether the current screen belongs to the RSM suite.
	 *
	 * @param string $hook Admin hook suffix.
	 * @return bool
	 */
	private static function is_rsm_admin_screen( $hook ) {
		if ( false !== strpos( $hook, self::MENU_SLUG ) ) {
			return true;
		}

		$screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
		if ( $screen && ! empty( $screen->id ) ) {
			$rsm_patterns = array( 'rsm_', 'rsm-', 'rsm-spark', 'rp_popup', 'ed_', 'rsm_form', 'rsm_submission', 'so_form', 'so_submission', 'openrsm_', 'job_listing', 'careertide', 'rsm-invoice', 'woocommerce_page_rsm-invoice' );
			foreach ( $rsm_patterns as $pattern ) {
				if ( false !== strpos( $screen->id, $pattern ) ) {
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * Queue submenu slugs for a plugin; applied in suite order on admin_menu priority 1002.
	 *
	 * @param string             $plugin_id   Plugin id from register_plugin().
	 * @param array<int, string> $owned_slugs Submenu slugs owned by this plugin.
	 * @return void
	 */
	public static function register_submenu_order( $plugin_id, array $owned_slugs ) {
		$plugin_id = sanitize_key( (string) $plugin_id );
		if ( '' === $plugin_id || empty( $owned_slugs ) ) {
			return;
		}

		self::$submenu_orders[ $plugin_id ] = array_values( array_unique( array_map( 'strval', $owned_slugs ) ) );
	}

	/**
	 * Deduplicate submenu rows that share the same slug (keeps the first).
	 *
	 * @return void
	 */
	public static function dedupe_parent_submenu() {
		global $submenu;

		$parent = self::parent_slug();
		if ( empty( $submenu[ $parent ] ) || ! is_array( $submenu[ $parent ] ) ) {
			return;
		}

		$seen  = array();
		$clean = array();

		foreach ( $submenu[ $parent ] as $item ) {
			$slug = (string) ( $item[2] ?? '' );
			if ( '' !== $slug && isset( $seen[ $slug ] ) ) {
				continue;
			}
			if ( '' !== $slug ) {
				$seen[ $slug ] = true;
			}
			$clean[] = $item;
		}

		$submenu[ $parent ] = $clean;
	}

	/**
	 * Apply queued submenu order after every plugin has registered admin menus.
	 *
	 * @return void
	 */
	public static function finalize_submenu_orders() {
		if ( empty( self::$submenu_orders ) || empty( self::$plugins ) ) {
			return;
		}

		self::dedupe_parent_submenu();

		$sorted = self::$plugins;
		uasort(
			$sorted,
			static function ( $a, $b ) {
				return $a['order'] <=> $b['order'];
			}
		);

		foreach ( array_keys( $sorted ) as $plugin_id ) {
			if ( empty( self::$submenu_orders[ $plugin_id ] ) ) {
				continue;
			}
			self::reorder_plugin_submenu( $plugin_id, self::$submenu_orders[ $plugin_id ] );
		}
	}

	/**
	 * Move a plugin's submenu block after the previous suite section.
	 *
	 * @param string              $plugin_id     Plugin id from register_plugin().
	 * @param array<int, string>  $owned_slugs   Submenu slugs owned by this plugin.
	 * @param string|null         $after_plugin  Prior plugin id; auto from order when null.
	 * @return void
	 */
	public static function reorder_plugin_submenu( $plugin_id, array $owned_slugs, $after_plugin = null ) {
		global $submenu;

		$parent = self::parent_slug();
		if ( empty( $submenu[ $parent ] ) || ! is_array( $submenu[ $parent ] ) || empty( $owned_slugs ) ) {
			return;
		}

		$owned_slugs = array_values( array_unique( array_map( 'strval', $owned_slugs ) ) );

		$owned_map  = array();
		$rest       = array();
		$seen_owned = array();

		foreach ( $submenu[ $parent ] as $item ) {
			$slug = (string) ( $item[2] ?? '' );
			if ( in_array( $slug, $owned_slugs, true ) ) {
				if ( isset( $seen_owned[ $slug ] ) ) {
					continue;
				}
				$seen_owned[ $slug ] = true;
				$owned_map[ $slug ]  = $item;
				continue;
			}
			$rest[] = $item;
		}

		$owned_items = array();
		foreach ( $owned_slugs as $slug ) {
			if ( isset( $owned_map[ $slug ] ) ) {
				$owned_items[] = $owned_map[ $slug ];
			}
		}

		if ( empty( $owned_items ) ) {
			return;
		}

		if ( null === $after_plugin ) {
			$after_plugin = self::previous_plugin_id( $plugin_id );
		}

		$insert_after = null;
		if ( is_string( $after_plugin ) && '' !== $after_plugin ) {
			$insert_after = self::find_section_insert_index( $rest, 'rsmspark-suite-' . $after_plugin );
		}

		if ( null === $insert_after ) {
			foreach ( $rest as $index => $item ) {
				if ( isset( $item[2] ) && 'rsm-spark' === (string) $item[2] ) {
					$insert_after = $index;
					break;
				}
			}
		}

		if ( null === $insert_after ) {
			$submenu[ $parent ] = array_merge( $rest, $owned_items );
			return;
		}

		$before             = array_slice( $rest, 0, $insert_after + 1 );
		$after              = array_slice( $rest, $insert_after + 1 );
		$submenu[ $parent ] = array_merge( $before, $owned_items, $after );
	}

	/**
	 * Plugin id immediately before $plugin_id in suite order.
	 *
	 * @param string $plugin_id Plugin identifier.
	 * @return string|null
	 */
	public static function previous_plugin_id( $plugin_id ) {
		if ( empty( self::$plugins ) ) {
			return null;
		}

		$sorted = self::$plugins;
		uasort(
			$sorted,
			static function ( $a, $b ) {
				return $a['order'] <=> $b['order'];
			}
		);

		$prev  = null;
		$found = false;

		foreach ( array_keys( $sorted ) as $id ) {
			if ( $found ) {
				return null;
			}
			if ( $id === $plugin_id ) {
				$found = true;
				return $prev;
			}
			$prev = $id;
		}

		return null;
	}

	/**
	 * Index of the last submenu row in a suite section (heading slug rsmspark-suite-{id}).
	 *
	 * @param array<int, array<int, string>> $items        Submenu rows.
	 * @param string                         $heading_slug Section heading page slug.
	 * @return int|null
	 */
	public static function find_section_insert_index( array $items, $heading_slug ) {
		$insert_after = null;
		$in_section   = false;

		foreach ( $items as $index => $item ) {
			$slug = (string) ( $item[2] ?? '' );

			if ( $heading_slug === $slug ) {
				$in_section   = true;
				$insert_after = $index;
				continue;
			}

			if ( $in_section ) {
				if ( 0 === strpos( $slug, 'rsmspark-suite-' ) && $heading_slug !== $slug ) {
					break;
				}
				$insert_after = $index;
			}
		}

		return $insert_after;
	}
}
